序列化类型的对象时检测到循环引用...如何解决?

时间:2015-08-13 16:30:47

标签: c# linq

所以我试图在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();

循环参考问题仍在继续,我不知道如何更改查询以使其不再发生。无论谁帮忙,谢谢。 :)

2 个答案:

答案 0 :(得分:0)

工作=&gt;重构(让它工作,然后重构)

工作:主要问题是尝试在一个动作中做太多。当您使用LINQ时,操作将延迟到请求运行,因此编译器无法捕获问题(在这种情况下不会发生问题)。

开始处理它的一种方法是首先获取父查询的信息(在这里看起来像Employee)。然后,您可以提取其他查询所需的信息,构建层次结构。

这将在将信息编译到List中做更多工作,但它将避免循环引用。

重构:一旦你使它工作,然后重构使它更容易理解和简洁。您可能会发现,一旦您拿出将要递归的部分,您就可以使用单个查询构建您的列表。

注意:我希望我有一个“只做这个”的答案,但我没有时间翻阅所有代码,看看是否有一个特定的部分可以解决递归问题。

答案 1 :(得分:0)

我通常使用两种方法,既关注序列化而不是模型结构本身。 (由于该模型在服务器端代码中很好,而且它只是序列化导致问题。)

首先,让我们简化一个更为人为的例子来说明。像这样:

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的实际吸引力。

导航属性在服务器端代码中非常出色(在实体框架中经常是必需的),但它们对序列化程序没有多大意义。