Home > front end >  Dynamic property Linq
Dynamic property Linq

Time:03-18

I have a list of objects. I have a list of filter values. I need a count of the list of objects where a field/property (specified at run-time via variable) has a value found in the filter list. BTW there are other "filtered" properties as well but these property names are "fixed" and therefore are known at build-time.

Let's say the filter list has the following values:

Orange, Yellow, Blue

Let's say the object list has the following properties/values:

Name: Bike, Color: Red |
Name: Car, Color: Green |
Name: Box, Color: Yellow |
Name: Door, Color: Orange |

The "filter field" is string variable defined at run-time to be the "color" property/field.

The result should be 2 (box and door) because the other objects in the list do not have a "color" that is found in the filter list.

Here is what I have and it works but I am looking for a way to dynamically specify the "filter field" property name ("Field1") at run-time.

total = (from objects in ObjectsObservableCollection where ((object.InCity && cityList.Select(ma => ma.CityText).Contains(objects.CityLabel)) && FilterValuesList.Select(ff => ff.ToString().ToUpper()).Contains(objects.**Field1**.ToString().ToUpper())) select objects.ID).Count();

I could use a switch-case (with a select statement for each property) to determine which property is being used as the "filter field" at run-time (because all of the property names are known at run-time) but I really want to understand how to do it "the right way".

I think Expressions is the right way to go here but I can't wrap my head around the syntax. If it was 1 value I was searching (instead of a list of values), I think I could do it.

Can anyone give me a hint, maybe point to an article or tutorial or perhaps explain what I need to use to accomplish this?

CodePudding user response:

If you already have a fixed set of fields you will want to test, and assuming they are all string fields, you can create a Dictionary of accessor lambdas that can be looked up by field name:

var FilterValues = FilterValueList.Select(s => s.ToLowerInvariant()).ToHashSet();
var TransportAccessorsDict = new[] {
    new { FieldName = "Color", Accessor = (Func<Transport,String>)(t => t.Color) },
    new { FieldName = "Name", Accessor = (Func<Transport,String>)(t => t.Name) },
}
.ToDictionary(fa => fa.FieldName, fa => fa.Accessor);

var getFilterField = TransportAccessorsDict[FilterField];
var total = (from objects in ObjectsObservableCollection
             where FilterValues.Contains(getFilterField(objects).ToLowerInvariant())
             select objects.ID
            ).Count();

This should be reasonably performant, though if performance is a big consideration, you should probably be converting the FilterValuesList to an uppercase (I think lowercase is preferred though both are bad, a culture aware string insensitive contains would be best) HashSet before using it in the LINQ query.

I removed the ToString calls as they should be redundant, otherwise you have bigger (type) issues.

  •  Tags:  
  • linq
  • Related