实体框架6:包含多对多子级嵌套的可空值

时间:2019-05-16 08:14:15

标签: c# entity-framework linq entity-framework-6

我的包含始终为空。尽管我可以在SQL数据库中看到父级和子级之间的链接。

IQueryable<ManageProductItems> models = db.Products
    .Include(m => m.Collections).Include(m => m.Images)
    .Include(m => m.VariantDetails.Select(x => x.Variants.Select(z => z.VariantType)))
    .Select....

我要包含嵌套的Products -> VariantDetails -> Variants -> VariantType

结果总是给我Variants = null

这是模型配置:

public class Product
{
    ...
    public ICollection<ProductCollection> Collections { get; set; }
    public ICollection<ProductVariantDetail> VariantDetails { get; set; }
    public ICollection<ProductImage> Images { get; set; }

    public ICollection<ProductVariantType> ProductVariantTypes { get; set; }
    public ICollection<ProductVariant> ProductVariants { get; set; }
}

public class ProductVariantDetail
{
    ....
    public ICollection<ProductVariant> Variants { get; set; }

    [ForeignKey("Product")]
    public Guid ProductId { get; set; }

    [CascadeDelete]
    public virtual Product Product { get; set; }

    public virtual ProductImage ProductImage { get; set; }
}

public class ProductVariant
{
    ...
    [ForeignKey("VariantType")]
    public Guid VariantTypeId { get; set; }

    [CascadeDelete]
    public virtual ProductVariantType VariantType { get; set; }

    public ICollection<ProductVariantDetail> VariantDetails { get; set; }

    [ForeignKey("Product")]
    public Guid? ProductId { get; set; }
    public Product Product { get; set; }
}

public class ProductVariantType
{
    ...
    [ForeignKey("Product")]
    public Guid? ProductId { get; set; }
    public Product Product { get; set; }
}

SQL服务器中有dbo.ProductVariantDetailProductVariants表,它们映射ProductVariantDetailProductVariant。两个表中的映射ID是正确的。

但这会发生:

Products -> VariantDetails -> Variants = null


编辑1

我试图通过以下方式缩短查询时间:

var xxx = db.ProductVariantDetails.Include(m => m.Variants).ToList();

正确加载了变体。因此,我确定嵌套的include或bug会存在问题。


编辑2

这太奇怪了。因此,我尝试使用该代码。我把这个:

var xxx = db.ProductVariantDetails.Include(m => m.Variants.Select(z => z.VariantType)).ToList();

IQueryable<ManageProductItems> models = db.Products
    .Include(m => m.Collections).Include(m => m.Images)
    .Include(m => m.VariantDetails.Select(x => x.Variants.Select(z => z.VariantType)))
    .Select....

因此,xxx缓存了VariantType,然后当models查询整个实体时,Product -> VariantDetails -> Variants -> VariantType被正确加载。如果我只查询models而没有xxx,则问题仍然存在。

因此,db需要在此之前缓存VariantsVariantTypes

我该如何解决?

2 个答案:

答案 0 :(得分:0)

注意:这是第二个答案,它解决了代码中的第二个问题。首先阅读其他答案,因为我怀疑这将是您问题的最直接答案。

您的Select正在撤消/忽略您的IncludeInclude用于(显式)隐式自动获取相关实体,但是Select明确要求提供其他返回值,该返回值将覆盖您设置的隐式包含规则。

一些展示其工作原理的示例:

var list = db.Products
             .ToList();

这将返回未加载相关实体的产品列表。

var list = db.Products
             .Include(m => m.ProductOwner)
             .ToList();

这将返回同时加载了ProductOwner属性的产品列表。

var list = db.Products
             .Select(p => p.Name)
             .ToList();

var list2 = db.Products
             .Include(m => m.ProductOwner)
             .Select(p => p.Name)
             .ToList();

两个列表都是一个字符串列表(带有产品名称)。进行Include无关紧要。

var list = db.Products
             .Select(p => p.ProductOwner.Name)
             .ToList();

var list2 = db.Products
             .Include(m => m.ProductOwner)
             .Select(p => p.ProductOwner.Name)
             .ToList();

两个列表都是字符串列表(带有产品所有者名称)。进行Include无关紧要。

这是需要注意的重要部分:您不需要Include来获取相关ProductOwner实体的名称。这是因为您的Select语句明确要求EF提取相关实体数据,因此EF不需要Include,因为它无论如何都不需要隐式加载相关实体。

对于任何Select语句(包括那些仍在使用完整Product实体的语句)都保持这种行为。意思是:

var list = db.Products
             .Select(p => new { Product = p })
             .ToList();

var list2 = db.Products
             .Include(m => m.ProductOwner)
             .Select(p => new { Product = p })
             .ToList();

在两种情况下,您都将获得Product实体而没有与之相关的ProductOwner,因为EF会监听Select并忽略其他有关要加载的数据(包括Include)。

答案 1 :(得分:0)

您缺少一个包含项:

db.Products
  .Include(m => m.Collections)
  .Include(m => m.Images)
  .Include(m => m.VariantDetails.Select(x => x.Variants.Select(z => z.VariantType)))
  .Select(...);

您从未要求自己加载Variants。未加载Variants时,所有随后相关的实体都将被忽略。

请尝试以下操作:

db.Products
  .Include(m => m.Collections)
  .Include(m => m.Images)
  .Include(m => m.VariantDetails.Select(x => x.Variants))
  .Include(m => m.VariantDetails.Select(x => x.Variants.Select(z => z.VariantType)))
  .Select(...);

但是,可能由于掩盖Select语句而隐藏了第二个问题。我添加了第二个答案来解决这个潜在的次要问题。