'SubSonic.Schema .DatabaseColumn' türünde bir nesne serileştirilirken dairesel bir başvuru algılandı.


170

Ben basit bir JSON dönüş yapmaya çalışıyorum ama aşağıdaki sorunları var sorunları yaşıyorum.

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

Bu sorunun başlığında gösterilen istisna dışında bir HTTP 500 alıyorum. Ben de denedim

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

Aynı sorunu verdi.

Bu bir hata mı yoksa benim uygulamam mı?


1
Şuna bak. Bu ScriptIgnoreözelliği kullanan bir çözüm var . stackoverflow.com/questions/1193857/subsonic-3-0-0-2-structs-tt
freddoo

Bu benim için en iyi çözümdü; Ben had Oyun> vb Ben yerleştirilen Turnuvası> Oyun> Turnuva> Oyun ScriptIgnoreTournament.Game mülkiyet niteliği ve cezası :) çalıştı
eth0

Birisinin fazladan kod gerektirmeyen bu sorun için "otomatik" (en iyi uygulama değil) çözüm
istemesi durumunda

Yanıtlar:


175

Nesne hiyerarşinizde JSON serileştiricisi tarafından desteklenmeyen dairesel referanslar var gibi görünüyor. Tüm sütunlara mı ihtiyacınız var? Yalnızca görünümde ihtiyacınız olan özellikleri alabilirsiniz:

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

Bu, JSON nesnenizi daha açık ve anlaşılması kolay hale getirir. Birçok özelliklere sahip olursa, AutoMapper için kullanılabilir otomatik DTO nesneleri ve Görünüm nesneleri arasındaki map.


Sanırım belki istediklerimin seçilmesi işe yarayabilir diye düşünüyorum dairesel referans çünkü IQueryable <Kategori> Olay IQueryable <Olay> olacak çünkü
Jon

7
Automapper, bu sorunu alamayacağınızın garantisi değildir. Buraya bir cevap aramaya geldim ve aslında automapper kullanıyorum.
Kaptan Kenpachi

1
Neden dairesel olabileceğini açıkladığı için @ClayKaboom'un yanıtını görün
PandaWood

106

Aynı sorunu yaşadım ve çözdüm using Newtonsoft.Json;

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

return Content(list, "application/json");

3
Bu satır içi kod benim için iyi çalıştı. Kravits88'in belirttiği gibi küresel yapılandırmadaki aynı şeyler benim için çalışmıyor. AYRICA, yöntem imzası bu kod için ContentResult döndürmek için güncelleştirilmelidir.
BiLaL

6
Bu, en iyi yanıt olarak işaretlenmelidir, çünkü nesnelerinizi kabul edilen olarak işaretlenen cevaptaki diğer sunumlara dönüştürmek için saat harcayamayacağınız durumları kapsar.
Renan

56

Bu aslında, ortaya çıkan json nesnesinin başarısız olmasını sağlayan karmaşık nesneler olduğu için olur. Ve başarısız olur çünkü nesne eşlendiğinde, ebeveynlerini haritalayan çocukları eşleştirir ve döngüsel bir referans oluşturur. Json serileştirmek için sonsuz zaman alacaktı, bu yüzden istisna ile ilgili sorunu önler.

Entity Framework eşlemesi de aynı davranışı üretir ve çözüm, istenmeyen tüm özellikleri atmaktır.

Sadece son cevabı açıklayan kodun tamamı şöyle olacaktır:

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
           );
}

Ayrıca, bir Resultözellik içindeki nesneleri istemiyorsanız aşağıdaki gibi olabilir :

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
           );
}

1
Net ve kolay anlaşılır şeyler için +1, teşekkürler @Clay. Hatanın arkasındaki kavramlar hakkındaki açıklamanızı beğendim.
Ajay2707

14

Özetlemek gerekirse, bunun için 4 çözüm var:

Çözüm 1: DBContext için ProxyCreation'ı kapatın ve sonunda geri yükleyin.

    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;
        }
    }

Çözüm 2: Serileştirici ayarlarında yoksaymak için ReferenceLoopHandling'i Ayarlayarak JsonConvert'i kullanma.

    //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);
        }
    }

Aşağıdaki iki çözüm aynıdır, ancak bir model kullanmak daha iyidir çünkü güçlü yazılmıştır.

Çözüm 3: Yalnızca gerekli özellikleri içeren bir Model döndürün.

    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);
        }
    }

Çözüm 4: Yalnızca gerekli özellikleri içeren yeni bir dinamik nesne döndürün.

    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);
        }
    }

7

JSON, xml ve diğer çeşitli biçimler gibi, ağaç tabanlı bir serileştirme biçimidir. Nesnelerinizde dairesel referanslar varsa, "ağaç" olacağı için sizi sevmez:

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

