130

I have a lambda expression that I'd like to be able to pass around and reuse. Here's the code:

public List<IJob> getJobs(/* i want to pass the lambda expr in here */) {
  using (SqlConnection connection = new SqlConnection(getConnectionString())) {
    connection.Open();
    return connection.Query<FullTimeJob, Student, FullTimeJob>(sql, 
      (job, student) => {         
        job.Student = student;
        job.StudentId = student.Id;
        return job;
        },
        splitOn: "user_id",
        param: parameters).ToList<IJob>();   
  }   

The key here, is I want to be able to pass the lambda expression that I'm using here into the method that's calling this code, so I can reuse it. The lambda expression is the second argument inside my .Query method. I'm assuming I'd want to use an Action or Func, but I'm not quite sure what the syntax is for this or how it quite works. Can someone please give me an example?

Adam Levitt
  • 10,316
  • 26
  • 84
  • 145

4 Answers4

146

Use a Func<T1, T2, TResult> delegate as the parameter type and pass it in to your Query:

public List<IJob> getJobs(Func<FullTimeJob, Student, FullTimeJob> lambda)
{
  using (SqlConnection connection = new SqlConnection(getConnectionString())) {
    connection.Open();
    return connection.Query<FullTimeJob, Student, FullTimeJob>(sql, 
        lambda,
        splitOn: "user_id",
        param: parameters).ToList<IJob>();   
  }  
}

You would call it:

getJobs((job, student) => {         
        job.Student = student;
        job.StudentId = student.Id;
        return job;
        });

Or assign the lambda to a variable and pass it in.

Oded
  • 489,969
  • 99
  • 883
  • 1,009
  • This looks very good, and how would I define the lambda OUTSIDE of this getJobs method? In other words, what's the line before the call to getJobs() look like to define the lambda? – Adam Levitt Jan 12 '13 at 20:30
  • @AdamLevitt - Same way you have with your example code. Will add to answer. – Oded Jan 12 '13 at 20:30
  • Also, can the function parameters be dynamic in anyway? – Adam Levitt Jan 12 '13 at 20:32
  • @AdamLevitt - you can make the function generic, though if you want different numbers of parameters on the lambdas, you will need overloads. – Oded Jan 12 '13 at 20:35
  • Right. What I'd actually like is to be able to pass in different implementations of the IJob interface, but that won't work with Dapper's Query<> because it requires the actual generic impl class at run time. This is still pretty close to what I was hoping for. – Adam Levitt Jan 12 '13 at 20:39
35

If I understand you need following code. (passing expression lambda by parameter) The Method

public static void Method(Expression<Func<int, bool>> predicate) { 
    int[] number={1,2,3,4,5,6,7,8,9,10};
    var newList = from x in number
                  .Where(predicate.Compile()) //here compile your clausuly
                  select x;
                newList.ToList();//return a new list
    }

Calling method

Method(v => v.Equals(1));

You can do the same in their class, see this is example.

public string Name {get;set;}

public static List<Class> GetList(Expression<Func<Class, bool>> predicate)
    {
        List<Class> c = new List<Class>();
        c.Add(new Class("name1"));
        c.Add(new Class("name2"));

        var f = from g in c.
                Where (predicate.Compile())
                select g;
        f.ToList();

       return f;
    }

Calling method

Class.GetList(c=>c.Name=="yourname");

I hope this is useful

Marinpietri
  • 112
  • 3
  • 8
  • 3
    Can you explain why we need the `Compile()` in the `.Where`? I have seen it works without that too. – Sнаđошƒаӽ Jan 16 '17 at 09:41
  • I think it is unnecessary. I think it should just be `predicate`. The `Compile()` would compile it to a `Func<>` and force it to loop through the entire list, row by row which is probably not what we want. ( see [these answers](https://stackoverflow.com/q/793571) ) for explanations. – kkuilla Sep 14 '20 at 11:41
11

Lambda expressions have a type of Action<parameters> (in case they don't return a value) or Func<parameters,return> (in case they have a return value). In your case you have two input parameters, and you need to return a value, so you should use:

Func<FullTimeJob, Student, FullTimeJob>
SztupY
  • 10,291
  • 8
  • 64
  • 87
6

You should use a delegate type and specify that as your command parameter. You could use one of the built in delegate types - Action and Func.

In your case, it looks like your delegate takes two parameters, and returns a result, so you could use Func:

List<IJob> GetJobs(Func<FullTimeJob, Student, FullTimeJob> projection)

You could then call your GetJobs method passing in a delegate instance. This could be a method which matches that signature, an anonymous delegate, or a lambda expression.

P.S. You should use PascalCase for method names - GetJobs, not getJobs.

devdigital
  • 34,151
  • 9
  • 98
  • 120