【问题标题】:LINQ to Entities null reference on Union with nested objects带有嵌套对象的联合上的 LINQ to Entities 空引用
【发布时间】: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 关键字,这是加载相关实体所必需的

标签: c# entity-framework linq


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

    1. 它由 foreach (C#) 或 For Each (Visual Basic) 枚举 声明。
    2. 由ToArray等集合操作枚举, ToDictionary 或 ToList。
    3. 在最外层指定了诸如 First 或 Any 等 LINQ 运算符 查询的一部分。
    4. 调用以下方法: 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 的查询。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多