单个 LINQ 中两个结构不兼容的初始化出错

Jef*_*son 5 c# linq entity-framework

我有一个下面的模型,当我使用下面的 linq 添加 ComboProducts 对象时,出现错误。这段代码中我缺少什么吗?

错误

出现在单个 LINQ to Entities 查询中的两个结构不兼容的初始化中。一个类型可以在同一个查询的两个地方初始化,但前提是在两个地方设置了相同的属性,并且这些属性的设置顺序相同。

模型

public class Product
{
    public int ProductId { get; set; }
    public string Name { get; set; }
    public List<Product> ComboProducts { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

代码

public class Product
{
    public int ProductId { get; set; }
    public string Name { get; set; }
    public List<Product> ComboProducts { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

Ger*_*old 5

异常消息很清楚,select new NCCN.Model.Product在一个 LINQ 语句中有两个语句,它们没有以相同的顺序设置相同的属性。这是我们必须处理的 EF 限制。LINQ-to-objects 不会抛出这个异常。

虽然你的问题不是很清楚,但我想我明白你实际上在问什么。即使在理解异常消息时,如何处理它也不是很明显。在指出问题之前,让我先介绍一个简化的模型。

public class Product
{
    public int ProductId { get; set; }
    public string Name { get; set; }
    
    public ICollection<ProductComponent> Components { get; set; }
}

public class ProductComponent
{
    public int ProductId { get; set; }
    public int ComponentProductId { get; set; }
    public Product Product { get; set; }
    public Product ComponentProduct { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

带映射代码。

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<ProductComponent>().HasKey(e => new { e.ProductId, e.ComponentProductId });
    modelBuilder.Entity<ProductComponent>().HasRequired(e => e.Product)
        .WithMany(p => p.Components).HasForeignKey(e => e.ProductId);
    modelBuilder.Entity<ProductComponent>().HasRequired(e => e.ComponentProduct)
        .WithMany().HasForeignKey(e => e.ComponentProductId)
        .WillCascadeOnDelete(false); // Prevent circular cascade
}
Run Code Online (Sandbox Code Playgroud)

现在我们可以使用导航属性而不是一直加入。

任务是将此模型映射到 DTO 类:

public class ProductDto
{
    public int ProductId { get; set; }
    public string Name { get; set; }
    public List<ProductDto> ComboProducts { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

应该很简单

var products = db.Products.Select(p => new ProductDto
{
    ProductId = p.ProductId,
    Name = p.Name,
    ComboProducts =
        p.Components.Select(pc => pc.ComponentProduct)
         .Select(c => new ProductDto
         {
            ProductId = c.ProductId,
            Name = c.Name,
         }).ToList()
});
Run Code Online (Sandbox Code Playgroud)

但是现在EF抛出你报告的异常。在您的情况下,您甚至跳过了整个范围的属性,这里只是ComboProducts在嵌套中ProductDto,但这已经足够了。内部也ProductDto应该有一个ComboProducts集合。

不幸的是,这并不像人们期望的那样简单。例如,使用这个内部选择...

 .Select(c => new ProductDto
 {
    ProductId = c.ProductId,
    Name = c.Name,
    ComboProducts = null
 }).ToList()
Run Code Online (Sandbox Code Playgroud)

...EF 投掷

NotSupportedException:无法创建类型为“System.Collections.Generic.List`1[[ProductDto]]”的空常量值。在此上下文中仅支持实体类型、枚举类型或基本类型。

和...

 .Select(c => new ProductDto
 {
    ProductId = c.ProductId,
    Name = c.Name,
    ComboProducts = null
 }).ToList()
Run Code Online (Sandbox Code Playgroud)

...投掷

NotSupportedException:无法在 LINQ to Entities 查询中初始化实现 IEnumerable 'System.Collections.Generic.List`1[[ProductDto]]' 的类型。

归结为:对于这样的嵌套投影,您必须对主要类型和嵌套类型使用两种不同的类型。但是匿名类型也符合要求,所以我认为解决这个烦人的 EF 限制的最简单方法是投射到匿名类型,然后ProductDto

 .Select(c => new ProductDto
 {
    ProductId = c.ProductId,
    Name = c.Name,
    ComboProducts = new List<ProductDto>()
 }).ToList()
Run Code Online (Sandbox Code Playgroud)