Применение фильтров к таблице


Я написал длительная процедура, которую я называю несколько раз, чтобы применить фильтры, определенные заказчиком в таблице, поскольку я не понимаю, как я смогла превратить свое имя столбца строку в LINQ в колонну и, как я мог бы превратить мою строку действия на действия на строки таким образом, что функцию можно использовать в LINQ-запроса, как:

private void applyFilterAction(ref IQueryable<TempArticle> products, FilterAction action)
{
    products = from p in products where MAGIC(p, action.Column).ToLower().MAGIC2(action.Action, action.Value) == action.AddOrKill select p;
}

Я писал это вместо того, чтобы в качестве временного решения:

private void applyFilterAction(ref IQueryable<TempArticle> products, FilterAction action)
{
    var actionValue = action.Value.ToLower();
    var column = action.Column;

    if (action.Action.Equals("StartsWith"))
    {
        if (column.Equals("Description"))           products = from p in products where p.Description.ToLower().StartsWith(actionValue) == action.AddOrKill select p;
        else if (column.Equals("LongDescription"))  products = from p in products where p.LongDescription.ToLower().StartsWith(actionValue) == action.AddOrKill select p;
        else if (column.Equals("Provider"))         products = from p in products where p.Provider.ToLower().StartsWith(actionValue) == action.AddOrKill select p;
        else if (column.Equals("ProviderCode"))     products = from p in products where p.ProviderCode.ToLower().StartsWith(actionValue) == action.AddOrKill select p;
        else if (column.Equals("Publisher"))        products = from p in products where p.Publisher.ToLower().StartsWith(actionValue) == action.AddOrKill select p;
        else if (column.Equals("PublisherCode"))    products = from p in products where p.PublisherCode.ToLower().StartsWith(actionValue) == action.AddOrKill select p;
        else if (column.Equals("Custom1"))          products = from p in products where p.Custom1.ToLower().StartsWith(actionValue) == action.AddOrKill select p;
        else if (column.Equals("Custom2"))          products = from p in products where p.Custom2.ToLower().StartsWith(actionValue) == action.AddOrKill select p;
        else if (column.Equals("Custom3"))          products = from p in products where p.Custom3.ToLower().StartsWith(actionValue) == action.AddOrKill select p;
        else if (column.Equals("EanCode"))          products = from p in products where p.EanCode.ToLower().StartsWith(actionValue) == action.AddOrKill select p;
    }

    else if (action.Action.Equals("EndsWith"))
    {
        if (column.Equals("Description"))           products = from p in products where p.Description.ToLower().EndsWith(actionValue) == action.AddOrKill select p;
        else if (column.Equals("LongDescription"))  products = from p in products where p.LongDescription.ToLower().EndsWith(actionValue) == action.AddOrKill select p;
        else if (column.Equals("Provider"))         products = from p in products where p.Provider.ToLower().EndsWith(actionValue) == action.AddOrKill select p;
        else if (column.Equals("ProviderCode"))     products = from p in products where p.ProviderCode.ToLower().EndsWith(actionValue) == action.AddOrKill select p;
        else if (column.Equals("Publisher"))        products = from p in products where p.Publisher.ToLower().EndsWith(actionValue) == action.AddOrKill select p;
        else if (column.Equals("PublisherCode"))    products = from p in products where p.PublisherCode.ToLower().EndsWith(actionValue) == action.AddOrKill select p;
        else if (column.Equals("Custom1"))          products = from p in products where p.Custom1.ToLower().EndsWith(actionValue) == action.AddOrKill select p;
        else if (column.Equals("Custom2"))          products = from p in products where p.Custom2.ToLower().EndsWith(actionValue) == action.AddOrKill select p;
        else if (column.Equals("Custom3"))          products = from p in products where p.Custom3.ToLower().EndsWith(actionValue) == action.AddOrKill select p;
        else if (column.Equals("EanCode"))          products = from p in products where p.EanCode.ToLower().EndsWith(actionValue) == action.AddOrKill select p;
    }

    else if (action.Action.Equals("Contains"))
    {
        if (column.Equals("Description"))           products = from p in products where p.Description.ToLower().Contains(actionValue) == action.AddOrKill select p;
        else if (column.Equals("LongDescription"))  products = from p in products where p.LongDescription.ToLower().Contains(actionValue) == action.AddOrKill select p;
        else if (column.Equals("Provider"))         products = from p in products where p.Provider.ToLower().Contains(actionValue) == action.AddOrKill select p;
        else if (column.Equals("ProviderCode"))     products = from p in products where p.ProviderCode.ToLower().Contains(actionValue) == action.AddOrKill select p;
        else if (column.Equals("Publisher"))        products = from p in products where p.Publisher.ToLower().Contains(actionValue) == action.AddOrKill select p;
        else if (column.Equals("PublisherCode"))    products = from p in products where p.PublisherCode.ToLower().Contains(actionValue) == action.AddOrKill select p;
        else if (column.Equals("Custom1"))          products = from p in products where p.Custom1.ToLower().Contains(actionValue) == action.AddOrKill select p;
        else if (column.Equals("Custom2"))          products = from p in products where p.Custom2.ToLower().Contains(actionValue) == action.AddOrKill select p;
        else if (column.Equals("Custom3"))          products = from p in products where p.Custom3.ToLower().Contains(actionValue) == action.AddOrKill select p;
        else if (column.Equals("EanCode"))          products = from p in products where p.EanCode.ToLower().Contains(actionValue) == action.AddOrKill select p;
    }

    else if (action.Action.Equals("Exact"))
    {
        if (column.Equals("Description"))           products = from p in products where p.Description.ToLower().Equals(actionValue) == action.AddOrKill select p;
        else if (column.Equals("LongDescription"))  products = from p in products where p.LongDescription.ToLower().Equals(actionValue) == action.AddOrKill select p;
        else if (column.Equals("Provider"))         products = from p in products where p.Provider.ToLower().Equals(actionValue) == action.AddOrKill select p;
        else if (column.Equals("ProviderCode"))     products = from p in products where p.ProviderCode.ToLower().Equals(actionValue) == action.AddOrKill select p;
        else if (column.Equals("Publisher"))        products = from p in products where p.Publisher.ToLower().Equals(actionValue) == action.AddOrKill select p;
        else if (column.Equals("PublisherCode"))    products = from p in products where p.PublisherCode.ToLower().Equals(actionValue) == action.AddOrKill select p;
        else if (column.Equals("Custom1"))          products = from p in products where p.Custom1.ToLower().Equals(actionValue) == action.AddOrKill select p;
        else if (column.Equals("Custom2"))          products = from p in products where p.Custom2.ToLower().Equals(actionValue) == action.AddOrKill select p;
        else if (column.Equals("Custom3"))          products = from p in products where p.Custom3.ToLower().Equals(actionValue) == action.AddOrKill select p;
        else if (column.Equals("EanCode"))          products = from p in products where p.EanCode.ToLower().Equals(actionValue) == action.AddOrKill select p;
    }
}

Он фильтрует продукты на основе действий объект, который содержит:

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

Я мог бы написать это в лучшую сторону? Я не получил инструкций в магической функции LINQify.



1555
8
задан 11 февраля 2011 в 01:02 Источник Поделиться
Комментарии
4 ответа


  1. У вас есть много кода повторяется - это определенно что-то значит. В основном у вас есть два поля, чтобы рассмотреть действие и колонки. Так как все столбцы рассматриваются как строки, так что вы можете отделить а) логика, которая определяет, какие колонки брать и Б) логика, которая определяет, как отфильтровать столбцы. Это приведет к драматическому изменению в коде.

    Вот пример:

    private void applyFilterAction(ref IQueryable<TempArticle> products, FilterAction action)
    {
    var actionValue = action.Value.ToLower();
    var column = action.Column;

    Func<TempArticle, string> valueExtractor;
    // determining field
    if (column.Equals("Description")) valueExtractor = p => p.Description;
    else if (column.Equals("LongDescription")) valueExtractor = p => p.LongDescription;
    else if (column.Equals("Provider")) valueExtractor = p => p.Provider;
    else if (column.Equals("ProviderCode")) valueExtractor = p => p.ProviderCode;
    else if (column.Equals("Publisher")) valueExtractor = p => p.Publisher;
    else if (column.Equals("PublisherCode")) valueExtractor = p => p.PublisherCode;
    else if (column.Equals("Custom1")) valueExtractor = p => p.Custom1;
    else if (column.Equals("Custom2")) valueExtractor = p => p.Custom2;
    else if (column.Equals("Custom3")) valueExtractor = p => p.Custom3;
    else if (column.Equals("EanCode")) valueExtractor = p => p.EanCode;
    else throw new NotSupportedException(column);

    Predicate<string> filteringPredicate;

    // determining predicate
    if (action.Action.Equals("StartsWith")) filteringPredicate = s => s.StartsWith(actionValue);
    else if (action.Action.Equals("EndsWith")) filteringPredicate = s => s.EndsWith(actionValue);
    else if (action.Action.Equals("Contains")) filteringPredicate = s => s.Contains(actionValue);
    else if (action.Action.Equals("Exact")) filteringPredicate = s => s.Equals(actionValue);
    else throw new NotSupportedException(action.Action);

    products = from p in products where filteringPredicate(valueExtractor(p).ToLower()) == action.AddOrKill select p;
    }

    Такой подход гораздо лучше, но я бы пошел еще дальше. Вместо этого , если блоки, я бы использовал словарей, карт входную строку в соответствующий делегатов .
    Это не имеет ничего общего с LINQ, только здравый смысл. Это была основная часть. Мой другой точки:


  2. UpperCamelCase?

  3. действий.AddOrKill выглядит очень странно. Мне потребовалось некоторое время, чтобы понять, что вы пытаетесь достичь с его помощью. Это выглядит очень странно и должна быть переписана в другой, более удобные для разработчика способом.

  4. Я бы не стал использовать такое сигнатура метода - пустота с параметр ref. Это не выглядит сплошной с существующими методами LINQ и нет смысла в такой подписи. Я хотел бы использовать регулярные LINQ в интерфейс IQueryable Имя_метода(интерфейс IQueryable<> источник, ...других параметров).

  5. Я считаю, в таких случаях вы должны создать интерфейс Fluent - это улучшит читаемость кода очень много. Особенно в реализации не функциональности.

7
ответ дан 11 февраля 2011 в 08:02 Источник Поделиться

Я не вижу ничего в LINQ, что может быть сделано здесь... но мне кажется, все содержимое все если заявления такие же. Возможно, которые должны быть извлечены в метод?

Также:

ToLower().StartsWith(actionValue) == action.AddOrKill

Должно быть

StartsWith(actionValue, StringComparison.CurrentCultureIgnoreCase) == action.AddOrKill 

Используя нижестоящим для сравнения строк без учета регистра не уважает культуру нынешней системы, и будет давать неверные результаты для нескольких языков (особенно с 3 случаев, а не 2). Плюс, это медленнее, потому что вы преобразуете всю строку, а не просто выполнение сравнений на начало строки.


Редактировать: я вижу разницу между если блоки сейчас. Это было время, так как я играл с C#, так что я не совсем уверен, что это будет компиляция из коробки, но это должно дать вам идею. В принципе можно пройти вокруг функтор, а не копировать, если блок снова и снова:

private void applyFilterAction(ref IQueryable<TempArticle> products, FilterAction action)
{
var actionValue = action.Value.ToLower();
var column = action.Column;
Func<string, string, bool> comparer;

if (action.Action.Equals("StartsWith"))
{
comparer = (a, b) => a.StartsWith(b, StringComparison.CurrentCultureIgnoreCase);
}
else if (action.Action.Equals("EndsWith"))
{
comparer = (a, b) => a.EndsWith(b, StringComparison.CurrentCultureIgnoreCase);
}
else if (action.Action.Equals("Contains"))
{
comparer = (a, b) => a.Contains(b, StringComparison.CurrentCultureIgnoreCase);
}
else if (action.Action.Equals("Exact"))
{
comparer = (a, b) => a.Equals(b, StringComparison.CurrentCultureIgnoreCase);
}

if (column == "Description") products = from p in products where comparer(p.Description, actionValue) == action.AddOrKill select p;
else if (column == "LongDescription") products = from p in products where comparer(p.LongDescription, actionValue) == action.AddOrKill select p;
else if (column == "Provider") products = from p in products where comparer(p.Provider, actionValue) == action.AddOrKill select p;
else if (column == "ProviderCode") products = from p in products where comparer(p.ProviderCode, actionValue) == action.AddOrKill select p;
else if (column == "Publisher") products = from p in products where comparer(p.Publisher, actionValue) == action.AddOrKill select p;
else if (column == "PublisherCode") products = from p in products where comparer(p.PublisherCode, actionValue) == action.AddOrKill select p;
else if (column == "Custom1") products = from p in products where comparer(p.Custom1, actionValue) == action.AddOrKill select p;
else if (column == "Custom2") products = from p in products where comparer(p.Custom2, actionValue) == action.AddOrKill select p;
else if (column == "Custom3") products = from p in products where comparer(p.Custom3, actionValue) == action.AddOrKill select p;
else if (column == "EanCode") products = from p in products where comparer(p.EanCode, actionValue) == action.AddOrKill select p;
}

Как я уже сказал, Это было некоторое время, так как я намудрил с C#, так что это может не работать :)

2
ответ дан 11 февраля 2011 в 03:02 Источник Поделиться

Если вы можете редактировать TempArticle класса, лучшим способом будет добавить индексатор для класса, который включает имя столбца и возвращает значение из этого столбца. Ваш код фильтра тогда будет

private void applyFilterAction(ref IQueryable<TempArticle> products, FilterAction action)
{
if (action.Action == "StartsWith")
{
products = from p in products where p[action.Column].StartsWith(action.Value, StringComparison.CurrentCultureIgnoreCase) == action.AddOrKill select p;
}
else
{
products = from p in products where p[action.Column].EndsWith(action.Value, StringComparison.CurrentCultureIgnoreCase) == action.AddOrKill select p;
}
}

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

static class PropertyAccessor<T>
{
static Dictionary<string, Func<T, object>> propGetters;
static Dictionary<string, Func<T, object>> PropGetters
{
get
{
if (propGetters == null)
{
Initialize();
}
return propGetters;
}
}

static Dictionary<string, Action<T, object>> propSetters;
static Dictionary<string, Action<T, object>> PropSetters
{
get
{
if (propSetters == null)
{
Initialize();
}
return propSetters;
}
}

static void Initialize()
{
propGetters = new Dictionary<string, Func<T, object>>();
propSetters = new Dictionary<string, Action<T, object>>();

var type = typeof(T);

foreach (var pi in type.GetProperties(BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Instance))
{
if (pi.CanRead)
{
var parameter = Expression.Parameter(type, "instance");

Expression<Func<T, object>> lambda;

if (pi.PropertyType.IsValueType)
{
lambda = Expression.Lambda<Func<T, object>>(Expression.TypeAs(Expression.Property(parameter, pi.Name), typeof(object)), parameter);
}
else
{
lambda = Expression.Lambda<Func<T, object>>(Expression.Property(parameter, pi.Name), parameter);
}

propGetters.Add(pi.Name, lambda.Compile());
}

if (pi.CanWrite)
{
var paramExpT = Expression.Parameter(type, "instance");
var paramExpObj = Expression.Parameter(typeof(object), "value");
var setterLambda = Expression.Lambda<Action<T, object>>(Expression.Call(paramExpT, pi.GetSetMethod(), Expression.ConvertChecked(paramExpObj, pi.PropertyType)), paramExpT, paramExpObj);
propSetters.Add(pi.Name, setterLambda.Compile());
}
}
}

public static object Get(T instance, string propName)
{
return PropGetters[propName](instance);
}

public static string[] GetAccessorKeys
{
get
{
return PropGetters.Keys.ToArray();
}
}

public static void Set(T instance, string propName, object value)
{
PropSetters[propName](instance, value);
}

public static string[] SetAccessorKeys
{
get
{
return PropSetters.Keys.ToArray();
}
}
}

applyFilterAction:

