所以我试图在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();
循环参考问题仍在继续,我不知道如何更改查询以使其不再发生。无论谁帮忙,谢谢。 :)
答案 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
的实际吸引力。
导航属性在服务器端代码中非常出色(在实体框架中经常是必需的),但它们对序列化程序没有多大意义。