【问题标题】:LINQ to Entities. How to create "Reusable" methods that "wrap" entities?LINQ 到实体。如何创建“包装”实体的“可重用”方法?
【发布时间】:2015-01-10 23:48:12
【问题描述】:

设置
我有一个“Order”类和一个“OrderDetail”类。
Order 类有一个名为“OrderDetails”的 IQueryable 类型的属性。
我在每个类中都有一个名为“GetData”的方法,该方法返回它所在类的 IQueryable。
“GetData”方法是实体的包装器,用于将实体转换为更友好的 .NET 类/属性。
例如: OrderDetail 实体在数据库中有一个“PullDate”作为 Char(10),但我希望 OrderDetail 类有一个名为“PullDate”的属性,该属性在类中属于 DateTime 类型。

代码

public class Order
{
    [Key]
    public int ID { get; set; }
    public IQueryable<OrderDetail> OrderDetails { get; set; }

    public static IQueryable<Order> GetData()
    {
        IQueryable<Order> orders;
        var db = new OrderEntities();

        // NOTE: This code will work
        try
        {
            var data = 
                (from O in db.Orders
                 where O.OrderID == 1
                 select new Order
                 {
                     ID = O.OrderID,
                     OrderDetails = 
                         (from OD in db.OrderDetails
                          where OD.OrderID == O.OrderID
                          select new OrderDetail
                          {
                              ID = OD.OrderDetailID,
                              OrderID = OD.OrderID,
                              PullDate = OD.PullDate == "00000000" ?
                                  (DateTime?)null : db.yyyyMMddToDate(OD.PullDate),
                              Description = OD.Desc
                          })
                 });

            orders = data.AsQueryable();
            var orderList = orders.ToList();
        }
        catch (Exception ex)
        {                
            throw;
        }

        // NOTE: This code will NOT work
        try
        {
            var data = (from O in db.Orders
                        where O.OrderID == 1
                        select new Order
                        {
                            ID = O.OrderID,
                            OrderDetails = (from OD in OrderDetail.GetData(db)
                                            where OD.OrderID == O.OrderID
                                            select OD)
                        });

            orders = data.AsQueryable();
            var orderList = orders.ToList();
        }
        catch (Exception ex)
        {

            throw;
        }

        return orders;
    }
}


public class OrderDetail
{
    [Key]
    public int ID { get; set; }
    public int OrderID { get; set; }
    public DateTime? PullDate { get; set; }
    public string Description { get; set; }

    public static IQueryable<OrderDetail> GetData(OrderEntities db)
    {
        IQueryable<OrderDetail> orderDetails;

            var data = (from OD in db.OrderDetails
                        select new OrderDetail
                        {
                            ID = OD.OrderDetailID,
                            OrderID = OD.OrderID,
                            PullDate = OD.PullDate == "00000000" ?
                                (DateTime?)null : db.yyyyMMddToDate(OD.PullDate),
                            Description = OD.Desc
                        });

            orderDetails = data.AsQueryable();
            var orderList = orderDetails.ToList();

            return orderDetails;
    }
}

错误
LINQ to Entities 无法识别方法 'System.Linq.IQueryable`1[Models.OrderDetail] GetData(Models.OrderEntities)' 方法,并且该方法无法转换为存储表达式。

请求
我希望 Order.GetData 方法使用 LINQ 调用 OrderDetail.GetData 方法。
我需要在 Order.GetData 类中“加入”GetData 方法或“子选择”OrderDetail.GetData。
这两个类都在其 GetData 方法中查询 EntityFramework。
投影是一项要求。
我的目标是在我的 DTO 类中创建像“GetData”这样的“可重用”方法,这些方法将包含特定的 SQL/实体逻辑。
例如,我在我的 DTO 类中使用了很多自定义 SQL 函数,如“db.yyyyMMddToDate”,将实体转换为更对象/.NET 友好的东西,我不想每次都“重新输入”所有逻辑需要从实体中“加入/分选”数据。

