Referensi melingkar terdeteksi saat membuat serial objek dari tipe 'SubSonic.Schema .DatabaseColumn'.

170

Saya mencoba melakukan pengembalian JSON sederhana tetapi saya mengalami masalah saya memiliki yang berikut di bawah ini.

public JsonResult GetEventData()
{
    var data = Event.Find(x => x.ID != 0);
    return Json(data);
}

Saya mendapatkan HTTP 500 dengan pengecualian seperti yang ditunjukkan pada judul pertanyaan ini. Saya juga mencoba

var data = Event.All().ToList()

Itu memberi masalah yang sama.

Apakah ini bug atau implementasi saya?

Jon
sumber
1
Lihat lah ini. Ada solusi menggunakan ScriptIgnoreatribut. stackoverflow.com/questions/1193857/subsonic-3-0-0-2-structs-tt
freddoo
Ini adalah solusi terbaik bagi saya; Saya memiliki Game> Tournament> Game> Tournament> Game, dll. Saya menempatkan ScriptIgnoreatribut pada properti Tournament.Game dan itu bekerja dengan baik :)
eth0
Jika seseorang menginginkan solusi "otomatis" (bukan praktik terbaik) untuk masalah ini yang tidak memerlukan kode tambahan, lihat QA ini: Jangan membuat serial referensi kelas Kerangka Entitas di JSON (ServiceStack.Text library)
nikib3ro

Jawaban:

175

Tampaknya ada referensi melingkar dalam hierarki objek Anda yang tidak didukung oleh serializer JSON. Apakah Anda memerlukan semua kolom? Anda hanya dapat mengambil properti yang Anda butuhkan dalam tampilan:

return Json(new 
{  
    PropertyINeed1 = data.PropertyINeed1,
    PropertyINeed2 = data.PropertyINeed2
});

Ini akan membuat objek JSON Anda lebih ringan dan lebih mudah dipahami. Jika Anda memiliki banyak properti, AutoMapper dapat digunakan untuk memetakan secara otomatis antara objek DTO dan objek Lihat.

Darin Dimitrov
sumber
Saya pikir mungkin memilih yang saya inginkan dapat bekerja, saya pikir referensi melingkar adalah karena dalam Acara saya memiliki IQueryable <Category> yang pada gilirannya akan memiliki IQueryable <Event>
Jon
7
Automapper bukan jaminan bahwa Anda tidak akan mendapatkan masalah ini. Saya datang ke sini mencari jawaban dan saya sebenarnya menggunakan automapper.
Kapten Kenpachi
1
Jangan melihat jawaban dari @ClayKaboom karena menjelaskan mengapa itu mungkin melingkar
PandaWood
106

Saya memiliki masalah yang sama dan diselesaikan oleh using Newtonsoft.Json;

var list = JsonConvert.SerializeObject(model,
    Formatting.None,
    new JsonSerializerSettings() {
        ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
});

return Content(list, "application/json");
ddfnfal
sumber
3
Kode inline ini berfungsi dengan baik untuk saya. Hal yang sama dalam konfigurasi global seperti yang disebutkan oleh kravits88 tidak berfungsi untuk saya. JUGA, tanda tangan metode harus diperbarui untuk mengembalikan ContentResult untuk kode ini.
BiLaL
6
Ini harus ditandai sebagai jawaban terbaik, karena mencakup kasus-kasus di mana Anda tidak dapat menghabiskan berjam-jam mengubah objek Anda menjadi representasi lain seperti pada jawaban yang ditandai sebagai diterima.
Renan
56

Ini sebenarnya terjadi karena objek kompleks adalah apa yang membuat objek json yang dihasilkan gagal. Dan itu gagal karena ketika objek dipetakan itu memetakan anak-anak, yang memetakan orang tua mereka, membuat referensi melingkar terjadi. Json akan mengambil waktu tak terbatas untuk membuat cerita bersambung, sehingga mencegah masalah dengan pengecualian.

Pemetaan Entity Framework juga menghasilkan perilaku yang sama, dan solusinya adalah membuang semua properti yang tidak diinginkan.

Hanya dengan menjelaskan jawaban akhir, seluruh kode akan menjadi:

public JsonResult getJson()
{
    DataContext db = new DataContext ();

    return this.Json(
           new {
                Result = (from obj in db.Things select new {Id = obj.Id, Name = obj.Name})
               }
           , JsonRequestBehavior.AllowGet
           );
}

Ini juga bisa menjadi yang berikut jika Anda tidak ingin objek di dalam Resultproperti:

public JsonResult getJson()
{
    DataContext db = new DataContext ();

    return this.Json(
           (from obj in db.Things select new {Id = obj.Id, Name = obj.Name})
           , JsonRequestBehavior.AllowGet
           );
}
ClayKaboom
sumber
1
+1 untuk hal-hal yang jelas dan mudah dipahami, terima kasih @Clay. Saya suka penjelasan Anda tentang konsep di balik kesalahan.
Ajay2707
14

Singkatnya, ada 4 solusi untuk ini:

Solusi 1: matikan ProxyCreation untuk DBContext dan kembalikan pada akhirnya.

    private DBEntities db = new DBEntities();//dbcontext

    public ActionResult Index()
    {
        bool proxyCreation = db.Configuration.ProxyCreationEnabled;
        try
        {
            //set ProxyCreation to false
            db.Configuration.ProxyCreationEnabled = false;

            var data = db.Products.ToList();

            return Json(data, JsonRequestBehavior.AllowGet);
        }
        catch (Exception ex)
        {
            Response.StatusCode = (int)HttpStatusCode.BadRequest;
            return Json(ex.Message);
        }
        finally
        {
            //restore ProxyCreation to its original state
            db.Configuration.ProxyCreationEnabled = proxyCreation;
        }
    }

Solusi 2: Menggunakan JsonConvert dengan Mengatur ReferenceLoopHandling untuk mengabaikan pengaturan serializer.

    //using using Newtonsoft.Json;

    private DBEntities db = new DBEntities();//dbcontext

    public ActionResult Index()
    {
        try
        {
            var data = db.Products.ToList();

            JsonSerializerSettings jss = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
            var result = JsonConvert.SerializeObject(data, Formatting.Indented, jss);

            return Json(result, JsonRequestBehavior.AllowGet);
        }
        catch (Exception ex)
        {
            Response.StatusCode = (int)HttpStatusCode.BadRequest;
            return Json(ex.Message);
        }
    }

Mengikuti dua solusi adalah sama, tetapi menggunakan model lebih baik karena diketik dengan kuat.

Solusi 3: kembalikan Model yang hanya mencakup properti yang diperlukan.

    private DBEntities db = new DBEntities();//dbcontext

    public class ProductModel
    {
        public int Product_ID { get; set;}

        public string Product_Name { get; set;}

        public double Product_Price { get; set;}
    }

    public ActionResult Index()
    {
        try
        {
            var data = db.Products.Select(p => new ProductModel
                                                {
                                                    Product_ID = p.Product_ID,
                                                    Product_Name = p.Product_Name,
                                                    Product_Price = p.Product_Price
                                                }).ToList();

            return Json(data, JsonRequestBehavior.AllowGet);
        }
        catch (Exception ex)
        {
            Response.StatusCode = (int)HttpStatusCode.BadRequest;
            return Json(ex.Message);
        }
    }

Solusi 4: kembalikan objek dinamis baru yang hanya mencakup properti yang diperlukan.

    private DBEntities db = new DBEntities();//dbcontext

    public ActionResult Index()
    {
        try
        {
            var data = db.Products.Select(p => new
                                                {
                                                    Product_ID = p.Product_ID,
                                                    Product_Name = p.Product_Name,
                                                    Product_Price = p.Product_Price
                                                }).ToList();

            return Json(data, JsonRequestBehavior.AllowGet);
        }
        catch (Exception ex)
        {
            Response.StatusCode = (int)HttpStatusCode.BadRequest;
            return Json(ex.Message);
        }
    }
Amro
sumber
7

JSON, seperti xml dan berbagai format lainnya, adalah format serialisasi berbasis pohon. Itu tidak akan mencintaimu jika Anda memiliki referensi melingkar di objek Anda, seperti "pohon" akan:

root B => child A => parent B => child A => parent B => ...

Sering ada cara menonaktifkan navigasi di sepanjang jalur tertentu; misalnya, dengan XmlSerializerAnda menandai properti induk sebagai XmlIgnore. Saya tidak tahu apakah ini mungkin dengan serializer json yang dipermasalahkan, atau apakah DatabaseColumnmemiliki penanda yang cocok ( sangat tidak mungkin, karena perlu merujuk setiap API serialisasi)

Marc Gravell
sumber
4

Ini karena templat DbContext T4 baru yang digunakan untuk menghasilkan entitas EntityFramework. Untuk dapat melakukan pelacakan perubahan, template ini menggunakan pola Proxy, dengan membungkus POCO Anda yang bagus dengannya. Ini kemudian menyebabkan masalah ketika membuat serial dengan JavaScriptSerializer.

Maka 2 solusi tersebut adalah:

  1. Entah Anda hanya membuat serial dan mengembalikan properti yang Anda butuhkan pada klien
  2. Anda dapat mematikan generasi proksi otomatis dengan mengaturnya pada konfigurasi konteks

    context.Configuration.ProxyCreationEnabled = false;

Sangat dijelaskan di artikel di bawah ini.

http://juristr.com/blog/2011/08/javascriptserializer-circular-reference/

nilesh
sumber
4

Menggunakan Newtonsoft.Json: Di metode Global.asax Application_Start Anda tambahkan baris ini:

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
kravits88
sumber
1
Tampaknya terlihat sangat lurus ke depan tetapi tidak berhasil untuk saya
BiLaL
4

tambahkan [JsonIgnore]ke properti virtual dalam model Anda.

MorenajeRD
sumber
4

Hindari mengonversi objek tabel secara langsung. Jika relasi diatur di antara tabel lain, itu mungkin membuang kesalahan ini. Sebaliknya, Anda bisa membuat kelas model, memberikan nilai ke objek kelas dan kemudian membuat cerita bersambung.

Unais.NI
sumber
3

Jawaban yang diberikan bagus, tetapi saya pikir mereka dapat ditingkatkan dengan menambahkan perspektif "arsitektur".

Penyelidikan

MVC's Controller.Jsonfungsi melakukan pekerjaan, tetapi sangat buruk dalam memberikan kesalahan yang relevan dalam kasus ini. Dengan menggunakan Newtonsoft.Json.JsonConvert.SerializeObject, kesalahan menentukan dengan tepat apa properti yang memicu referensi melingkar. Ini sangat berguna ketika membuat serial hierarki objek yang lebih kompleks.

Arsitektur yang tepat

Seseorang seharusnya tidak pernah mencoba membuat serial model data (misalnya model EF), karena properti navigasi ORM adalah jalan menuju kehancuran ketika datang ke serialisasi. Aliran data harus sebagai berikut:

Database -> data models -> service models -> JSON string 

Model layanan dapat diperoleh dari model data menggunakan pemetaan otomatis (misalnya Automapper ). Meskipun ini tidak menjamin kurangnya referensi melingkar, desain yang tepat harus melakukannya: model layanan harus berisi persis apa yang dibutuhkan oleh konsumen layanan (yaitu properti).

