refactoring LINQ IQueryable expression to remove duplicated portions of queries

半城伤御伤魂 提交于 2019-12-20 23:25:18

问题


I have some linq queries that have redundancy I'd like to factor out a single piece of code. These are join experssions that are IQueryable, and its important I don't cause the query to be evaluated earlier than it would be without the refactoring.

Here is a simplified query:

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
};

What I want to factor out is th logic "given an order, what is the first product bought". I am using the same logic in other queries. How can I factor it out into a shared method?

Generally, for code reuse and IQueryables, what I've been able to do is code that takes an IQueryable going in and produces an IQueryable/IOrderedQueryable as output. With such functions I can build up LINQ expressions with reusable code that still defer query until the query is fully constructed. Here, since I only have an int (the orderID) I'm not sure how to make it work.

thanks


回答1:


Sorry to answer my own question, but I found a good solution. I think though that depending on what you're trying to do, there are different way to factor out different LINQ expressions without evaluating the IQueryable. So I hope people share alternative solutions.

My solution was to create a "view" for the factored out query. I call it a view because it has a lot in common with a SQL view (from the perspective of a LINQ client). Unlike a SQL view though, it cannot be indexed or have columns persisted. So using this view becomes a bottleneck, it would be appropriate to use an actual SQL view.

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()
              };
    }
}

With this, I can factor out the duplicated portion of the query, replacing the original query with the following:

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
};

You can imagine other columns being added... I think one cool thing is that if you add extra columns but don't use them in your final select, LINQ won't actually query for those things from the database.




回答2:


We had the same problem. It is not supported out of the box and it is a major problem for LOB applications. I ended up writing a code-project article about LINQ expressions reuse, including a very small utility called LinqExpressionPrjection that enables the reuse in projections (including into anonymous types).

Find the article here.

You can get the assembly for the projection reuse as a nuget package and the source is on CodePlex.

Some time has passed since your post. I hope it is still helpful for you. If not, maybe for others reading this thread.




回答3:


Another important way to factor linq expressions is to pass expressions around, for example:

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

I got the idea by looking into the article of Barak's article - and even though he does a bit more on that topic, I thought I mention this piece again here. It seems to be an obvious first thing to point out directly.



来源:https://stackoverflow.com/questions/769351/refactoring-linq-iqueryable-expression-to-remove-duplicated-portions-of-queries

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!