实体框架 linq 主细节投影到自定义类

Posted

技术标签:

【中文标题】实体框架 linq 主细节投影到自定义类【英文标题】:Entity Framework linq master detail projection into custom classes 【发布时间】:2014-12-31 12:59:03 【问题描述】:

我有一个 Order 类,其属性为“ID 为 Int64,描述为字符串,详细信息为 List(Of OrderDetail)” 还有一个 OrderDetail 类,其属性为“ID 为 Int64,描述为字符串,金额为十进制”

Public Class Order
    Property ID As Int64
    Property Description As String
    Property Details As List(Of OrderDetail)
End Class

Public Class OrderDetail
    Property ID As Int64
    Property Description As String
    Property Amount As Decimal
End Class

然后我创建了一个静态/共享函数来使用 LINQ 调用实体框架,并希望在每个订单上创建一个订单列表以及 OrderDetails。

Public Shared Function GetOrders() As List(Of Order)
    Dim db = New OrderEntities()

    Dim orders = (From O In db.OrderTable
    Select New Order With 
        .ID = O.OrderID,
        .Description = O.OrderDescription,
        .Details = (From OD In db.OrderDetailTable
            Where OD.OrderID = O.OrderID
            Select New OrderDetail With 
                .ID = OD.OrderDetailID,
                .Description = OD.OrderDetailDescription,
                .Amount = OD.OrderDetailAmount)
        
    ).ToList()

    Return orders
End Function

执行时会出现以下错误。

Unable to cast the type 'System.Linq.IQueryable`1[[OrderDetails]]' to type 
'System.Collections.Generic.List`1[[OrderDetails]]'. 
LINQ to Entities only supports casting EDM primitive or enumeration types.

注意: 这是实体框架问题,而不是 LINQ 问题。 例如,当使用 EF 但不投影到新对象列表时,以下内容可以正常工作。

    Public Shared Function GetOrders() As Object
    Dim db = New OrderEntities()

    Dim orders = (From O In db.OrderTable
    Select New With 
        .ID = O.OrderID,
        .Description = O.OrderDescription,
        .Details = (From OD In db.OrderDetailTable
            Where OD.OrderID = O.OrderID
            Select New With 
                .ID = OD.OrderDetailID,
                .Description = OD.OrderDetailDescription,
                .Amount = OD.OrderDetailAmount)
        
    ).ToList()

    Return orders
End Function

编辑: 好吧,这太疯狂了。问题似乎是 EF 的 VB.NET 问题。 这个 C# 版本有效。

var data = db.OrderTable
    .Select(O => new Order  ID = O.OrderID, 
    Description = O.OrderDescription,
    Details = db.OrderDetailTable.Where(D => D.OrderID == O.OrderID)
        .Select(OD => new OrderDetail  Description = OD.OrderDetailDescription))
        .ToList();  

这在 VB.NET 中不起作用。

Dim data = db.OrderTable
    .Select(Function(O) New Order With .ID = O.OrderID, 
    .Description = O.OrderDescription,
    .Details = db.OrderDetailTable.Where(Function(D) D.OrderID = O.OrderID)
        .Select(Function(OD) New OrderDetail With .Description = OD.OrderDetailDescription))
        .ToList()

问题似乎与此工作项有关。https://entityframework.codeplex.com/workitem/808

编辑: 根据 UrbanEsc 和 jameslj,我能够获得一些工作代码。 似乎如果 Order.Details 属性是“LIST”,那么代码只有在使用时才有效:

Dim orders = (From O In db.OrderTable
    Select New With 
        .ID = O.OrderID,
        .Description = O.OrderDescription,
        .Details = (From OD In db.OrderDetailTable.AsEnumerable()
            Where OD.OrderID = O.OrderID
            Select New With 
                .ID = OD.OrderDetailID,
                .Description = OD.OrderDetailDescription,
                .Amount = OD.OrderDetailAmount).ToList()
        
    ).ToList()

