简体   繁体   English

LINQ to Entities对带有嵌套对象的Union的null引用

[英]LINQ to Entities null reference on Union with nested objects

Given the following class structure: 给定以下类结构:

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
}

And the following queries which return Users and Invitations, respectively, with the intent of getting a union of the two queries: 以下查询分别返回用户和邀请,目的是获得两个查询的并集:

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();

When I attempt to do the union DB-side, I get a null reference exception deep down in System.Data.Entity.CoreQuery.PlanCompiler. 当我尝试使用union DB-side时,我在System.Data.Entity.CoreQuery.PlanCompiler中深入获得了一个null引用异常。 If I call ToList() on each part individually, it works; 如果我单独调用每个部件上的ToList(),它就可以工作; and if I call ToList() on each part and then union those, it works. 如果我在每个部分上调用ToList()然后将它们联合起来,它就可以了。

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

It also appears that if I union them BEFORE creating the ResponseAddress part, then create the ResponseAddress part in a later call to Select, it works: 看来如果我在创建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();

Any thoughts as to why the call to Union in the first set of queries would return a null reference exception, while the call in the last query does not? 关于为什么在第一组查询中调用Union会返回空引用异常的任何想法,而最后一个查询中的调用不会? Both are executed DB-side, and both should produce similar queries (theoretically virtually identical, save for the way LINQ does query nesting.) 两者都在DB端执行,两者都应该产生类似的查询(理论上几乎相同,除了LINQ查询嵌套的方式。)

In the ResponseAddress part, you create a new instance of ResponseAddress class. 在ResponseAddress部分中,您将创建一个ResponseAddress类的新实例。 This has no meaning for the relational database. 这对关系数据库没有意义。 When you turn your query results into list, then it's runtime's job to handle the union and it knows about objects whereas database server has no knowledge of ResponseAddress because that's not how it represents data. 当您将查询结果转换为列表时,它是运行时处理联合的工作,它知道对象,而数据库服务器不知道ResponseAddress,因为它不是它如何表示数据。

DbSet and IDbSet implements IQueryable meaning a query is executed against the database only when: DbSet和IDbSet实现IQueryable意味着仅在以下情况下对数据库执行查询:

  1. It is enumerated by a foreach (C#) or For Each (Visual Basic) statement. 它由foreach(C#)或For Each(Visual Basic)语句枚举。
  2. It is enumerated by a collection operation such as ToArray, ToDictionary or ToList. 它由ToArray,ToDictionary或ToList等集合操作枚举。
  3. LINQ operators such as First or Any are specified in the outermost part of the query. LINQ运算符(如First或Any)在查询的最外部指定。
  4. The following methods are called: the Load extension method on a DbSet, DbEntityEntry.Reload and Database.ExecuteSqlCommand 调用以下方法:DbSet上的Load扩展方法,DbEntityEntry.Reload和Database.ExecuteSqlCommand

     var users = db.Users.Select(x => new var users = db.Users.Select(x => new  \n{ {\nId = x.Id, Id = x.Id,\nZip = x.Address.Zip Zip = x.Address.Zip\n}); }); 

As it is IQueryable above query has not yet executed against the database and users is an empty collection rather than null 由于IQueryable上面的查询尚未对数据库执行,因此用户是空集合而不是null

var users = db.Users.Select(x => new  
{
    Id = x.Id,
    Zip = x.Address.Zip
}).ToList();

Now calling the ToList() has executed the query to the db. 现在调用ToList()已经执行了对db的查询。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM