40

This is not about the reuse of a result but more the statement itself. Nor is it about an error when using var as mentioned in: LINQ to SQL: Reuse lambda expression

Out of sheer curiosity I was wondering if it is possible to reuse a single LINQ statement.

Lets say I have the following LINQ statement:

.Where(x => x.Contains(""));

Is it possible to extract the statement x => x.Contains("") and use some kind of reference to this for later usage in, lets say, another class?

So I can call it like: .Where(previouslySavedStatement);

Community
  • 1
  • 1
Blaatz0r
  • 1,205
  • 1
  • 12
  • 24
  • 5
    put it in a variable. `Func func = x => x.contains("");` – M.kazem Akhgary Dec 17 '15 at 14:44
  • 3
    @wudzik This isn't a duplicate. The duplicate reference refers to re-using results, the question asks about re-using the query itself. – Shlomo Dec 17 '15 at 14:46
  • Thanks Shlomo :) just made the edit myself – Blaatz0r Dec 17 '15 at 14:46
  • 3
    @Shlomo sorry, missunderstood question, reopened – Kamil Budziewski Dec 17 '15 at 14:47
  • 3
    `Func previouslySavedStatement = x => x.Contains("")` or `Expression>` if the source is an `IQueryable`. – Lee Dec 17 '15 at 14:47
  • 2
    Possible duplicate of [LINQ to SQL: Reuse lambda expression](http://stackoverflow.com/questions/4562132/linq-to-sql-reuse-lambda-expression) – Robert McKee Dec 17 '15 at 16:51
  • @RobertMcKee I have looked at the link you posted but the problem there is between var / func<> it can be used to answer my question but in the base it is different. So no 'similairity' – Blaatz0r Dec 18 '15 at 06:39
  • @Blaatz0r The answer for that and this is the same. The first answer in the link I gave says that for LINQ over SQL (and LINQ over EF) is `Expression> lambda = x => x.Id > 1000;` and `Func lambda = x => x.Id > 1000;` for IEnumerables which is the same as the answer you marked correct. The "base" is the same. IEnumerables take lamdas/delegates, while IQueryables take expressions to build expression trees. – Robert McKee Dec 18 '15 at 15:59

4 Answers4

51

You can store it in a variable. If you are working with IQueryable then use:

System.Linq.Expressions.Expression<Func<Foo, bool>> selector = x => x.Contains("");

If you are using IEnumerable then use:

Func<Foo, bool> selector = x => x.Contains("");

And use it in your query:

query.Where(selector);
4444
  • 3,541
  • 10
  • 32
  • 43
Hamid Pourjam
  • 20,441
  • 9
  • 58
  • 74
  • 7
    FYI to @Blaatz0r If you are working with a `IEnumerable` instead of a `IQueryable` you would only use `Func selector = ...`, but if you are using `IQueryable` you most defiantly want to use the `Expression>` version. – Scott Chamberlain Dec 17 '15 at 14:51
  • 1
    @ScottChamberlain Most of my statements are IEnumerable so thanks for the heads up – Blaatz0r Dec 17 '15 at 14:52
  • 1
    @Blaatz0r if you have the expression version you can also convert it in to a delegate by doing `Func selector2 = selector.Compile();`, you can't go the other direction from a delegate to a expression. – Scott Chamberlain Dec 17 '15 at 14:54
7

Yes, you can write a function containing the query you want to reuse, which takes and returns an IQueryable<T>

   public IQueryable<T> ContainsEmpty(IQueryable<T> query)
   {
       return query.Where(x => x.Contains(""));
   }

Now you can reuse it:

   query1 = ContainsEmpty(query1);
   query2 = ContainsEmpty(another);
  • +1 This does not directly answer OP's question, but it's a much better way of solving his problem _(though, why did you choose `IQueryable` instead of `IEnumerable`?)_ – BlueRaja - Danny Pflughoeft Dec 17 '15 at 19:04
  • 1
    The datatype isn't specified in the question, but it does mention "LINQ to SQL" and has "query" in the title, so I guessed IQueryable :-) –  Dec 18 '15 at 09:24
5

It depends. There's two Where methods, Enumerable.Where and Queryable.Where. If you're applying the .Where to an IEnumerable than the first one is called, if you're applying it to an IQueryable the second one is called.

Since Enumerable.Where takes in a Func, it isn't reusable. Since Queryable.Where takes in an expression, it is reusable. You can do so as follows:

var x = new List<string>().AsQueryable();

var query = x.Where (n => n.Contains("some string"));

//Extract the lambda clause
var expr = query.Expression;
var methodExpr = (MethodCallExpression)expr;
var quoteExpr = (UnaryExpression)methodExpr.Arguments[1];
var funcExpr = (Expression<Func<string, bool>>)quoteExpr.Operand;

You can then later re-apply the where expression:

var query2 = x.Where(funcExpr);
Shlomo
  • 14,102
  • 3
  • 28
  • 43
  • 2
    Yes, I even read the accepted answer, and I even read the question too. I even believe my answer answers the question as even asked better than the accepted answer even did. – Shlomo Dec 17 '15 at 17:58
  • `var methodExpr = (MethodCallExpression)expr;` seems less clean than just `MethodCallExpression methodExpr = expr;` – Origin Apr 18 '20 at 12:17
2

I wrote a library to address exactly this concern, it's called CLinq and you can find an implementation for the EntityFramework here: https://www.nuget.org/packages/CLinq.EntityFramework

It allows to create query snippets and use them everywhere you in a linq query. Following the example of Hamid, create the following expression:

System.Linq.Expressions.Expression<Func<Foo, bool>> selector = x => x.Contains("");

You can now use this query everywhere in your linq queries like this:

query.AsComposable().Where(o => selector.Pass(o));

Additionally to this simple example you're also able to combine your query snippets:

query.AsComposable().Where(o => selector.Pass(o) || anotherSelector.Pass(o));

or even merge them together:

query.AsComposable().Where(o => anotherSelector.Pass(selector.Pass(o)));

There's some more features, but I think it's really helpful, so check it out :)

crazy_crank
  • 659
  • 4
  • 17