注意细节上的“AsEnumerable”和“ToList()”。两者都是必需的。 但是,如果我将 Order.Details 属性更改为“IQueryable”或“IEnumerable”,那么我可以删除额外的“ToList”并使用 AsQueryable 或 AsEnumerable。

Property Details As IQueryable(Of OrderDetail)

Dim orders = (From O In db.OrderTable
    Select New With 
        .ID = O.OrderID,
        .Description = O.OrderDescription,
        .Details = (From OD In db.OrderDetailTable.AsQueryable()
            Where OD.OrderID = O.OrderID
            Select New With 
                .ID = OD.OrderDetailID,
                .Description = OD.OrderDetailDescription,
                .Amount = OD.OrderDetailAmount)
        
    ).ToList()

【问题讨论】:

【参考方案1】:

根据我的评论,您需要为您的 SELECT on Details 添加一个 .ToList()。

Public Shared Function GetOrders() As List(Of Order)
    Dim db = New OrderEntities()

    Dim orders = (From O In db.OrderTable
    Select New Order With 
        .ID = O.OrderID,
        .Description = O.OrderDescription,
        .Details = (From OD In db.OrderDetailTable
            Where OD.OrderID = O.OrderID
            Select New OrderDetail With 
                .ID = OD.OrderDetailID,
                .Description = OD.OrderDetailDescription,
                .Amount = OD.OrderDetailAmount)
            .ToList()
        
    ).ToList()

    Return orders
End Function

【讨论】:

ToList() 不起作用。这返回了一个不同的错误。 “无法转换类型 'System.Linq.IQueryable1[[OrderDetails]]' to type 'System.Collections.Generic.IEnumerable1[[OrderDetails]]'。LINQ to Entities 仅支持转换 EDM 基元或枚举类型。” 您发布的代码不起作用。我直接复制并粘贴,当我运行代码时,我得到“无法将类型'System.Linq.IQueryable1[[OrderDetails]]'转换为'System.Collections.Generic.IEnumerable1[[OrderDetails]]'。LINQ to实体仅支持转换 EDM 基元或枚举类型。” 您的代码非常接近。我使用了@jameslj 代码,并将您的代码“从 db.OrderDetailTable 中的 OD”更改为“从 db.OrderDetailTable.AsEnumerable() 中的 OD”并且它可以工作。仍然需要代码中的所有“ToList()”。 用正确的代码回答您自己的问题并将其标记为答案【参考方案2】:
orders = db.OrderTable.Select(Function(x) New Order() With 
    .Id = x.OrderID,
            .Description = x.OrderDescription,
            .Details = db.OrderDetailTable.Where(
                Function(y) y.OrderId = x.OrderID).ToList().Select(
                    Function(z) New OrderDetail() With
                        .Id = z.OrderDetailId,
                         .Description = z.OrderDetailDescription,
                         .Amount = z.OrderDetailAmount).ToList()).ToList()

【讨论】:

此代码不起作用。它返回以下错误。 “无法转换类型 'System.Collections.Generic.List1[[OrderDetail]]' to type 'System.Collections.Generic.ICollection1[[OrderDetail]]'。LINQ to Entities 仅支持转换 EDM 基元或枚举类型。” 抱歉,您的代码确实有效。当它应该是一个列表时,我将 Details 属性更改为一个 Collection。

以上是关于实体框架 linq 主细节投影到自定义类的主要内容,如果未能解决你的问题,请参考以下文章

实体框架 - 您能否将导入的存储过程的结果类型映射到自定义实体类型?

使用 RestKit 将 NSManagedObject 实体映射到自定义类

Spring Boot制作个人博客-框架搭建(设计与规范,实体类构建)

Linq 投影:获取新投影实体的参考

Web在线聊天室 --- 异常类+工具类+实体类

如何以编程方式添加尺寸类自定义