Dalam kasus yang jarang terjadi, ketika klien meminta hierarki yang melibatkan jenis objek yang sama pada tingkat yang berbeda, layanan dapat membuat struktur linier dengan hubungan orangtua - anak (menggunakan pengidentifikasi hanya, bukan referensi).

Aplikasi modern cenderung menghindari pemuatan struktur data yang kompleks sekaligus dan model layanan harus ramping. Misalnya:

  1. mengakses suatu peristiwa - hanya data tajuk (pengidentifikasi, nama, tanggal dll.) yang dimuat -> model layanan (JSON) yang hanya berisi data tajuk
  2. daftar peserta yang dikelola - akses sembulan dan malas muat daftar -> model layanan (JSON) yang hanya berisi daftar peserta
Alexei
sumber
1

Saya Menggunakan perbaikan, Karena Menggunakan Knockout dalam tampilan MVC5.

Beraksi

return Json(ModelHelper.GetJsonModel<Core_User>(viewModel));

fungsi

   public static TEntity GetJsonModel<TEntity>(TEntity Entity) where TEntity : class
    {
        TEntity Entity_ = Activator.CreateInstance(typeof(TEntity)) as TEntity;
        foreach (var item in Entity.GetType().GetProperties())
        {
            if (item.PropertyType.ToString().IndexOf("Generic.ICollection") == -1 && item.PropertyType.ToString().IndexOf("SaymenCore.DAL.") == -1)
                item.SetValue(Entity_, Entity.GetPropValue(item.Name));
        }
        return Entity_;  
    }
A.Kosecik
sumber
0

Anda dapat melihat properti yang menyebabkan referensi melingkar. Maka Anda dapat melakukan sesuatu seperti:

private Object DeCircular(Object object)
{
   // Set properties that cause the circular reference to null

   return object
}
Bassel
sumber
-1
//first: Create a class as your view model

public class EventViewModel 
{
 public int Id{get;set}
 public string Property1{get;set;}
 public string Property2{get;set;}
}
//then from your method
[HttpGet]
public async Task<ActionResult> GetEvent()
{
 var events = await db.Event.Find(x => x.ID != 0);
 List<EventViewModel> model = events.Select(event => new EventViewModel(){
 Id = event.Id,
 Property1 = event.Property1,
 Property1 = event.Property2
}).ToList();
 return Json(new{ data = model }, JsonRequestBehavior.AllowGet);
}
Ynnoboy
sumber
Ini tidak menjawab pertanyaan
Dane I
-1

Alternatif yang lebih mudah untuk menyelesaikan masalah ini adalah mengembalikan string, dan memformat string tersebut ke json dengan JavaScriptSerializer.

public string GetEntityInJson()
{
   JavaScriptSerializer j = new JavaScriptSerializer();
   var entityList = dataContext.Entitites.Select(x => new { ID = x.ID, AnotherAttribute = x.AnotherAttribute });
   return j.Serialize(entityList );
}

Penting bagian "Pilih", yang memilih properti yang Anda inginkan dalam tampilan Anda. Beberapa objek memiliki referensi untuk orang tua. Jika Anda tidak memilih atribut, referensi melingkar dapat muncul, jika Anda hanya mengambil tabel secara keseluruhan.

Jangan lakukan ini:

public string GetEntityInJson()
{
   JavaScriptSerializer j = new JavaScriptSerializer();
   var entityList = dataContext.Entitites.toList();
   return j.Serialize(entityList );
}

Lakukan ini sebagai gantinya jika Anda tidak ingin seluruh tabel:

public string GetEntityInJson()
{
   JavaScriptSerializer j = new JavaScriptSerializer();
   var entityList = dataContext.Entitites.Select(x => new { ID = x.ID, AnotherAttribute = x.AnotherAttribute });
   return j.Serialize(entityList );
}

Ini membantu membuat tampilan dengan data lebih sedikit, hanya dengan atribut yang Anda butuhkan, dan membuat web Anda berjalan lebih cepat.

Sterling Diaz
sumber