在 LINQ to Objects 中,这与连接来自不同类的两个不同列表相同。
但似乎 LINQ to Entity 不知道如何从其他类中加入方法,即使该方法被标记为可查询。
我知道 LINQ to Entity 将“GetData”方法视为 SQL 函数,但我需要一种方法来告诉 LINQ to Entity 这些只是更多的实体,可以连接在一起以获得结果。

【问题讨论】:

    标签: c# linq entity-framework


    【解决方案1】:

    您收到此错误是因为在处理 LINQ to Entities 时,您没有像在 LINQ to Object 中那样将 lambdas 传递给 LINQ 运算符。您正在传递表达式树。您在这些查询中编写的代码将永远不会被执行。它将被实体框架解释为一系列指令,以翻译成其他语言,如 SQL。

    GetData 方法在 SQL 中没有任何意义。实际上,这并不意味着您自己的程序之外的任何内容。这就是 LINQ to Entities 无法将您的方法识别为有效构造的原因。

    要在像您这样的场景中访问订单的详细信息,您需要构建查询,以便 EF 知道使用 Include 运算符将 OrderDetails 实体包含在主查询中。执行此操作时,EF 希望您的对象具有与此类似的属性:

    public virtual ICollection<OrderDetail> OrderDetails { get; set; }
    

    这就是 EF 如何知道将包含这些元素的查询的结果对象放在哪里。

    var ctx = new DatabaseContext();
    var query = ctx.Orders
       .Where(o => o.OrderId == 1)
       .Include(o => o.OrderDetails);
    

    此查询将向远程服务器询问 OrderId 为 1 的所有订单及其所有匹配的 OrderDetails。然后,EF 会自动为您创建所有相关的客户端实体。

    这里是关于这个主题的 EF 官方文档:http://msdn.microsoft.com/en-ca/data/jj574232.aspx

    【讨论】:

    • “包含”与使用示例(1)中标记为“此代码将起作用”的子选择有何不同?我的示例简化了我遇到的问题,但通常我需要能够调用 OrderDetail.GetData 以便 LINQ 可以为整个 LINQ 表达式调用一次数据库。
    • Include 方法告诉 EF 在底层查询中生成一个连接子句。这将自动填充您应该在 Orders 对象中具有的名为 OrderDetails 的虚拟 ICollection 属性。您当前要做的是告诉 EF 在查询中生成对 GetData 的调用。这是一个 SQL 查询,它不会在您的应用程序的上下文中执行。它在一个完全不知道GetData 含义的潜在远程服务器上执行。因此,EF 无法将 GetData 调用翻译成任何有意义的内容。
    • 很抱歉,我看不出“包含”将如何帮助我调用 OrderDetails.GetData?所有“包含”要做的就是告诉 LINQ 从 OrderDetails 实体中“包含”数据。在这种情况下使用“包含”与在示例 1 中使用的 LINQ 表达式中使用“连接”没有什么不同。“包含”也不允许我将实体投影到 DTO 对象中。
    • 其实因为include子句,EF会自动填充我给你提到的导航属性,所以你甚至不用自己将OrderDetails投影到Order中。这是使用 EF 处理关系的基础。您不应该尝试使用自定义 GetData 方法自己执行此操作。 Include 可以为您做到这一点。
    • 我必须使用投影。例如,“Order”类有一个名为“ID”的属性,但“Order”实体没有 ID,而是有一个名为“OrderID”的字段映射到 Order 类中的“ID”。我的例子很乏味。在实际环境中,甚至没有一个名为“Order”的实体,但是在我的“Order”类中将几个实体连接在一起形成了一个 Order 模型。我将在问题中添加更多信息,以更好地帮助解释问题。
    猜你喜欢
    • 1970-01-01
    • 2017-03-25
    • 1970-01-01
    • 2012-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多