7

我有一些冗余的 linq 查询我想分解出一段代码。这些是 IQueryable 的连接 experssions,重要的是我不会使查询在没有重构的情况下被更早地评估。

这是一个简化的查询:

var result = 
from T in db.Transactions
join O in db.Orders on T.OrderID equals O.OrderID
join OD in db.OrderDetails on O.OrderID equals OD.OrderID into OrderDetails
let FirstProductBought = OrderDetails.First().Select(OD => OD.Product.ProductName)
select new
{
  TransactionID = T.TransactionID,
  OrderID = O.OrderID,
  FirstProductBought = FirstProductBought
};

我要考虑的是“给定订单,购买的第一个产品是什么”的逻辑。我在其他查询中使用相同的逻辑。如何将其分解为共享方法?

通常,对于代码重用和 IQueryables,我能够做的是接受 IQueryable 并生成 IQueryable/IOrderedQueryable 作为输出的代码。使用这些函数,我可以使用可重用代码构建 LINQ 表达式,这些代码仍然推迟查询,直到查询完全构造。在这里,因为我只有一个 int(orderID),所以我不确定如何使它工作。

谢谢

4

3 回答 3

4

很抱歉回答我自己的问题,但我找到了一个很好的解决方案。我认为,尽管根据您要执行的操作,有不同的方法可以在不评估 IQueryable 的情况下分解出不同的 LINQ 表达式。所以我希望人们分享替代解决方案。

我的解决方案是为分解出的查询创建一个“视图”。我称它为视图是因为它与 SQL 视图有很多共同点(从 LINQ 客户端的角度来看)。但与 SQL 视图不同的是,它不能被索引或保留列。所以使用这个视图成为一个瓶颈,使用一个实际的 SQL 视图是合适的。

static public class MyDataContextExtension
{
    // The view exposes OrderSummary objects
    public class OrderSummary
    {
        public OrderID { get; set; }
        public string FirstProductListed { get; set; }
    }

    static public IQueryable<OrderSummary> OrderySummaryView(this MyDataContext db)
    {
         return (
              from O in db.Orders
              join OD in db.OrderDetails on O.OrderID equals OD.OrderID into OrderDetails
              let AProductBought = OrderDetails.First().Select(OD => OD.Product.ProductName)
              let TotalCost = OrderDetails.Aggregate(0
              select new OrderSummary()
              {
                  OrderID = OD.OrderID,
                  FirstProductListed = AProductBought.FirstOrDefault()
              };
    }
}

有了这个,我可以排除查询的重复部分,将原始查询替换为以下内容:

var result = 
from T in db.Transactions
join OS in db.OrderSummaryView() on T.OrderID equals OS.OrderID
select new
{
  TransactionID = T.TransactionID,
  OrderID = T.OrderID,
  FirstProductBought = OS.FirstProductListed
};

您可以想象添加了其他列...我认为一件很酷的事情是,如果您添加额外的列但不在最终选择中使用它们,LINQ 实际上不会从数据库中查询这些内容。

于 2009-04-21T04:20:10.893 回答
1

我们遇到了同样的问题。它不支持开箱即用,它是 LOB 应用程序的主要问题。我最终写了一篇关于 LINQ 表达式重用的代码项目文章,其中包括一个名为 LinqExpressionPrjection 的非常小的实用程序,它可以在投影中重用(包括匿名类型)。

在这里找到文章。

您可以将用于投影重用的程序集作为nuget 包获取,源代码位于CodePlex上。

你的帖子已经过去了一段时间。我希望它仍然对你有帮助。如果没有,也许对于其他阅读此主题的人。

于 2012-06-15T08:56:25.873 回答
0

分解 linq 表达式的另一个重要方法是传递表达式,例如:

X GetSomeX(Expression<Func<Y, X>> map)
{
    return SourceOfYs.Select(map);
}

我通过查看 Barak 的文章获得了这个想法——尽管他在这个主题上做了更多的工作,但我想我在这里再次提到了这篇文章。直接指出似乎是显而易见的第一件事。

于 2013-07-03T19:17:00.863 回答