Belirli bir yol boyunca gezinmeyi devre dışı bırakmanın genellikle yolları vardır; örneğin, XmlSerializerüst mülkü olarak işaretleyebilirsiniz XmlIgnore. Bu söz konusu json serileştirici ile mümkün olup olmadığını veya DatabaseColumnuygun işaretleyicileri olup olmadığını bilmiyorum ( çok muhtemel, her serileştirme API'sine başvurması gerektiği için)


4

EntityFramework varlıklarını oluşturmak için kullanılan yeni DbContext T4 şablonu nedeniyle. Değişiklik izlemeyi gerçekleştirmek için bu şablonlar, güzel POCO'larınızı onlarla sararak Proxy desenini kullanır. Bu daha sonra JavaScriptSerializer ile serileştirirken sorunlara neden olur.

Böylece 2 çözüm:

  1. Ya sadece istemcide ihtiyacınız olan özellikleri serileştirir ve döndürürsünüz
  2. Bağlam yapılandırmasına ayarlayarak otomatik proxy oluşturma özelliğini kapatabilirsiniz

    context.Configuration.ProxyCreationEnabled = yanlış;

Aşağıdaki makalede çok iyi açıklanmıştır.

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


4

Newtonsoft.Json kullanma: Global.asax Application_Start yönteminize şu satırı ekleyin:

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;

1
Görünüşe göre çok düz görünüyor ama benim için çalışmadı
BiLaL


4

Tablo nesnesini doğrudan dönüştürmekten kaçının. Diğer tablolar arasında ilişkiler ayarlanırsa bu hatayı atabilir. Bunun yerine, bir model sınıfı oluşturabilir, sınıf nesnesine değerler atayabilir ve sonra bunu serileştirebilirsiniz.


3

Verilen cevaplar iyidir, ancak bence "mimari" bir perspektif ekleyerek geliştirilebilirler.

soruşturma

MVC's Controller.Jsonişlevi işi yapıyor, ancak bu durumda ilgili bir hata sağlama konusunda çok zayıf. Kullanarak Newtonsoft.Json.JsonConvert.SerializeObjecthata, dairesel başvuruyu tetikleyen özelliğin tam olarak ne olduğunu belirtir. Bu özellikle daha karmaşık nesne hiyerarşilerini serileştirirken kullanışlıdır.

Uygun mimari

Veri modellerini (örneğin EF modelleri) asla serileştirmeye çalışılmamalıdır, çünkü ORM'in navigasyon özellikleri serileştirme söz konusu olduğunda perdition'a giden yoldur. Veri akışı aşağıdaki gibi olmalıdır:

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

Servis modelleri, otomatik haritalayıcılar (örn. Automapper ) kullanılarak veri modellerinden elde edilebilir . Bu, dairesel referansların eksikliğini garanti etmese de, uygun tasarım bunu yapmalıdır: hizmet modelleri, hizmet tüketicisinin tam olarak ne istediğini içermelidir (yani özellikler).

Bu nadir durumlarda, istemci farklı düzeylerde aynı nesne türünü içeren bir hiyerarşi istediğinde, hizmet üst - alt ilişkisiyle doğrusal bir yapı oluşturabilir (yalnızca başvuruları değil, tanımlayıcıları kullanarak).

Modern uygulamalar, karmaşık veri yapılarını aynı anda yüklemekten kaçınma eğilimindedir ve hizmet modelleri ince olmalıdır. Örneğin:

  1. bir etkinliğe erişme - yalnızca başlık verilerine (tanımlayıcı, ad, tarih vb.) yüklenir -> yalnızca başlık verilerini içeren hizmet modeli (JSON)
  2. yönetilen katılımcılar listesi - bir açılır pencereye erişin ve tembel listeyi yükleyin -> yalnızca katılımcıların listesini içeren hizmet modeli (JSON)

1

Düzeltmeyi kullanıyorum, çünkü MVC5 görünümlerinde Nakavt kullanma.

Hareket halinde

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

fonksiyon

   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_;  
    }

0

Dairesel referansa neden olan özellikleri görebilirsiniz. Sonra şöyle bir şey yapabilirsiniz:

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

   return object
}

-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);
}

Bu soruya cevap vermiyor
Dane I

-1

Bu sorunu çözmek için daha kolay bir alternatif bir dize döndürmek ve bu dizeyi JavaScriptSerializer ile json'a biçimlendirmektir.

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

Görünümünüzde olmasını istediğiniz özellikleri seçen "Seç" bölümü önemlidir. Bazı nesnelerin üst öğe için bir referansı vardır. Nitelikleri seçmezseniz, tabloları bir bütün olarak alırsanız, dairesel başvuru görünebilir.

Bunu yapma:

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

Tüm tabloyu istemiyorsanız bunu yapın:

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

Bu, yalnızca ihtiyacınız olan özelliklerle daha az veri içeren bir görünüm oluşturmanıza yardımcı olur ve web'inizin daha hızlı çalışmasını sağlar.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.