带有嵌套对象的联合上的 LINQ to Entities 空引用

Posted

技术标签:

【中文标题】带有嵌套对象的联合上的 LINQ to Entities 空引用【英文标题】:LINQ to Entities null reference on Union with nested objects 【发布时间】:2016-04-08 11:33:02 【问题描述】:

给定以下类结构:

public class User  // DB model

    public Guid Id  get; set; 
    public Address Address  get; set; 
    // And other propeties


public class Invitation  // DB model

    public Guid Id  get; set; 
    // And other propeties


public class Address  // DB model

    public string Zip  get; set; 
    // And other properties


public class ResponseModel

    public Guid Id  get; set; 
    public ResponseAddress Address  get; set; 


public class ResponseAddress

    public string Zip  get; set; 
    // And other properties

以下查询分别返回用户和邀请,目的是获得两个查询的联合:​​

var users = db.Users.Select(x => new ResponseModel() 

    Id = x.Id,
    Address = new ResponseAddress()
    
        Zip = x.Address.Zip
    
);
var invitations = db.Invitations.Select(x => new ResponseModel() 

    Id = x.Id,
    Address = new ResponseAddress()
    
        Zip = String.Empty
    
);
var union = users.Union(invitations).ToList();

当我尝试执行联合数据库端时,我在 System.Data.Entity.CoreQuery.PlanCompiler 的深处得到一个空引用异常。如果我分别在每个部分上调用 ToList() ,它就可以工作;如果我在每个部分上调用 ToList() 然后合并它们,它就可以工作。

users.ToList();
invitations.ToList();
users.ToList().Union(invitations.ToList());

如果我在创建 ResponseAddress 部分之前合并它们,然后在稍后调用 Select 时创建 ResponseAddress 部分,它似乎也可以工作:

var users = db.Users.Select(x => new  

    Id = x.Id,
    Zip = x.Address.Zip
);
var invitations = db.Invitations.Select(x => new  

    Id = x.Id,
    Zip = String.Empty
);
var union = users.Union(invitations).Select(x=>new ResponseModel() 
    Id = x.Id,
    Address = new ResponseAddress() 
        Zip = x.Zip
    
).ToList();

关于为什么在第一组查询中调用 Union 会返回空引用异常,而在最后一个查询中调用不会返回有什么想法吗?两者都在 DB 端执行,并且都应该产生相似的查询(理论上几乎相同,除了 LINQ 进行查询嵌套的方式。)

【问题讨论】:

您可以在IQueryable上使用ToString()查看查询 另外我在您的模型中没有看到任何 virtual 关键字,这是加载相关实体所必需的 【参考方案1】:

在 ResponseAddress 部分,您创建 ResponseAddress 类的新实例。这对关系数据库没有意义。当您将查询结果转换为列表时,处理联合是运行时的工作,它知道对象,而数据库服务器不知道 ResponseAddress,因为它不是表示数据的方式。

【讨论】:

【参考方案2】:

DbSet 和 IDbSet 实现 IQueryable 意味着执行查询 仅在以下情况下针对数据库:

    它由 foreach (C#) 或 For Each (Visual Basic) 枚举 声明。 由ToArray等集合操作枚举, ToDictionary 或 ToList。 在最外层指定了诸如 First 或 Any 等 LINQ 运算符 查询的一部分。

    调用以下方法: DbSet、DbEntityEntry.Reload 和 Database.ExecuteSqlCommand

    var users = db.Users.Select(x => new
    
    Id = x.Id,
    邮编 = x.地址.邮编
    );

因为它是 IQueryable 上面的查询还没有对数据库执行,用户是一个空集合而不是 null

var users = db.Users.Select(x => new  

    Id = x.Id,
    Zip = x.Address.Zip
).ToList();

现在调用 ToList() 已经执行了对 db 的查询。

【讨论】:

以上是关于带有嵌套对象的联合上的 LINQ to Entities 空引用的主要内容,如果未能解决你的问题,请参考以下文章

[C#Linq嵌套查询中的联合集合

如何关联不同 dbml 图上的 Linq-To-Sql 对象?

Linq XML to Object,其中 xml 节点具有嵌套元素

为啥 Linq-to-sql 错误地删除了我的联合中的字段?

我应该在linq-to-sql中选择哪种关系?

LINQ to SQL 执行联合和左外连接