开发者

C# Linq where clause according to property name

开发者 https://www.devze.com 2023-04-12 00:36 出处:网络
Let\'s say I have the following class : public class Person { public string FirstName { get; set; } public string SurName { get; set; }

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.

searc开发者_如何学编程hField 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?


Looks like you need Dynamic Linq queries:

http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx


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


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.


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);
}


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);


Use Reflection

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


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消