简体   繁体   中英

refactoring LINQ IQueryable expression to remove duplicated portions of queries

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

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.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM