31

Firstly, I am not sure what terms to use to ask this question, which is probably why I have not found an answer from searching myself.

So I am working with Linq to SQL (C#, .Net 4) and I want to get a list of all users that match a criteria, the basics of which I would do something like this:

var users = DataContext.Users.Where(x => x.Criteria1 == "something");

but in this case there are a few fields I want to match, the thing is these particular fields are a common check and I would like to be able to create a dedicating function that I can use within any of my user queries to check for this match.

To try and explain that a bit better lets give an example: Lets say a user has 5 flags, and I want a common check to see if any of those flags are set. So I could write my query like so:

var users = DataContext.Users.Where(x => x.Flag1 || x.Flag2 || x.Flag3 || x.Flag4 || x.Flag5);

But what I would like to do is seperate out that "5 flag check" so I can use it in other queries too, ultimately I would like to use something like:

var users = DataContext.Users.Where(x => x.Criteria1 == "something" && CheckForFlags(x));

I have tried this by having a function like this:

static bool CheckForFlags(User user)
{
   return user.Flag1 || user.Flag2 || user.Flag3 || user.Flag4 || user.Flag5;
}

but I get an error:

"Method 'Boolean CheckForFlags(User)' has no supported translation to SQL."

...which makes sense, but it there something I can do to make this work the way I want it to? Or is this a restriction because I am using Linq to SQL and is in fact something that would work with Linq to Objects?

Mario S
  • 11,715
  • 24
  • 39
  • 47
musefan
  • 47,875
  • 21
  • 135
  • 185
  • 1
    This is definitely a restriction of LINQ-to-SQL, LINQ-to-objects allows arbitrary predicates. The question is, of course, what would be a good workaround for the SQL case. – Vlad Nov 01 '12 at 16:02
  • You can use dynamic linq to build your expression like an sql string. You can also convert your method to an expression tree but it is much more complicated. – Amiram Korach Nov 01 '12 at 16:03
  • @Vlad: You are correct. I am not so concerned about how to use my attempted function, but more how to get the same end result – musefan Nov 01 '12 at 16:06
  • This post will help bit, http://stackoverflow.com/questions/9606979/string-isnullorwhitespace-in-linq-expression – Prasad Kanaparthi Nov 01 '12 at 16:28

4 Answers4

57

The neat thing about how LINQ to SQL handles expressions is that you can actually build out expressions elsewhere in your code and reference them in your queries. Why don't you try something like this:

public static class Predicates
{
    public static Expression<Func<User, bool>> CheckForFlags()
    {
        return (user => user.Flag1 || user.Flag2 || user.Flag3 ||
                        user.Flag4 || user.Flag5);
    }

    public static Expression<Func<User, bool>> CheckForCriteria(string value)
    {
        return (user => user.Criteria1 == value);
    }
}

Once you have your predicates defined, it's very easy to use them in a query.

var users = DataContext.Users
    .Where(Predicates.CheckForFlags())
    .Where(Predicates.CheckForCriteria("something"));
Adam Maras
  • 26,269
  • 6
  • 65
  • 91
4

Have you tried PredicateBuilder? I haven't used it in over a year, but I found it effective when writing "Or Where" Queries.

http://www.albahari.com/nutshell/predicatebuilder.aspx

An example from their page:

IQueryable<Product> SearchProducts (params string[] keywords)
{
  var predicate = PredicateBuilder.False<Product>();

  foreach (string keyword in keywords)
  {
    string temp = keyword;
    predicate = predicate.Or (p => p.Description.Contains (temp));
  }
  return dataContext.Products.Where (predicate);
}
JoshVarty
  • 9,066
  • 4
  • 52
  • 80
  • 1
    +1: Combining _Expressions_ instead of _predicates_ seems to be the way to go. – Vlad Nov 01 '12 at 16:16
  • Looks interesting.. unfortunately, for me anyway, I cannot use such 3rd party libraries in this particular project.. of course, I wont consider "my project limitations" when I come to choosing an accepted answer though – musefan Nov 01 '12 at 16:20
  • @musefan: look it up, the code is really trivial, so you can do redevelop it yourself. Actually, Adam's answer is doing the same thing. – Vlad Nov 01 '12 at 16:21
0

I think this will work, and I think I've used it in my Linq-to-SQL project, but I can't find an example offhand. If it doesn't, let me know.


Rather than creating a function, create a new property on the Users object:

partial class Users {
   bool CheckForFlags
    {
       get { 
          return Flag1 || Flag2 || Flag3 || Flag4 || Flag5;
       }
    }
}

Then you should be able to do

var users = DataContext.Users.Where(x => x.CheckForFlags);
Bobson
  • 13,498
  • 5
  • 55
  • 80
  • I fear this would require me to change the auto-generated dbml classes - which is to much of a pain to be doing and maintaining – musefan Nov 01 '12 at 16:13
  • That's specifically why they're created `partial`. Just declare a new `partial class Users{}` in a separate file in the same project and put this in there. – Bobson Nov 01 '12 at 16:14
  • @Vlad, this is also my concern which is making me hesitant to try it. It would be ideal with the partial class to be able to do this though if it did translate back to SQL – musefan Nov 01 '12 at 16:16
  • @Vlad - To be honest, that's why I posted the qualifiers. I'm pretty sure I've done it, but I have no idea how it actually works. I'd say it's worth a try, at least. Easy to remove afterwards if it isn't useful. – Bobson Nov 01 '12 at 16:18
  • @Bobson: may be it's good idea to produce an expression instead? `public partial class Users { public static Expression> CheckForFlags() { return p => p.Flag1 && p.Flag2 && ...; } }` (the idea stolen from [here](http://www.albahari.com/nutshell/predicatebuilder.aspx)) – Vlad Nov 01 '12 at 16:19
  • @Vlad - Certainly possible. I'd love to try some performance comparisons, but I don't think I'm going to get to that today. – Bobson Nov 01 '12 at 16:24
  • @Bobson: the LINQ-to-SQL will anyway compile the expression into SQL code, so there should be no performance hit. – Vlad Nov 01 '12 at 16:24
  • @Vlad - I meant between the simple property and the expression. At best, they'll be the same. I wouldn't be surprised if the expression is faster, but if they are the same, then the simple property is easier to use. – Bobson Nov 01 '12 at 16:27
0

To the best of my knowledge there are two possible ways to do this.

The quick-n-easy way to is filter your results after the SQL executes, with something like this:

var users = DataContext.Users.Where(x => x.Criteria1 == "something");
    .ToEnumerable()
    .Where(x => CheckForFlags(x));

However this is very poor in terms of performance. It will return ALL rows from the database matching only the first criteria, and then filter the results in memory on the client. Functional, but far from optimal.

The second, much more performant option is to create a UDF on the database itself and call it from LINQ. See for example this question. The obvious downside is that it moves code into the database, which no one likes to do (for lots of valid reasons).

There may very well be other viable solutions, but those are the only two I know of.

Community
  • 1
  • 1
ean5533
  • 8,884
  • 3
  • 40
  • 64
  • Yes I suppose the UDF is an option, but then I think we are just shifting the "effort" to a different place... I would rather the type the "5 flag check" out each time (and try to remember to do them all if the requirement changes of course) – musefan Nov 01 '12 at 16:11
  • 1
    perhaps `.ToEnumerable()` would be slightly better than `.ToList()` – Vlad Nov 01 '12 at 16:11
  • @Vlad Agreed. I'll change it, though hopefully I've sufficiently discouraged the asker from using that solution. – ean5533 Nov 01 '12 at 16:12