通过LINQ

时间:2016-03-17 14:24:16

标签: c# linq entity-framework-core

我试图使用LINQ获取所有子实体的某个实体的列表,但我使用的方法都没有正常工作。 这是类结构:

public class License {
   public virtual Product Product {get; set}
   . . . . . . 
   //some other License properties
   . . . . . . 
}

public class LicenseUser {
   public virtual Account User { get; set;}
   public virtual License License { get; set; }
}

我试图获取某些用户的许可证列表,但每个License对象也必须包含Product。我试过的任何查询都返回了许可证列表" null"产品属性中的值 以下是LINQ查询我尝试过的示例:

var licenses = context.LicenseUsers
    .Include(lu => lu.License.Product)
    .Where(lu => lu.User.Id == 1)
    .Select(lu => lu.License)
    .ToList();

var licenses = context.LicenseUsers
    .Include(lu => lu.License).ThenInclude(l => l.Product)
    .Where(lu => lu.User.Id == 1)
    .Select(lu => lu.License)
    .ToList();

我确信这些查询获得的记录包含对Products表的有效引用(ProductId字段不为null)。 我还检查了这个LINQ请求生成的SQL(使用诊断工具)。正如预期的那样,它不包含Products表的JOIN语句。

是否有正确的方法来获得必要的结果?

4 个答案:

答案 0 :(得分:3)

这有点违反直觉,但问题是您的Select正在过滤掉SQL查询包含的内容。

因此,您可能希望强制EF将产品包含在SQL中(使用包含许可证及其产品的匿名对象),然后转换为内存中的License集合:

var licenses = context.LicenseUsers
  .Include(lu => lu.License.Product)
  .Where(lu => lu.User.Id == 1)
  .Select(lu => new { lic = lu.License, prod = lu.License.Product } )
  .AsEnumerable()  // Here we have forced the SQL to include the product too
  .Select(lu => lu.lic)
  .ToList(); // Then we select (locally) only the license for convenience 
             // (so that our collection is of type License)
             // Since the SQL Query actually loaded the products
             // the references will be ok

答案 1 :(得分:2)

嗯,我自己也很陌生,但这可能会给你一个想法。

根据this MSDN文章,要急切加载多个级别,您可以尝试这样的事情:

class MyObject {

    private OtherObject otherObject;

    public OtherObject getOtherObject() {
        return otherObject;
    }
}

...
myObjects.stream().map(MyObject::getOtherObject).forEach(System.out::println)

并检查其他示例,正如亚历山大所说,Where()在任何情况下应该在Include()之前。

答案 2 :(得分:0)

您需要。选择您想要的属性。在这里,您只需提取许可证属性。尝试将其更改为:

var licenses = context.LicenseUsers
    .Include(lu => lu.License.Product)
    .Where(lu => lu.User.Id == user.Id)
    .Select(lu => new { lu.License, lu.Product })
    .ToList();

在这里,您要创建一个新的匿名类型,其中包含您想要附加的两个属性。现在,许可证应该包含这些新的匿名类型的列表,您可以通过

之类的方式访问这些类型
Licenses[0].Product

答案 3 :(得分:0)

我们找到了一种解决方法,但在一般情况下可能会导致效率低下。 首先,Include调用正常工作并正确填充LicenseUser对象列表中的License.Product属性。 问题出在最后一次选择呼叫"转换"许可证列表中的LicenseUser对象列表。 解决方案 - 添加" ToList"在Where:

之后打电话
var licenses = context.LicenseUsers
    .Include(lu => lu.License.Product)
    .Where(lu => lu.User.Id == 1)
    .ToList()
    .Select(lu => lu.License)
    .ToList();

(我认为其中一个答案中建议的AsEnumerable调用也适用于此处。)

但是我仍然认为它是Entity Framework Core的LINQ to SQL实现中的一个错误。它可能会看到最后一个Select(它获取LicenseUser的许可证部分)并且忽略了第一个Include请求将结果集添加产品信息(以SQL方式连接到Products表)。