tugberk
tugberk

Reputation: 58434

C# Linq where clause according to property name

Let's say I have the following class :

public class Person { 

    public string FirstName { get; set; }
    public string SurName { get; set; }
    public int Age { get; set; }
    public string Gender { get; set; }

}

Also, I have the following method and I am reaching out to person data via a repository.

public IEnumerable<Person> getPeople(string searchField, string searchTerm) { 

    //_repo.GetAll() returns IEnumerable<Person>
    var model = _repo.GetAll(); 

    //Need the logic here for filtering

    return model;
}

As you can see I am getting two parameter for the method : searchField and searchTerm.

searchField is for the field name whose value will be used for filtering. searchTerm is the value which will be used to compare with retrived value (sorry if I am not clear here but this is the most I can come up with)

What I would normally do is as follows :

public IEnumerable<Person> getPeople(string searchField, string searchTerm) { 

    //_repo.GetAll() returns IEnumerable<Person>
    var model = _repo.GetAll(); 

    switch(searchField) { 

        case "FirstName":
            model = model.Where(x => x.FirstName == searchTerm);
            break;

        case "SurName":
            model = model.Where(x => x.SurName == searchTerm);
            break;

        //Keeps going
    }

    return model;

}

Which will work just fine. But if I make a change on my class, this code will have a change to break or be in lack of some functions if I add new properties this class.

What I am looking for is something like below :

NOTE :

This below code completely belongs to my imagination and there is no such a thing exists.

model = model.Where(x => x.GetPropertyByName(searchField) == searchTerm);

Am I flying too high here if it is impossible or being complete idiot if there is already a built in way for this?

Upvotes: 10

Views: 13827

Answers (7)

Jim Wooley
Jim Wooley

Reputation: 10398

Rather than messing with reflection, custom expression trees, etc., when using Entity Framework, consider using the Builder Method extensions to the standard LINQ operators which take strings rather than lambdas. These are much easier to build for dynamic query requirements:

 string filter = String.Format("it.{0} = @value", fieldName);
 var model = context.People.Where(filter, new ObjectParameter("value", searchValue));

Of course, this would mean that you yould need to modify your repository to return IObjectSet rather than IEnumerable. It would perform better as well. By returning IEnumerable, you are hydrating every row in your database to your repository and then filtering via LINQ to Objects rather than applying the filter back in your database.

For more information about the Builder Methods in EF, see the BuilderMethodSamples.cs in http://archive.msdn.microsoft.com/EFQuerySamples/Release/ProjectReleases.aspx?ReleaseId=4422.

Upvotes: 0

blangblang
blangblang

Reputation: 21

I think the following implementation looks an awful lot like what you originally intended, although changing this to a generic method likely makes more sense.

public IEnumerable<Person> getPeople(string searchField, string searchTerm) {
        PropertyInfo getter=typeof(Person).GetProperty(searchField);
        if(getter==null) {
            throw new ArgumentOutOfRangeException("searchField");
        }
        return _repo.GetAll().Where(x => getter.GetValue(x, null).ToString()==searchTerm);
}

Upvotes: 2

Joey
Joey

Reputation: 1790

This should be type-safe:

public IEnumerable<T> Where<T,U>(Func<T,U> propertySelector, U value)
{
  return  model.Where(x => propertySelector(x) == value);
}

usage:

Where((MyClass x) => x.PropertyName, propertyValue);

Or:

public IEnumerable<T> Where<T>(Func<T,bool> entitySelector)
{
  return  model.Where(entitySelector);
}

usage:

Where<MyClass>(x => x.PropertyName == propertyValue && x.OtherProperty == otherValue);

Upvotes: 0

Saeed Amiri
Saeed Amiri

Reputation: 22555

For linq2Object You can use reflection as bellow(it's not very fast):

model.Where(x => x.GetType().GetProperty(propName).GetValue(x, null) == propVal);

but for linq2Entity I think this doesn't work, it works for linq2objects.

Upvotes: 2

Albin Sunnanbo
Albin Sunnanbo

Reputation: 47038

Use Reflection

model = model.Where(x =>
((string)x.GetType().GetProperty("searchField").GetValue(0, null)) == searchTerm);

Upvotes: 0

Icarus
Icarus

Reputation: 63956

I use this extension method to achieve what you want.

public static IQueryable<TEntity> Where<TEntity>(this IQueryable<TEntity> source, string propertyName, string value) 
{

    Expression<Func<TEntity, bool>> whereExpression = x => x.GetType().InvokeMember(propertyName, BindingFlags.GetProperty, null, x, null).ObjectToString().IndexOf(value, StringComparison.InvariantCultureIgnoreCase) >= 0;

    return source.Where(whereExpression);       
}

Note: ObjectToString is just another extension method that returns string.Empty if the Object passed in is NULL

Upvotes: 3

Related Questions