重构LINQ IQueryable表达式以删除重复的查询部分

时间:2009-04-20 17:47:11

标签: .net linq linq-to-sql iqueryable

我有一些具有冗余的linq查询我想要分解一段代码。这些是IQueryable的连接表达式,重要的是我不会比没有重构时更早地评估查询。

这是一个简化的查询:

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),我不知道如何使它工作。

感谢

3 个答案:

答案 0 :(得分:4)

很抱歉回答我自己的问题,但我找到了一个很好的解决方案。我认为,根据你想要做的事情,有不同的方法来分解不同的LINQ表达式而不评估IQueryable。所以我希望人们分享替代解决方案。

我的解决方案是为分解的查询创建一个“视图”。我将其称为视图,因为它与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实际上不会从数据库中查询这些内容。 / p>

答案 1 :(得分:1)

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

找到文章here

您可以将投影重新使用的汇编作为nuget package,并且源位于CodePlex

自您发布以来已经过了一段时间。我希望它对你有用。如果没有,也许是其他人阅读这个帖子。

答案 2 :(得分:0)

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

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

我通过查看巴拉克文章的文章得到了这个想法 - 尽管他在这个主题上做了更多,但我想我在这里再次提到这篇文章。直接指出这似乎是首要的事情。