0

I need to pass in a "where" lambda expression that'll be used in a LINQ query inside a method. The problem is, I don't know what the where value will be compared against until I get into the method.

Now to explain further and clarify some of what I said above I'll come up with a bit of a contrived example.

Imagine I have a List<Products> and I need to narrow that list down to a single record using a productId property of the Products object. Normally I would do this:

var product = productList.Where(p=>p.productId == 123).FirstOrDefault();

Now take it a step further - I need to put the above logic into a method that isn't limited to a List<Products> but is instead a List<T> so ideally, I'd be calling it like this (and I know the below won't work, it's simply here to show what I am trying to achieve):

myMethod(productList, p => p.productId == X)

With the caveat being that X isn't known until I'm inside the method.

Finally, for what it's worth, I need to point out that my collection of data is an OData DataServiceQuery.

So, to re-summarize my question: I need to know how to construct a lambda "where" expression that I can pass into a method and how to use it against a collection of objects in a LINQ query.

Cœur
  • 37,241
  • 25
  • 195
  • 267
bugfixr
  • 7,997
  • 18
  • 91
  • 144
  • T always has productId property? – sll Aug 12 '11 at 21:14
  • No, productId won't always be the property. Sometimes I may have a list of Users, so it would be UserId, or perhaps a companyId for a set of Company objects. – bugfixr Aug 12 '11 at 22:07

3 Answers3

2

myMethod(productList, p => p.productId == X) - you can emulate with this trick

    static void myMethod<T>(List<T> list, Func<T,bool> predicate, ref int x)
    {
        x = 5;
        var v = list.Where(predicate);
        foreach (var i in v)
            Console.Write(i);
        Console.ReadLine();
    }

    static void Main(string[] args)
    {
        List<int> x = new List<int> { 1, 2, 3, 4, 5 };
        int z = 0;
        myMethod(x, p => p == z, ref z);
    }

but not sure if it solves your problem in whole

elevener
  • 1,097
  • 7
  • 20
2

For one, if you are going to query an IEnumerable<T>, you will need to ensure that your comparison will work in the first place. In that case you can make your objects implement an interface that guarantees that they will support the comparison.

Once you do that, your method can have a generic constraint that limits the input to those interfaces. At that point, your method can take a Func, which can be passed to the LINQ Where clause:

public interface Identifier
{
   int Id { get; set; }
}

public class Product : Identifier
{
   public int Id { get; set; }
   //Other stuff
}

public T GetMatch<T>(IEnumerable<T> collection, Func<T, int, bool> predicate) where T : Identifier
{
   int comparison = 5;
   return collection.Where(item => predicate(item, comparison)).FirstOrDefault();
}

Which can be invoked like:

var match = GetMatch<Identifier>(collection, (x, y) => x.Id == y);

UPDATE: I modified the above code to take in a comparison parameter

davecoulter
  • 1,806
  • 13
  • 15
  • Thanks Dave - the main problem is though that I don't know the value of 5 until the method is executed. – bugfixr Aug 12 '11 at 22:11
  • @Chu -- is the value (5 in the example provided), something that is going to passed in as a parameter to the GetSubcollection method? How does the value get inside the function scope? – davecoulter Aug 12 '11 at 22:24
  • @Chu -- I added a parameter above to show how a comparison value could be added to the Func<> – davecoulter Aug 12 '11 at 22:30
  • The GetSubCollection method actually determines the value to use in the comparison. – bugfixr Aug 12 '11 at 22:39
  • @Chu -- In the last edit, I included the arbitrary comparison in the body of the GetMatch method... in that way, the method determines the comparison value, but the invocation of the method determines how the comparison is made. – davecoulter Aug 12 '11 at 23:09
0

You could try to use the PredicateBuilder class from the free LinqKit library(tutorial).

You can then construct a predicate using

PredicateBuilder predicate = PredicateBuilder.True<T>();
predicate = PredicateBuilder.And(predicate, p=> p.product_id == X);

where X is of type T.

You can use this predicate in a where clause such as .Where(predicate) and return an IQueryable or return the predicate itself which would be of type Expression<Func<T, bool>>

arviman
  • 5,087
  • 41
  • 48