2013-10-19 67 views
60

我有一個奇怪的錯誤。我正在嘗試.NET 4.5 Web API,實體框架和MS SQL Server。我已經創建了數據庫並設置了正確的主鍵和外鍵和關係。實體框架自檢迴路檢測

我創建了一個.edmx模型並導入了兩個表:Employee和Department。一個部門可以有很多員工,這種關係是存在的。我使用腳手架選項創建了一個名爲EmployeeController的新控制器,以使用Entity Framework創建具有讀取/寫入操作的API控制器。在嚮導中,選擇Employee作爲模型,併爲數據上下文提供正確的實體。

所創建看起來像這樣的方法:

public IEnumerable<Employee> GetEmployees() 
{ 
    var employees = db.Employees.Include(e => e.Department); 
    return employees.AsEnumerable(); 
} 

當我通過/ API /員工打電話給我的API,我得到這個錯誤:

The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; ...System.InvalidOperationException","StackTrace":null,"InnerException":{"Message":"An error has occurred.","ExceptionMessage":"Self referencing loop detected with type 'System.Data.Entity.DynamicProxies.Employee_5D80AD978BC68A1D8BD675852F94E8B550F4CB150ADB8649E8998B7F95422552'. Path '[0].Department.Employees'.","ExceptionType":"Newtonsoft.Json.JsonSerializationException","StackTrace":" ...

爲什麼自參考[0 ] .Department.Employees?這並沒有太大的意義。如果我在數據庫中進行循環引用,我會期待這種情況發生,但這是一個非常簡單的例子。可能會出現什麼問題?

+1

可能重複[JSON.NET錯誤自檢參考循環類型檢測](https://stackoverflow.com/questions/7397207/json-net-error-self-referencing-loop-detected-for-type) –

回答

97

那麼基於Json.net默認的Json格式化器的正確答案是設置ReferenceLoopHandlingIgnore

在Global.asax中只需添加這到Application_Start

HttpConfiguration config = GlobalConfiguration.Configuration; 

config.Formatters.JsonFormatter 
      .SerializerSettings 
      .ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; 

這是正確的做法。它會忽略指向對象的引用。

其他響應集中在更改通過排除數據或製作門面對象而返回的列表,有時這不是選項。

使用JsonIgnore屬性來限制引用可能很耗時,並且如果您想從另一個點開始序列化樹,那將是一個問題。

+1

我同意這是正確的答案,並解決了我在2.5年後遇到的同樣問題。 – mentat

+0

var config = GlobalConfiguration.Configuration.Formatters.JsonFormatter? –

+1

HttpConfiguration config = GlobalConfiguration.Configuration; //System.Web.Http –

41

發生這種情況是因爲您試圖直接序列化EF對象集合。由於部門與員工和員工之間有聯繫,因此JSON序列化程序將循環讀取d.Employee.Departments.Employee.Departments等...

要在序列化之前修復此權限,請使用props創建匿名類型你想

例子(僞)代碼:

departments.select(dep => new { 
    dep.Id, 
    Employee = new { 
     dep.Employee.Id, dep.Employee.Name 
    } 
}); 
+2

這是正確的答案。你需要告訴EF你需要什麼。 –

+0

任何人都可以請告訴我哪一個是最佳做法。接受的答案是最好的還是最多的? –

+0

在這種情況下我會用一個模型類如 departments.select(DEP =>新的{ dep.Id, 僱員=新ModelClass { 編號= dep.Employee.Id,名稱= dep.Employee.Name } }); –

7

的主要問題是序列化具有與其他實體模型(外鍵關係)關係的實體模型。這個關係會導致自引用,這會在序列化爲json或xml時拋出異常。 有很多選項。不使用自定義模型序列化實體模型。使用AutomapperValueinjector映射到自定義模型(對象映射)的實體模型數據的值或數據,然後返回請求,並且序列化時不會出現任何其他問題。 或者你可以序列化的實體模型,以便先禁用代理 在實體模型

public class LabEntities : DbContext 
{ 
    public LabEntities() 
    { 
     Configuration.ProxyCreationEnabled = false; 
    } 

要保留XML對象引用,你有兩個選擇。更簡單的選項是將[DataContract(IsReference = true)]添加到您的模型類。 IsReference參數啓用了oibject引用。請記住,DataContract使得系列化選入,所以你也將需要添加的DataMember屬性的屬性:xml格式

[DataContract(IsReference=true)] 
public partial class Employee 
{ 
    [DataMember] 
    string dfsd{get;set;} 
    [DataMember] 
    string dfsd{get;set;} 
    //exclude the relation without giving datamember tag 
    List<Department> Departments{get;set;} 
} 

JSON格式 在Global.asax中

var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter; 
json.SerializerSettings.PreserveReferencesHandling = 
    Newtonsoft.Json.PreserveReferencesHandling.All; 

var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter; 
var dcs = new DataContractSerializer(typeof(Employee), null, int.MaxValue, 
    false, /* preserveObjectReferences: */ true, null); 
xml.SetSerializer<Employee>(dcs); 
+0

使用DataContract和DataMember屬性,選項1工作正常,但在結果的末尾,我得到「Employees」:[{「$ ref」:「1」}]}}]。它已經消除了症狀,但問題仍然存在。其他人肯定會遇到同樣的問題。我是否錯誤地接近了這個解決方案? – Melon

+0

我在global.asax文件中設置了ProxyCreationEnabled = false和jason thingy。它的功能就像一個f **國王的魅力。謝謝 – nahaelem

25

我有同樣的問題,發現你可以將[JsonIgnore]屬性應用到你不想串行的導航屬性alised。它仍然會串行化父類和子類實體,但只是避免了自引用循環。

+0

你鏈接仍然有關?如果沒有,請更新或刪除它。謝謝。 – GibboK

+0

刪除鏈接,因爲它顯示我正在引用的文章現在已被刪除。 –

+3

對我來說,這是最簡單也是最理想的解決方案。感謝B-Lat。 – pimbrouwers

5

消息錯誤意味着你有一個自引用循環。

你生成JSON是這樣的例子(有一個員工的列表):

[ 
employee1 : { 
    name: "name", 
    department : { 
     name: "departmentName", 
     employees : [ 
      employee1 : { 
       name: "name", 
       department : { 
        name: "departmentName", 
        employees : [ 
         employee1 : { 
          name: "name", 
          department : { 
           and again and again.... 
          } 
        ] 
       } 
      } 
     ] 
    } 
} 

]

你必須告訴你不想讓所有鏈接的分貝範圍內實體當你要求什麼時。 爲的DbContext的選項Configuration.LazyLoadingEnabled

我發現最好的方法是創建序列化上下文:

public class SerializerContext : LabEntities 
{ 
    public SerializerContext() 
    { 
     this.Configuration.LazyLoadingEnabled = false; 
    } 
} 
1

在您的上下文模型部分類定義的構造函數中添加一行Configuration.ProxyCreationEnabled = false;

public partial class YourDbContextModelName : DbContext 
{ 
    public YourDbContextModelName() 
     : base("name=YourDbContextConn_StringName") 
    { 
     Configuration.ProxyCreationEnabled = false;//this is line to be added 
    } 

    public virtual DbSet<Employee> Employees{ get; set; } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
    } 
} 
+0

這項工作對我來說在2017年,其他解決方案不起作用 –

1

我只有一個模式,我想用,所以我結束了下面的代碼:

var JsonImageModel = Newtonsoft.Json.JsonConvert.SerializeObject(Images, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }); 
3

我知道這個問題是很老,但它仍然是受歡迎的,我可以沒有看到任何解決方案的ASP.net核心。

ASP.net核心我的情況下,你需要在Startup.cs文件中添加新的JsonOutputFormatter

public void ConfigureServices(IServiceCollection services) 
    { 

     services.AddMvc(options => 
     { 
      options.OutputFormatters.Clear(); 
      options.OutputFormatters.Add(new JsonOutputFormatter(new JsonSerializerSettings() 
      { 
       ReferenceLoopHandling = ReferenceLoopHandling.Ignore, 
      }, ArrayPool<char>.Shared)); 
     }); 

     //... 
    } 

實現它之後,JSON序列將完全忽略循環引用。它的意思是:它將返回null而不是無限地加載相互引用的對象。

不使用上述溶液:

var employees = db.Employees.ToList(); 

將加載Employees和與之相關的Departments

設置ReferenceLoopHandlingIgnore後,Departments將被設置爲null,除非你將其包含在您的查詢:

var employees = db.Employees.Include(e => e.Department); 

另外,請記住,它會清除所有OutputFormatters,如果你不希望出現這種情況你可以嘗試刪除這一行:

options.OutputFormatters.Clear(); 

但是,消除再次引起self referencing loop例外,在我的情況下,出於某種原因。