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

时间:2016-01-04 19:14:48

标签: c# entity-framework linq

给出以下类结构:

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

当我尝试执行union DB-side时,我在System.Data.Entity.CoreQuery.PlanCompiler中深入获得了一个null引用异常。如果我单独调用每个部件上的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查询嵌套的方式。)

2 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:0)

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

  1. 它由foreach(C#)或For Each(Visual Basic)枚举 言。
  2. 它由ToArray等集合操作枚举, ToDictionary或ToList。
  3. 在最外层指定LINQ运算符,例如First或Any 查询的一部分。
  4. 调用以下方法:a上的Load扩展方法 DbSet,DbEntityEntry.Reload和Database.ExecuteSqlCommand

    var users = db.Users.Select(x => new  
    {
    Id = x.Id,
    Zip = x.Address.Zip
    });
  5. 因为它是IQueryable以上查询还没有针对数据库执行而用户是一个空集合而不是null

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

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