C # Linq, где пункт в соответствии с именем свойства - PullRequest
6 голосов
/ 11 октября 2011

Допустим, у меня есть следующий класс:

public class Person { 

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

}

Кроме того, у меня есть следующий метод, и я обращаюсь к личным данным через репозиторий.

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

Как вывидно, что я получаю два параметра для метода: searchField и searchTerm.

searchField - для имени поля, значение которого будет использоваться для фильтрации.searchTerm - это значение, которое будет использоваться для сравнения с восстановленным значением (извините, если я не совсем уверен, но это самое большее, что я могу придумать)

То, что я обычно делал бысделать это следующим образом:

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;

}

, который будет работать очень хорошо.Но если я внесу изменения в свой класс, в этом коде будет изменение, которое будет нарушено или в нем отсутствуют некоторые функции, если я добавлю новые свойства этому классу.

То, что я ищу, выглядит примерно так:

ПРИМЕЧАНИЕ:

Этот код ниже полностью принадлежит моему воображению, и такого понятия не существует.

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

Я летаю слишком высоко, если это невозможно, или я полный идиот, если для этого уже есть способ?

Ответы [ 7 ]

5 голосов
/ 11 октября 2011

Похоже, вам нужно Динамические запросы Linq :

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

2 голосов
/ 11 октября 2011

Для linq2Object Вы можете использовать отражение как показано ниже (это не очень быстро):

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

, но для linq2Entity я думаю, что это не работает, оно работает для linq2objects.

2 голосов
/ 11 октября 2011

Я использую этот метод расширения, чтобы достичь того, что вы хотите.

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

Примечание: ObjectToString - это просто еще один метод расширения, который возвращает строку. Пустой, если переданный объект равен NULL

1 голос
/ 11 октября 2011

Я думаю, что следующая реализация выглядит очень похоже на то, что вы изначально намеревались, хотя, вероятно, имеет смысл изменить ее на общий метод.

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);
}
1 голос
/ 11 октября 2011

Это должно быть безопасным для типа:

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

использование:

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

Или:

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

использование:

Where<MyClass>(x => x.PropertyName == propertyValue && x.OtherProperty == otherValue);
0 голосов
/ 11 октября 2011

Вместо использования отражения, деревьев пользовательских выражений и т. Д. При использовании Entity Framework рассмотрите возможность использования расширений метода Builder для стандартных операторов LINQ, которые принимают строки, а не лямбда-выражения.Их гораздо проще построить для требований динамических запросов:

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

Конечно, это будет означать, что вам нужно будет изменить свой репозиторий, чтобы он возвращал IObjectSet, а не IEnumerable.Это будет работать лучше, а также.Возвращая IEnumerable, вы гидрируете каждую строку в своей базе данных в свой репозиторий, а затем фильтруете через LINQ to Objects вместо того, чтобы применять фильтр обратно в вашей базе данных.

Для получения дополнительной информации о методах Builder в EF см.BuilderMethodSamples.cs в http://archive.msdn.microsoft.com/EFQuerySamples/Release/ProjectReleases.aspx?ReleaseId=4422.

0 голосов
/ 11 октября 2011

Использовать отражение

model = model.Where(x =>
((string)x.GetType().GetProperty("searchField").GetValue(0, null)) == searchTerm);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...