2

I have a lambda expression that is passed to a First() or FirstOrDefault() call.

I would like to dynamically inject a parameter value into the lambda when it is executed.

This is the hacked code I have now; it "works" in the sense that it runs.

ObservableCollection<T> Rows { get; set; }

T currentRow = Rows[0];
Template r = (Template)(object)currentRow;

Func<T, bool> p = e => ((Template)(object)e).TemplateId.Equals(r.TemplateId);

var firstRow = Rows.First(p);

I would like something that handles the generic T properly

public class Model<T>
{
    public ObservableCollection<T> Rows { get; set; } = {add rows here}
    
    public Func<T, T, bool> Lambda { get; set; }
    
    public T CompareRow {get; set;} // assume not null
    public T SelectedRow { get; set; }

    public GetRow()
    {
       T currentRow = CompareRow; 

       // First extension takes a lambda expression of Func<T,bool>
       // SOMEHOW inject the runtime value of currentRow into lambda
       // to convert Func<T, T, bool> to Func<T, bool>

       var firstRow = Rows.First(Lambda);  // get first row that matches Lamda

       SelectedRow = firstRow;
    }
} 

public class MyModel: Model<Entity>
{
    public void MyModel()  : base()
    {
       // define the lambda expression with the concrete type of <Entity>
       // each <Entity> type has different fields;
       // so want to define a Lambda in the concrete class to validate the fields,
       // but one that can be used in the generic base class.

       Func<Entity, Entity, bool> p = (e,r) => e.TemplateId.Equals(r.TemplateId);
       Lambda = p;
    }

    public SetRow()  // called from somewhere
    {
       CompareRow = Rows.Last();  // assume Rows is not empty
    }

    public GetRow()
    {
        base.GetRow();
    }
}

I have found these...

[https://stackoverflow.com/questions/16985310/convert-expressionfunct-t-bool-to-expressionfunct-bool] (this has extra code in it... so can this be improved on?)

[https://stackoverflow.com/questions/21922214/create-dynamic-linq-expression-for-select-with-firstordefault-inside] (this is specific to creating a "select" lambda).

[https://www.codementor.io/@juliandambrosio/how-to-use-expression-trees-to-build-dynamic-queries-c-xyk1l2l82]

[https://stackoverflow.com/questions/63172233/dynamic-firstordefault-predicate-expression]

Also: if there is a different way to call

var firstRow = Rows.First(Lambda);

that is straight forward, open to suggestions.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
lkujala
  • 111
  • 9

1 Answers1

1

You can call your Lambda function this way.

public GetRow()
{
   T currentRow = CompareRow; 
   var firstRow = Rows.First(row => Lambda(row, CompareRow));  // get first row that matches Lambda

   SelectedRow = firstRow;
}

Here is another example using string parameters:

List<string> names = new() { "Alice", "Bob", "Charlie" };
string nameToMatch = "Alice";
Func<string, string, bool> Lambda = (left, right) => left.GetHashCode() == right.GetHashCode();
var alice = names.First(name => Lambda(name, nameToMatch));
Console.WriteLine($"Hi {alice}");

You may have some issues setting Lambda. The type looks wrong Func<Entity, Entity, bool> is not Func<T, T, bool> as there are no constraints on what type T is.

You may want to consider adding a constraint on T, maybe something like this:

public class Model<T>
   where T : Entity
{
    public Func<Entity, Entity, bool> Lambda { get; set; }
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Steve Wong
  • 2,038
  • 16
  • 23
  • 1
    1 - yes I actually am using the where clause; I didnt include in the example code. 2 your first suggestion seems to work... testing it out now.... (definitely much cleaner code than some of the other solutions I was reading up on.) – lkujala Jan 20 '22 at 23:08