TruthOf42 TruthOf42 - 3 months ago 24
C# Question

C# Pass lambda expression field into method and use field in linq query

How can I pass a field (via lambda expression) into a method and then use that field as part of a linq query?

I would like to call the method something like

IDictionary<string, string> stuff = foo(items, otherItems, otherItems => otherItems.FieldToUse)


I'm not sure how I would write the method, but I would want to use it sort of like the code below. I know I could use generics and pass the field name (via a string) into the method, but even then I don't know how I would use it in the linq query as follows. Also, I like using lambda, since I could just rename the field anytime I choose.

private IDictionary<string, string> foo<TModel>(IEnumerable<string> items, IEnumerable<TModel> otherItems, object FieldToUse)
{
//this will return a list of key value pairs of rowIDs and equipment
IDictionary<string, string> x = (from o in otherItems
join i in items on o.FieldToUse.ToString() equals i //joining on the equipment assetcode
select new { rowID = o.RowID, item = i }).ToDictionary(k => k.rowID.ToString(), v => v.item);
return x;
}


Clarification: FieldToUse is a property or field of TModel

Answer

Use a Func Delegate

Change the last parameter in method foo to

Func<TModel, String> FieldToUse

and in the LINQ query call the function

FieldToUse(o)

Here is the entire method foo

private IDictionary<string, string> foo<TModel>(IEnumerable<string> items,
  IEnumerable<TModel> otherItems,
  Func<TModel, String> FieldToUse)
{
  //this will return a list of key value pairs of rowIDs and equipment
  IDictionary<string, string> x = (from o in otherItems
                                   join i in items on FieldToUse(o) equals i //joining on the equipment assetcode
                                   select new { rowID = o.RowID, item = i })
                                   .ToDictionary(k => k.rowID.ToString(), v => v.item);
  return x;
}

This is how you can use it

public void DoStuff()
{
  string[] items = { "abc", "def", "ghi" };
  List<Model> otherItems = new List<Model> { 
        new Model() { Field1 = "abc", Field2 = "xyz" }, 
        new Model() { Field1 = "abc", Field2 = "xyz" } };

  var result = foo<Model>(items, otherItems, a => a.Field2);
}

class Model 
{
  public string Field1 { get; set; }
  public string Field2 { get; set; }
}

You will have another problem though. The generic TModel does not have RowID. Perhaps provide a generic where constraint for TModel.

The code then becomes

 private IDictionary<string, string> foo<TModel>(IEnumerable<string> items,
  IEnumerable<TModel> otherItems,
  Func<TModel, String> FieldToUse) where TModel : BaseModel
{
  //this will return a list of key value pairs of rowIDs and equipment
  IDictionary<string, string> x = (from o in otherItems
                                   join i in items on FieldToUse(o) equals i //joining on the equipment assetcode
                                   select new { rowID = o.RowID, item = i })
                                   .ToDictionary(k => k.rowID.ToString(), v => v.item);
  return x;
}

class BaseModel
{
  public int RowID { get; set; }
}
class Model : BaseModel
{
  public string Field1 { get; set; }
  public string Field2 { get; set; }
}