views:

165

answers:

4

Suppose I have the two following Linq queries I want to refactor:

var someValue1 = 0;
var someValue2= 0;
var query1 = db.TableAs.Where( a => a.TableBs.Count() > someValue1 )
                  .Take( 10 );
var query2 = db.TableAs.Where( a => a.TableBs.First().item1 == someValue2)
                  .Take( 10 );

Note that only the Where parameter changes. There is any way to put the query inside a method and pass the Where parameter as an argument?

+1  A: 

Yeah, the parameter type is a lamba expression Func<TSource, bool>

Func<Person, bool> c1 = p => p.LastName == "1";
Persons.Where(c1);
Func<Person, bool> c2 = p => p.FirstName == "2";
Persons.Where(c2)
Kai Wang
This isn't working: Unsupported overload used for query operator 'Where'.
Jader Dias
Have you actually tried this?
Jader Dias
Yes, I compiled and ran before posting here.
Kai Wang
Maybe it isn't working for me because I am using LinqToSql.
Jader Dias
All right, I know why. I didn't realize this is for Linq to Sql.
Kai Wang
If you know why please post the answer in the new question : http://stackoverflow.com/questions/865350/how-to-refactor-multiple-similar-linq-to-sql-queries
Jader Dias
+2  A: 

Of couse there is. The where parameter is just a simple closure of type Func<T, bool> (where T is the type of your DB items - I don't know them out of your code) and you can wrap it into a (anonymous) function.

Func<Func<T, bool>, IEnumerable<T>> MakeQuery = (Func<T, bool> whereParam) => db.TableAs.Where(whereParam).Take(10);

Use it like this

var query1 = MakeQuery(a => a.TableBS.Count() > someValue1);
Dario
+1  A: 

You can do it the same way you'd refactor any other code:

T MyQuery(U db, Func<TSource, bool> pred) {
  return db.TableAs.Where( pred )
                  .Take( 10 );
}

where T and U are whatever types ar erelevant in your query.

jalf
I was not used to lambda expressions, so I didn't realized it was so obvious.
Jader Dias
A lambda expression is nothing more than syntactic sugar for the Func<> delegate with the appropriate generic types inserted. There's no magic. :)
jalf
This solution isn't working: Unsupported overload used for query operator 'Where'.
Jader Dias
Maybe it isn't working for me because I am using LinqToSql
Jader Dias
Most likely you're using the wrong types then. The first generic parameter for the predicate has to be the element type of the enumerable/queriable it works on. Hard to say exactly what the problem is without seeing some more code (or at least which types we're working with)
jalf
Ah yeah, that's most likely the problem. LinqToSql is a lot more picky because it has to be able to compile the predicate function into SQL.
jalf
+2  A: 

You can using Predicate<T>.

public IQueryable<TableA> Helper(Predicate<TableA> predicate)
{
    return db.TableAs.Where(predicate).Take(10);
}

The just call it like that.

var query1 = Helper(a => a.TableBs.Count() > someValue1);
var query2 = Helper(a => a.TableBs.First().item1 == someValue2);

And give a better namen than Helper.

Daniel Brückner
Kind of off-topic, but shouldn't that method maybe be either private or static or both?
Svish
It depends - it might be a public method of a helper class or a private method of the class that uses it. It might be static, but one might also decide to keep it non-static for better testability. I would not try to guess the best way based on this few lines of code.
Daniel Brückner
Just a correction, Where accepts Func<T,TResult> and not Predicate<T>, but I got the idea.
Jader Dias