簡體   English   中英

序列化類型的對象時檢測到循環引用...如何解決?

[英]A circular reference was detected while serializing an object of type… How to solve this?

因此,我試圖在KendoUI MVC網格中列出通過YearPlanViewModel列出員工和相應數據。

這是模型。

 public class YearPlanViewModel
    {
        public int? ID { get; set; }
        public  String EmployeeName { get; set; }
        public List<Business.CompensationDayRequest> CompensationDays { get; set; }
        public List<Business.EmployeeVacationDay> VacationDays { get; set; }
        public List<Business.Displacement> Displacementes { get; set; }
        public List<Business.CriticalTask> CriticalTasks { get; set; }
        public List<Tuple<Business.Absence,Business.TimePeriod>> Absences {get;set;}


    }

因此,對於我的網格的DataSource函數,這是我的查詢

List<YearPlanViewModel> a = wtmEntities.Employee.ToList().Select(employee => new YearPlanViewModel
        {
            ID = employee.IDEmployee,
            EmployeeName = employee.FirstName + " " + employee.LastName,
            CompensationDays = employee.CompensationDayRequests.Where(cr => cr.Employee.IDEmployee == employee.IDEmployee && cr.Date.Year == 2015).ToList(),
            VacationDays = employee.EmployeeVacationDays.Where(vc => vc.Employee.IDEmployee == employee.IDEmployee && vc.Day.Year == 2014).ToList()
            ,
            Displacementes = employee.Displacements.Where(d => d.Employee.IDEmployee == employee.IDEmployee && d.StartDate.Year == 2014).ToList(),

            Absences = employee.Absences.Join(wtmEntities.TimePeriods,
            abs => abs.Period,
             tps => tps.IDTimePeriod,
             (abs, tps) => new { abs, tps })
             .Where(x => x.abs.Employee.IDEmployee == employee.IDEmployee)
             .ToList()
             .Select(t => new Tuple<Business.Absence, Business.TimePeriod>(t.abs, t.tps)).ToList()
             ,

            CriticalTasks = wtmEntities.CriticalTasks.Where(ct => ct.IDPerson == employee.IDPerson).ToList()

        }).ToList();

但是我得到了標題所說的錯誤,主要是因為補償日,置換等等都有對員工的引用,這就是為什么有循環引用的原因。

但是,他們告訴我,我應該在查詢中使用Includes,以免發生,所以我嘗試使用CompensationDays並使用Employee Class而不是ViewModel。

 List<Business.Employee> a = wtmEntities.Employee.Include(e => e.CompensationDayRequests).ToList();

循環引用問題仍然存在,我不知道應該如何更改查詢,以免再次發生。 無論誰幫忙,謝謝。 :)

Working => Refactor(讓它工作,然后重構)

工作:主要問題是嘗試在一個動作中做太多。 當您使用LINQ時,操作將延遲到請求運行,因此編譯器無法捕獲問題(在這種情況下無論如何都不會)。

開始處理它的一種方法是首先獲取父查詢的信息(在這里看起來像Employee)。 然后,您可以提取其他查詢所需的信息,構建層次結構。

這將在將信息編譯到List中做更多工作,但它將避免循環引用。

重構:一旦你使它工作,然后重構,使它更容易理解和簡潔。 您可能會發現,一旦您拿出將要遞歸的部分,您就可以使用單個查詢構建列表。

注意:我希望我有一個“只做這個”的答案,但我沒有時間翻閱所有代碼,看看是否有一個特定的部分可以解決遞歸問題。

我通常使用兩種方法,既關注序列化而不是模型結構本身。 (因為模型在服務器端代碼中很好,而且它只是序列化導致問題。)

首先,讓我們簡化一個更為人為的例子來說明。 像這樣的東西:

public class Parent
{
    public virtual ICollection<Child> Children { get; set; }
}

public class Child
{
    public virtual Parent Parent { get; set; }
}

顯然,這為序列化器提供了無限遞歸,因為任何給定的子節點都有一個返回其父節點的引用。 根據API是如何構成的,一個API邊界之外的孩子並不需要其父的引用。 當一個人得到父母,一個人得到孩子。

如果這是普遍正確的(例如,當Parent是聚合根並且Child從不存在時,並且永遠不會獨立請求),那么您可以修飾模型屬性以防止序列化:

public class Child
{
    [JsonIgnore]
    public virtual Parent Parent { get; set; }
}

在服務器端代碼中,它永遠不會有所作為,但在API邊界處進行序列化時,它根本不包含該屬性。 客戶端代碼不需要它。

如果它普遍,並且只有這一個操作需要忽略子項,那么在序列化之前,您可以將模型轉換為自定義視圖模型。 這是一個更多的手動,可能會導致更多的重復結構,但它可以在緊要關頭完成工作。 它可能看起來像這樣:

var someParent = SomeOperationToGetAParent();
return new ParentViewModel
{
    Children = someParent.Children.Select(c => new ChildViewModel())
};

在這個人為的例子中,沒有任何其他屬性可以使用,但在更現實的東西中會有。 您甚至可以將該構造重構為視圖模型本身,添加工廠方法以生成給定模型實例的視圖模型實例。 就像是:

public class ParentViewModel
{
    public IEnumerable<ChildViewModel> Children { get; set; }
    // other properties

    public static ParentViewModel BuildViewModel(Parent parent)
    {
        return new ParentViewModel
        {
            Children = parent.Children.Select(c => ChildViewModel.BuildViewModel(c)),
            // other properties
        };
    }
}

public class ChildViewModel
{
    // other properties

    public static ChildViewModel BuildViewModel(Child child)
    {
        return new ChildViewModel
        {
            // other properties
        };
    }
}

它可以非常復雜。 代碼本身非常簡單,但對於大型復雜模型結構,可能會有很多。 這導致了使用JsonIgnore的實際吸引力。

導航屬性在服務器端代碼中很好(並且在實體框架中經常是必需的),但它們對序列化器沒有多大意義。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM