代码之家  ›  专栏  ›  技术社区  ›  Frank Schwieterman

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

  •  7
  • Frank Schwieterman  · 技术社区  · 15 年前

    我有一些具有冗余的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
    };
    

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

    一般来说,对于代码重用和IQueryable,我所能做的是接收IQueryable并生成IQueryable/IOrderedQueryable作为输出的代码。有了这些函数,我可以用可重用的代码构建linq表达式,这些代码仍然延迟查询,直到查询完全构造完成。这里,因为我只有一个int(orderid),所以我不知道如何使它工作。

    谢谢

    3 回复  |  直到 11 年前
        1
  •  4
  •   Frank Schwieterman    15 年前

    很抱歉回答我自己的问题,但我找到了一个很好的解决办法。我认为,尽管这取决于您要做什么,但是有不同的方法可以在不计算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实际上不会从数据库中查询这些列。

        2
  •  1
  •   Asher Barak    12 年前

    我们也有同样的问题。它不支持开箱即用,这是lob应用程序的一个主要问题。最后我写了一篇关于linq表达式重用的代码项目文章,其中包括一个非常小的实用程序linqexpressionprjection,它支持在投影中重用(包括匿名类型)。

    找到文章 here .

    您可以将用于投影重用的部件作为 nuget package 消息来源是 CodePlex .

    你的职位已经过了一段时间。我希望这对你还是有帮助的。如果不是的话,也许对其他阅读这篇文章的人来说。

        3
  •  0
  •   John    11 年前

    将linq表达式作为因子的另一个重要方法是传递表达式,例如:

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

    我是从巴拉克的文章中得到这个想法的,尽管他在这个话题上做的更多,我想我还是在这里再次提到这篇文章。直接指出这一点似乎是显而易见的第一件事。