Một tham chiếu vòng tròn đã được phát hiện trong khi tuần tự hóa một đối tượng thuộc loại 'SubSonic.Schema .DatabaseColumn'.


170

Tôi đang cố gắng thực hiện một trả về JSON đơn giản nhưng tôi gặp vấn đề tôi có những điều sau đây.

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

Tôi nhận được HTTP 500 với ngoại lệ như trong tiêu đề của câu hỏi này. Tôi cũng đã thử

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

Điều đó đã cho cùng một vấn đề.

Đây có phải là một lỗi hoặc thực hiện của tôi?


1
Nhìn cái này. Có một giải pháp sử dụng ScriptIgnorethuộc tính. stackoverflow.com/questions/1193857/subsonic-3-0-0-2-structs-tt
freddoo

Đây là giải pháp tốt nhất cho tôi; Tôi đã có Trò chơi> Giải đấu> Trò chơi> Giải đấu> Trò chơi, v.v. Tôi đã đặt một ScriptIgnorethuộc tính trên thuộc tính Giải đấu. Trò chơi và nó hoạt động tốt :)
eth0

Trong trường hợp ai đó muốn giải pháp "tự động" (không thực hành tốt nhất) cho vấn đề này không yêu cầu thêm mã, hãy xem QA này: Không tuần tự hóa các tham chiếu lớp Entity Framework trong JSON (thư viện ServiceStack.Text)
nikib3ro

Câu trả lời:


175

Dường như có các tham chiếu vòng tròn trong hệ thống phân cấp đối tượng của bạn không được trình tuần tự hóa JSON hỗ trợ. Bạn có cần tất cả các cột? Bạn chỉ có thể chọn các thuộc tính bạn cần trong dạng xem:

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

Điều này sẽ làm cho đối tượng JSON của bạn nhẹ hơn và dễ hiểu hơn. Nếu bạn có nhiều thuộc tính, AutoMapper có thể được sử dụng để tự động ánh xạ giữa các đối tượng DTO và Xem các đối tượng.


Tôi nghĩ rằng có thể chọn những cái tôi muốn có thể hoạt động Tôi nghĩ rằng tham chiếu vòng tròn là bởi vì trong Sự kiện tôi có IQueryable <Category> mà lần lượt sẽ có một IQueryable <Event>
Jon

7
Automapper không đảm bảo rằng bạn sẽ không gặp phải vấn đề này. Tôi đến đây để tìm kiếm một câu trả lời và tôi thực sự đang sử dụng automapper.
Thuyền trưởng Kenpachi

1
Hãy xem câu trả lời từ @ClayKaboom vì nó giải thích lý do tại sao nó có thể là hình tròn
PandaWood

106

Tôi đã có cùng một vấn đề và giải quyết bằng using Newtonsoft.Json;

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

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

3
Mã nội tuyến này làm việc tốt cho tôi. Những thứ tương tự trong cấu hình toàn cầu như kravits88 đã đề cập không hoạt động với tôi. CSONG, chữ ký phương thức nên được cập nhật để trả về ContentResult cho mã này.
BiLaL

6
Điều này nên được đánh dấu là câu trả lời tốt nhất, vì nó bao gồm các trường hợp bạn không thể dành hàng giờ để chuyển đổi các đối tượng của mình thành các biểu diễn khác như trong câu trả lời được đánh dấu là được chấp nhận.
Renan

56

Điều này thực sự xảy ra bởi vì các đối tượng phức tạp là những gì làm cho đối tượng json kết quả không thành công. Và nó thất bại bởi vì khi đối tượng được ánh xạ, nó lập bản đồ cho trẻ em, ánh xạ cha mẹ của chúng, làm cho một tham chiếu vòng tròn xảy ra. Json sẽ mất vô thời gian để tuần tự hóa nó, vì vậy nó ngăn chặn vấn đề với ngoại lệ.

Ánh xạ khung thực thể cũng tạo ra hành vi tương tự và giải pháp là loại bỏ tất cả các thuộc tính không mong muốn.

Chỉ cần khám phá câu trả lời cuối cùng, toàn bộ mã sẽ là:

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

Nó cũng có thể là trường hợp sau đây trong trường hợp bạn không muốn các đối tượng bên trong một Resulttài sản:

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
+1 cho những điều rõ ràng và dễ hiểu, cảm ơn @Clay. Tôi thích lời giải thích của bạn về các khái niệm đằng sau lỗi.
Ajay2707

14

Tóm lại, có 4 giải pháp cho vấn đề này:

Giải pháp 1: tắt ProxyCreation cho DBContext và khôi phục lại cuối cùng.

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

Giải pháp 2: Sử dụng JsonConvert bằng cách đặt ReferenceLoopHandling để bỏ qua các cài đặt 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);
        }
    }

Theo hai giải pháp là như nhau, nhưng sử dụng một mô hình sẽ tốt hơn vì nó được gõ mạnh.

Giải pháp 3: trả về một Mô hình chỉ bao gồm các thuộc tính cần thiết.

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

Giải pháp 4: trả về một đối tượng động mới chỉ bao gồm các thuộc tính cần thiết.

    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, như xml và các định dạng khác, là định dạng tuần tự hóa dựa trên cây. Nó sẽ không yêu bạn nếu bạn có các tham chiếu tròn trong các đối tượng của mình, vì "cây" sẽ là:

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

