Entity framework EF匿名对象查询返回空集合而不是空集合

Entity framework EF匿名对象查询返回空集合而不是空集合,entity-framework,collections,nullreferenceexception,anonymous,fixup,Entity Framework,Collections,Nullreferenceexception,Anonymous,Fixup,我用这个技巧用EF执行条件Include 我遇到的问题是,任何没有记录的集合都是空的,并且不是空的。这让人头疼,因为我必须先检查每个集合,然后才能在mvc视图中循环使用它,否则会出现空引用异常 例如,StudentModules集合将为null。如何在查询中将其转换为空列表?ie,而无需对其进行循环检查 我可以在poco中放置一个构造函数来初始化列表,这会修复它,但是这个集合是poco中的一个虚拟成员(基于EF视频!)-当然不是这样做的吗 var query = from module in d

我用这个技巧用EF执行条件Include

我遇到的问题是,任何没有记录的集合都是空的,并且不是空的。这让人头疼,因为我必须先检查每个集合,然后才能在mvc视图中循环使用它,否则会出现空引用异常

例如,StudentModules集合将为null。如何在查询中将其转换为空列表?ie,而无需对其进行循环检查

我可以在poco中放置一个构造函数来初始化列表,这会修复它,但是这个集合是poco中的一个虚拟成员(基于EF视频!)-当然不是这样做的吗

var query = from module in db.Modules
            where module.Id == id
            select new 
            {
              module,
              QualificationModules = from qualificationModule in module.QualificationModules
                                     where qualificationModule.IsDeleted == false
                                     select new
                                     {
                                       qualificationModule,
                                       qualificationModule.Qualification,
                                       StudentModules = from studentModule in qualificationModule.StudentModules
                                                        where studentModule.IsDeleted == false 
                                                        select new
                                                        {
                                                          studentModule,
                                                          studentModule.Student
                                                        }
                                     },

              Assessments = (from assessment in module.Assessments
                             where assessment.IsDeleted == false
                             select new
                             {
                               assessment,
                               assessment.AssessmentType
                             }
                            )
            };

var modules = query.AsEnumerable().Select(x => x.module);

return modules.ToList().First();

当一个实体被附加到上下文时,关系修复将运行—可以通过调用
Attach
手动执行,也可以通过查询(您的案例)将该实体具体化

它基于实体的外键并在两个方向上工作:

  • 如果上下文已经包含一个实体
    A
    ,该实体具有一个外键
    f
    到实体
    B
    ,并且一个实体
    B
    被附加到上下文,该上下文的主键与
    A
    中的外键具有相同的值
    f
    (即,两个实体通过FK关系关联)然后实体框架将执行以下操作:

    • 如果
      A
      具有指向
      B
      的导航引用属性,它将为该属性分配附加的实体
      B
    • 如果
      B
      具有指向
      a
      (一对一关系)的导航引用属性,则将
      a
      分配给该属性
    • 如果
      B
      具有指向
      a
      (一对多关系)的导航集合属性,则将
      a
      添加到附加实体
      B
      中的该集合中。如果集合为
      null
      ,它将在添加之前实例化集合
  • 如果实体
    B
    被附加到具有外键
    f
    的上下文,而实体
    a
    则上下文已经包含并且具有
    f
    作为主键,则EF将基于上述相同规则设置导航属性

作为旁注:关系修复基于外键(无论FK是否在模型类中作为属性公开,在查询实体时都会加载外键)也是关系修复不适用于多对多关系且不适用于多对多关系的原因,因为多对多关系的两个实体没有外键

现在,如果在您的案例中没有相关的
StudentModules
,那么就没有加载到上下文中的
StudentModule
实体,EF也没有什么可以用于修复的目标。请记住,固定算法与特定查询无关,并且不仅修复了该查询将实现的实体之间的关系,而且考虑到上下文如何包含,所有的实体都会考虑上下文是否已包含,而不管它们是如何进入上下文的。如果希望集合被实例化为空集合,则EF已通过
StudentModules
的所有附加父实体运行,并仅创建一个空集合。在修复过程中这样做而不是在实体连接到上下文之前创建空集合是没有意义的

我可以在poco中放置一个构造函数来初始化列表,这 修复了它,但此集合是poco中的虚拟成员 (基于EF视频!)-这肯定不是一条路吗

var query = from module in db.Modules
            where module.Id == id
            select new 
            {
              module,
              QualificationModules = from qualificationModule in module.QualificationModules
                                     where qualificationModule.IsDeleted == false
                                     select new
                                     {
                                       qualificationModule,
                                       qualificationModule.Qualification,
                                       StudentModules = from studentModule in qualificationModule.StudentModules
                                                        where studentModule.IsDeleted == false 
                                                        select new
                                                        {
                                                          studentModule,
                                                          studentModule.Student
                                                        }
                                     },

              Assessments = (from assessment in module.Assessments
                             where assessment.IsDeleted == false
                             select new
                             {
                               assessment,
                               assessment.AssessmentType
                             }
                            )
            };

var modules = query.AsEnumerable().Select(x => x.module);

return modules.ToList().First();

在我看来,如果您不希望模型类实例中有
null
集合,那么是最好的解决方案。集合是否声明为
virtual
(以启用延迟加载)并不重要。集合类型没有派生代理类型,只有添加到集合中的实例是派生代理。在这两种情况下,您都可以使用
StudentModules=newhashset()(或
列表
,如果您愿意)。

aaaaaah,我明白了,很好的解释。非常彻底。谢谢你。这个算法最大的问题是它使实体是否没有子实体或者它们是否没有被加载变得模糊不清。其中一个可能是编程错误(忘记立即加载),而另一个是有效的系统状态。在许多web场景中,延迟加载很可能是一种体系结构反模式,并在上下文级别故意禁用,因为您希望确保数据库服务器和web服务器之间只发生一次往返。是否有其他方法可以推断相关实体(集合或单个引用)是否已加载?@Marchy:我认为唯一可靠的模式是:
if(!context.Entry(parent).reference(p=>p.Child).IsLoaded)context.Entry(parent).reference(p=>p.Child).Load()
(或
.collection(p=>p.Children)
用于收藏)。但是,它可能会生成不必要的查询,因为
IsLoaded
可能返回
false
,尽管由于之前的一些查询,所有相关实体都已附加。EF将检测到这一点,但仅在
.Load()
触发器的查询返回结果后。