【问题标题】:A circular reference was detected while serializing an object of type... How to solve this?序列化类型对象时检测到循环引用......如何解决这个问题?
【发布时间】:2015-08-13 16:30:46
【问题描述】:

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

循环引用问题仍然存在,我不知道应该如何更改我的查询,以免再次发生。谁有帮助,谢谢。 :)

【问题讨论】:

    标签: c# linq


    【解决方案1】:

    Working => 重构(让它工作,然后重构)

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

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

    这将在将信息编译成List时做更多的工作,但会避免循环引用。

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

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

    【讨论】:

      【解决方案2】:

      对此我通常使用两种方法,都侧重于序列化点而不是模型结构本身。 (因为模型在服务器端代码中很好,它只是序列化会导致问题。)

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

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

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

      【讨论】:

      • 在子类中,我没有指向父类的属性,但我在实体框架的数据库模型中有该属性。我将尝试第二个示例... :)
      猜你喜欢
      • 2012-05-04
      • 2012-12-01
      • 1970-01-01
      • 2015-01-21
      • 2010-11-12
      • 2013-04-23
      • 2010-10-14
      • 1970-01-01
      相关资源
      最近更新 更多