private void applyFilterAction(ref IQueryable<TempArticle> products, FilterAction action)
{
if (action.Action == "StartsWith")
{
products = from p in products where ((string)PropertyAccessor<TempArticle>.Get(p, action.Column)).StartsWith(action.Value, StringComparison.CurrentCultureIgnoreCase) == action.AddOrKill select p;
}
else
{
products = from p in products where ((string)PropertyAccessor<TempArticle>.Get(p, action.Column)).EndsWith(action.Value, StringComparison.CurrentCultureIgnoreCase) == action.AddOrKill select p;
}
}

Методы доступа компилируются только один раз для каждого типа, но чуть медленнее, чем прямой доступ, частично из-за приведения типов. (Вы можете использовать строку вместо объекта , если вы фильтр по пи.PropertyType в инициализации цикла.)

Кроме этого, я согласен с Snowbear на стиль кодирования и использовать шаблон. Этот интерфейс выглядит совершенно нелогичным и это определенно не хорошая идея, чтобы назвать флагом или.

Часть кода доступа взяты из:
Быстрое динамическое свойство Access с помощью C# (комментарии)

Редактировать:

Если вы хотите больше скорости, вы должны использовать HyperDescriptor вместо: он пишет Il напрямую и очень быстро. В .Сеть 4, вам нужно исправить права доступа.

2
ответ дан 11 февраля 2011 в 09:02 Источник Поделиться

Я хотел бы написать некоторые вспомогательные методы расширения:

public static class FilterActionHelper {
private static Dictionary<string, Func<string, Func<string, bool>>> actions = new Dictionary<string, Func<string, Func<string, bool>>>() {
{ "StartsWith", x => y => x.StartsWith(y) },
{ "EndsWith", x => y => x.EndsWith(y) },
{ "Contains", x => y => x.Contains(y) }
};

private static Dictionary<string, Func<TempArticle, string>> propertySelectors = new Dictionary<string, Func<TempArticle, string>>() {
{ "Description", x => x.Description },
{ "LongDescription", x => x.LongDescription },
{ "Provider", x => x.Provider },
{ "ProviderCode", x => x.ProviderCode },
{ "Publisher", x => x.Publisher },
{ "PublisherCode", x => x.PublisherCode },
{ "Custom1", x => x.Custom1 },
{ "Custom2", x => x.Custom2 },
{ "Custom3", x => x.Custom3 },
};

public static bool HasAction(string actionName) {
return actions.ContainsKey(actionName);
}

public static Func<string, bool> GetAction(this string str, string actionName) {
return actions[actionName](str);
}

public static bool HasProperty(string propertyName) {
return propertySelectors.ContainsKey(propertyName);
}

public static string GetProperty(this TempArticle product, string propertyName) {
return propertySelectors[propertyName](product);
}
}

что позволит более лаконичный код:

private void ApplyFilterAction(ref IQueryable<TempArticle> products, FilterAction action) {
var actionValue = action.Value.ToLower();
var column = action.Column;

if (!FilterActionHelper.HasAction(action.Action)) {
return;
}

if (!FilterActionHelper.HasProperty(column)) {
return;
}

products = from p in products
where p.GetProperty(column).ToLower().GetAction(action.Action)(actionValue) == action.AddOrKill
select p;
}

Использование словарей позволяет легко добавлять новые свойства и действия. Тогда методы расширения сделать это намного проще, чтобы использовать эти словари в пути, который читает хорошо.

Другое решение можно найти проще-сделать фильтра универсальный:

public class FilterAction<T> {
public string Value { get; set; }
public Func<T, string> GetProperty { get; set; }
public Func<string, string, bool> Action { get; set; }
public bool AddOrKill { get; set; }

public bool Matches(T t) {
var filterValue = Value.ToLower();
var propertValue = GetProperty(t).ToLower();
return (Action(propertyValue, filterValue)) == AddOrKill);
}
}

FilterAction<TempArticle> filter = new FilterAction<TempArticle>();
filter.Value = "foo";
filter.GetProperty = x => x.Custom1;
filter.Action = x, y => x.StartsWith(y);
filter.AddOrKill = false;

var products = from p in products where filter.Matches(p) select p;

1
ответ дан 12 февраля 2011 в 05:02 Источник Поделиться