Thường có những cách vô hiệu hóa điều hướng dọc theo một con đường nhất định; ví dụ: với XmlSerializerbạn có thể đánh dấu thuộc tính cha là XmlIgnore. Tôi không biết liệu điều này có khả thi với trình tuần tự json được đề cập hay không, liệu DatabaseColumncó các dấu phù hợp hay không ( rất khó xảy ra, vì nó sẽ cần tham chiếu mọi API tuần tự hóa)


4

Đó là do mẫu DbContext T4 mới được sử dụng để tạo các thực thể EntityFramework. Để có thể thực hiện theo dõi thay đổi, các mẫu này sử dụng mẫu Proxy, bằng cách gói POCO đẹp của bạn với chúng. Điều này sau đó gây ra các vấn đề khi tuần tự hóa với JavaScriptSerializer.

Vì vậy, 2 giải pháp là:

  1. Hoặc bạn chỉ cần tuần tự hóa và trả lại các thuộc tính bạn cần trên máy khách
  2. Bạn có thể tắt thế hệ proxy tự động bằng cách đặt nó vào cấu hình của bối cảnh

    bối cảnh.Configuration.ProxyCreationEnables = false;

Giải thích rất tốt trong bài viết dưới đây.

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


4

Sử dụng Newtonsoft.Json: Trong phương thức Global.asax Application_Start của bạn thêm dòng này:

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

1
Rõ ràng trông rất thẳng về phía trước nhưng không hiệu quả với tôi
BiLaL

4

thêm [JsonIgnore]vào các thuộc tính ảo trong mô hình của bạn.


4

Tránh chuyển đổi đối tượng bảng trực tiếp. Nếu quan hệ được đặt giữa các bảng khác, nó có thể gây ra lỗi này. Thay vào đó, bạn có thể tạo một lớp mô hình, gán các giá trị cho đối tượng lớp và sau đó tuần tự hóa nó.


3

Cung cấp câu trả lời là tốt, nhưng tôi nghĩ rằng chúng có thể được cải thiện bằng cách thêm một phối cảnh "kiến trúc".

Cuộc điều tra

MVC's Controller.JsonChức năng đang thực hiện công việc, nhưng rất kém trong việc cung cấp một lỗi có liên quan trong trường hợp này. Bằng cách sử dụng Newtonsoft.Json.JsonConvert.SerializeObject, lỗi xác định chính xác thuộc tính nào đang kích hoạt tham chiếu vòng tròn. Điều này đặc biệt hữu ích khi tuần tự hóa các hệ thống phân cấp đối tượng phức tạp hơn.

Kiến trúc đúng

Không bao giờ nên cố gắng tuần tự hóa các mô hình dữ liệu (ví dụ như các mô hình EF), vì các thuộc tính điều hướng của ORM là con đường dẫn đến sự sai lệch khi nói đến tuần tự hóa. Luồng dữ liệu phải như sau:

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

Các mô hình dịch vụ có thể được lấy từ các mô hình dữ liệu bằng cách sử dụng trình ánh xạ tự động (ví dụ: Automapper ). Mặc dù điều này không đảm bảo thiếu các tham chiếu vòng tròn, nhưng thiết kế phù hợp nên thực hiện: các mô hình dịch vụ nên chứa chính xác những gì người tiêu dùng dịch vụ yêu cầu (tức là các thuộc tính).

Trong những trường hợp hiếm hoi đó, khi máy khách yêu cầu một hệ thống phân cấp liên quan đến cùng loại đối tượng ở các cấp độ khác nhau, dịch vụ có thể tạo cấu trúc tuyến tính với mối quan hệ cha-> con (chỉ sử dụng định danh, không tham chiếu).

Các ứng dụng hiện đại có xu hướng tránh tải các cấu trúc dữ liệu phức tạp cùng một lúc và các mô hình dịch vụ nên mỏng. Ví dụ:

  1. truy cập một sự kiện - chỉ dữ liệu tiêu đề (mã định danh, tên, ngày, v.v.) được tải -> mô hình dịch vụ (JSON) chỉ chứa dữ liệu tiêu đề
  2. danh sách người tham dự được quản lý - truy cập cửa sổ bật lên và lười tải danh sách -> mô hình dịch vụ (JSON) chỉ chứa danh sách người tham dự

1

Tôi đang sử dụng bản sửa lỗi, vì sử dụng Knockout trong chế độ xem MVC5.

Hành động

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

chức năng

   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

Bạn có thể nhận thấy các thuộc tính gây ra tham chiếu tròn. Sau đó, bạn có thể làm một cái gì đó như:

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

Điều này không trả lời câu hỏi
Dane I

-1

Một cách khác dễ dàng hơn để giải quyết vấn đề này là trả về một chuỗi và định dạng chuỗi đó thành json với 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 );
}

Điều quan trọng là phần "Chọn", chọn các thuộc tính bạn muốn trong chế độ xem của bạn. Một số đối tượng có một tài liệu tham khảo cho cha mẹ. Nếu bạn không chọn các thuộc tính, tham chiếu vòng tròn có thể xuất hiện, nếu bạn chỉ lấy toàn bộ các bảng.

Đừng làm điều này:

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

Thay vào đó, hãy làm điều này nếu bạn không muốn cả bảng:

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

Điều này giúp hiển thị chế độ xem với ít dữ liệu hơn, chỉ với các thuộc tính bạn cần và làm cho web của bạn chạy nhanh hơn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.