2015-02-10 2 views
5

Мне нужна помощь, у меня есть метод, который повторяется 6 раз в моем классе, и единственное, что изменяется в каждом из методов, это свойство LINQ (в примере " имя, но у меня также есть имя для фамилии, фирменного наименования, идентификатора пользователя, статуса). Я хотел бы получить некоторую помощь рефакторинга это так, что я могу использовать только один метод и сделать свойство быть динамическими или передаются в.Помощь с рефакторингом метода LINQ

private static IQueryable<MyModel> FilterFirstName(IQueryable<MyModel> query, string searchText, string searchFilter) 
    { 
     switch (searchFilter.ToLower()) 
     { 
      case "contains": 
       query = query.Where(x => x.FirstName.ToLower().Contains(searchText.ToLower())); 
       break; 
      case "does not contain": 
       query = query.Where(x => !x.FirstName.ToLower().Contains(searchText.ToLower())); 
       break; 
      case "starts with": 
       query = query.Where(x => x.FirstName.StartsWith(searchText, StringComparison.InvariantCultureIgnoreCase)); 
       break; 
      case "ends with": 
       query = query.Where(x => x.FirstName.EndsWith(searchText, StringComparison.InvariantCultureIgnoreCase)); 
       break; 
      case "equals": 
       query = query.Where(x => x.FirstName.Equals(searchText, StringComparison.InvariantCultureIgnoreCase)); 
       break; 
     } 

     return query; 
    } 
+0

Всегда ли строковое свойство? – spender

+0

да, это всегда свойство строки – Madhatter5501

ответ

2

Что вы можете сделать, это использовать Compose метод, который может составить одно выражение с другим:

public static Expression<Func<TFirstParam, TResult>> 
    Compose<TFirstParam, TIntermediate, TResult>(
    this Expression<Func<TFirstParam, TIntermediate>> first, 
    Expression<Func<TIntermediate, TResult>> second) 
{ 
    var param = Expression.Parameter(typeof(TFirstParam), "param"); 

    var newFirst = first.Body.Replace(first.Parameters[0], param); 
    var newSecond = second.Body.Replace(second.Parameters[0], newFirst); 

    return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param); 
} 

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

public static Expression Replace(this Expression expression, 
    Expression searchEx, Expression replaceEx) 
{ 
    return new ReplaceVisitor(searchEx, replaceEx).Visit(expression); 
} 
internal class ReplaceVisitor : ExpressionVisitor 
{ 
    private readonly Expression from, to; 
    public ReplaceVisitor(Expression from, Expression to) 
    { 
     this.from = from; 
     this.to = to; 
    } 
    public override Expression Visit(Expression node) 
    { 
     return node == from ? to : base.Visit(node); 
    } 
} 

Теперь вы можете написать:

private static IQueryable<MyModel> FilterFirstName(
    IQueryable<MyModel> query, 
    Expression<Func<MyModel, string>> selector, 
    string searchText, 
    string searchFilter) 
{ 
    switch (searchFilter.ToLower()) 
    { 
     case "contains": 
      query = query.Where(selector.Compose(
       text => text.ToLower().Contains(searchText.ToLower()))); 
      break; 
     case "does not contain": 
      query = query.Where(selector.Compose(
       text => !text.ToLower().Contains(searchText.ToLower()))); 
      break; 
     case "starts with": 
      query = query.Where(selector.Compose(
       text => text.StartsWith(searchText, StringComparison.InvariantCultureIgnoreCase))); 
      break; 
     case "ends with": 
      query = query.Where(selector.Compose(
       text => text.EndsWith(searchText, StringComparison.InvariantCultureIgnoreCase))); 
      break; 
     case "equals": 
      query = query.Where(selector.Compose(
       text => text.Equals(searchText, StringComparison.InvariantCultureIgnoreCase))); 
      break; 
    } 

    return query; 
} 

На боковой ноте, вы ould действительно используют enum для представления различных типов фильтров для searchFilter, а не для строки. Это сделает его намного проще для вызывающего, так как им не нужно будет вводить точную строку, не имея никакого хорошего способа узнать, что такое точные параметры, или если предоставленный параметр действителен.

+0

, это выглядит хорошо, но как мне это назвать? – Madhatter5501

+1

@ Madhatter5501 Предоставить лямбду, которая проецирует элемент в списке в строковое значение, предположительно, путем доступа к его свойствам в большинстве случаев (хотя он может делать что угодно). За исключением одного добавленного параметра, остальное все равно. – Servy

+1

Это по существу PredicateBuilder (http://www.albahari.com/nutshell/predicatebuilder.aspx), который является гораздо более сложным решением, чем то, что требуется. Я думаю, что @ Madhatter5501 просто нужно выбрать отдельную собственность у объекта, а не создавать произвольные запросы. –

-5

Если это называется 6 раз в вашем коде, и есть 5 путей внутри него, Я бы сказал, что сам метод вводит ненужную связь и должен быть удален.

+0

да, я junior dev, просто ищу помощь – Madhatter5501

+0

Я бы полностью удалил этот метод и попросил ваш код напрямую использовать запросы LINQ. Нет причин, кроме гораздо более сложных сценариев, чтобы поместить слой абстракции поверх LINQ. – moarboilerplate

+4

Вопрос в том, как конкретно его убрать; вы не даете никаких указаний относительно того, как это сделать. – Servy

-1

Эта версия позволяет передавать свойство фильтра, например:

Filter(models, (MyModel m) => m.FirstName, "Joe", "contains"); 
Filter(models, (MyModel m) => m.LastName, "Smith", "contains"); 

private static IQueryable<MyModel> Filter(IQueryable<MyModel> query, Func<MyModel, string> property, string searchText, string searchFilter) 
{ 
    switch (searchFilter.ToLower()) 
    { 
     case "contains": 
      query = query.Where(x => property(x).ToLower().Contains(searchText.ToLower())); 
      break; 
     case "does not contain": 
      query = query.Where(x => !property(x).ToLower().Contains(searchText.ToLower())); 
      break; 
     case "starts with": 
      query = query.Where(x => property(x).StartsWith(searchText, StringComparison.InvariantCultureIgnoreCase)); 
      break; 
     case "ends with": 
      query = query.Where(x => property(x).EndsWith(searchText, StringComparison.InvariantCultureIgnoreCase)); 
      break; 
     case "equals": 
      query = query.Where(x => property(x).Equals(searchText, StringComparison.InvariantCultureIgnoreCase)); 
      break; 
    } 

    return query; 
} 
+0

Поставщик запросов не сможет перевести это на SQL; они будут ошибки при попытке выполнить запрос. Кроме того, нет необходимости явно указывать тип лямбда при вызове метода; это будет выведено. – Servy

+0

Оооооооооооооооооооооооооооооооооооооооооооооооооо! Я проверил его с локально определенным списком. ий · Che. –

-1

Я хотел бы сделать одно или несколько из следующих действий:

  1. Отражения и кэшировать поиск свойства " метод "(бит" x.FirstName "может быть легко обработан с помощью отражения

  2. Возьмите функцию« селектор »и ​​примените ее для получения целевого объекта

  3. Сделать это метод расширения

Код:

public static class FilterPerson 
{ 
    static IQueryable<Person> FilterPerson(
     this IQueryable<Person> query, 
     FilterString filter, 
     Func<Person, string> selector, 
     string searchText) 
    { 
     var enumerableQuery = query.AsEnumerable(); 
     switch (filter) 
     { 
      case FilterString.Contains: 
       enumerableQuery = enumerableQuery.Where(x => selector(x).ToLowerInvariant().Contains(searchText.ToLowerInvariant())); 
       break; 
      case FilterString.DoesNotContain: 
       enumerableQuery = enumerableQuery.Where(x => !selector(x).ToLower().Contains(searchText.ToLower())); 
       break; 
      case FilterString.StartsWith: 
       enumerableQuery = enumerableQuery.Where(x => selector(x).StartsWith(searchText, StringComparison.InvariantCultureIgnoreCase)); 
       break; 
      case FilterString.EndsWith: 
       enumerableQuery = enumerableQuery.Where(x => selector(x).EndsWith(searchText, StringComparison.InvariantCultureIgnoreCase)); 
       break; 
      case FilterString.Equals: 
       enumerableQuery = enumerableQuery.Where(x => selector(x).Equals(searchText, StringComparison.InvariantCultureIgnoreCase)); 
       break; 
     } 

     return enumerableQuery.AsQueryable(); 
    } 
} 

public class Person 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public string PhoneNumber { get; set; } 

} 

public enum FilterString 
{ 
    StartsWith, 
    Contains, 
    DoesNotContain, 
    EndsWith, 
    Equals 
} 
+0

Поставщик запросов не сможет перевести это на SQL; они будут ошибки при попытке выполнить запрос. – Servy

+0

Пример? Доказательство? Он также не указал необходимость SQL. –

+1

Он принимает 'IQueryable'. Если бы это была коллекция в памяти, это было бы 'IEnumerable'. Даже если это не база данных, а 'IQueryable' на самом деле является представлением для чего-то другого, остается то, что поставщик запросов (по крайней мере, любой поставщик запросов, отличный от провайдера запросов LINQ to objects) не сможет осмысленно переведите это во что-нибудь еще, потому что оно зависит от возможности выполнить 'selector'. Если он принимает «IEnumerable», то это будет абсолютно правильным решением. – Servy

Смежные вопросы