Как применить фильтр к результатам LINQtoSQL?

2

С помощью элемента управления ListBox можно передать ему DataSource, назвать DisplayMember и ValueMember, и через некоторую магию он отобразит поле из DataSource и вернет выбранное значение ValueMember. Он может работать с результатом linq-to-sql, даже не зная о специфике таблицы, с которой она связана.

Не отражается ли отражение и атрибуты? Как это работает! Мне нужно сделать что-то подобное, но я не знаю, с чего начать. Я новичок в LINQtoSQL.

Это то, что я хочу сделать. У меня есть исходная таблица, которую я хочу фильтровать. Исходная таблица может быть чем угодно, но будет происходить из некоторого DataContext.

var MySourceTable =
    from MyRecord in Context.GetTable<MySourceTable>()
    select new
    {
        Value = MyRecord.ID,
        Display = MyRecord.Name,
        FilterValue = MyRecord.Value
    };

В моем управлении я хочу иметь возможность фильтровать MySourceTable по некоторому заданному значению. Элемент управления не знает, какая таблица используется (MySourceTable в приведенном выше примере), и элемент управления знает только три имени, идентификатора, имени и значения полей в записи, которую он должен использовать.

Запрос фильтра должен выглядеть следующим образом.

var MyTable
    from Record in MySourceTable
    where FilterValue == GivenValue
    select new
    {
        Value = Record.ID,
        Display = Record.Name,
    };

Может кто-нибудь посоветовать мне, с чего начать?

  • 1
    Похоже, вы понимаете, как ограничить набор данных, который вы получаете от linqtosql. С чем конкретно у вас проблемы?
  • 0
    Вы ищете способ в общем передать любые MySourceTable , FilterValue и GivenValue и получить обратно MyTable ?
Показать ещё 6 комментариев
Теги:
attributes
linq-to-sql
system.reflection

3 ответа

0
Лучший ответ

Я написал механизм фильтрации, который принимает свойство и значение как строку и может использовать это как предложение where.

IQueryable<T> FilterFunction<T>(IQueryable<T> query)
{
    ParameterExpression p = Expression.Parameter(typeof(T), "notused");

    Expression<Func<T, bool>> wherePredicate =
      Expression.Lambda<Func<T, bool>>(
          Expression.Equal(
            Expression.Call(Expression.Property(p, FilterProperty), "ToString", new Type[0]),
            Expression.Constant(FilterValue)), p);

    return query.Where(wherePredicate);
}

Вы должны иметь возможность передать Expression<Func<T, TResult>> аналогичный путь в query.Select()

Если я правильно понимаю ваш вопрос, я считаю, что это сработает:

string DisplayProperty = "Name";
string ValueProperty = "ID";

IQueryable<Record> SelectRecordProperties<T>(IQueryable<T> query)
{
    ParameterExpression p = Expression.Parameter(typeof(T), "notused");

    MethodInfo ctorMethod = typeof(Record).GetMethod("Create");

    Expression<Func<T, Record>> selectPredicate =
      Expression.Lambda<Func<T, Record>>(
        Expression.Call(ctorMethod,
            Expression.PropertyOrField(p, DisplayProperty),
            Expression.PropertyOrField(p, ValueProperty)), p);

    return query.Select(selectPredicate);
}
class Record
{
    public static Record Create(string display, string value)
    {
        return new Record() { Display = display, Value = value };
    }
    public object Display { get; set; }
    public object Value { get; set; }
}

Итак, для вашей полной функции вам нужно объединить эти две идеи, чтобы ваша фильтрация работала.

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

  • 0
    Я попробую, спасибо!
  • 0
    Это путь. Я могу видеть логику этого. Все еще возникают некоторые проблемы, но это решает мою начальную проблему.
Показать ещё 3 комментария
1

Похоже, что вы отсутствуете в том месте, где находится ваш запрос. Он должен выглядеть следующим образом:

var MyTable = 
    from Record in MySourceTable
    where Record.FilterValue == GivenValue
    select new
    {
        Value = Record.ID,
        Display = Record.Name,
    };

ДанныйValue, по-видимому, является локальной переменной или свойством, содержащим все, что вы хотите сравнить FilterValue. Но FilterValue является свойством анонимного типа, созданного в вашем первом запросе, который создал MySourceTable. Во втором запросе запись является экземпляром этого анонимного типа, и вам нужно использовать эту ссылку для экземпляра во всех остальных частях запроса для ссылки на экземпляр, который вы проверяете для предложения where или для выбора для предложения select. Если вы просто разместили FilterValue, то он понятия не имеет, что вы имеете в виду.

  • 0
    Вы правы, GivenValue является локальной переменной. Когда его значение изменяется, оно запускает выполнение запроса. Что я не знаю, как это сделать, это выбрать элементы «Record.FilterValue», «Record.ID» и «Record.Name», зная их только по имени (иначе как строка). Другая проблема заключается в использовании MySourceTable, который я могу привести к IQuerable, но потом у меня ничего не осталось. Я не могу использовать это сразу, и я не могу преобразовать его в IEnumerable, просто чтобы назвать два возможных продолжения.
  • 0
    Я не совсем уверен, что вы на самом деле пытаетесь сделать здесь, но вы можете получить значение любого из полей в вашем объекте записи, выполнив Record.GetType (). GetField ("FilterValue"). GetValue (Record) .ToString (), заменив строку FilterValue строкой, соответствующей имени любого из полей вашего объекта.
0

Я нашел способ, он работает, но не является полностью удовлетворяющим методом.

"Проблема" (по сравнению с моим первоначальным вопросом) заключается в том, что она не использует linq-to-sql для фильтрации источника. Но это работает, и на данный момент это прекрасно для меня.

Резюме: В модуле высокого уровня я готовлю инструкцию LINQtoSQL, которая приведет к некоторому IQueriable <Object> , эффективно объекту IQueriable. Этот объект предоставляется модулю нижнего уровня вместе с тремя именами: один для идентификатора, один для Дисплея и один для фильтрации. Когда мне нужно больше контролировать источник, например. для нефильтрованных данных, которые приведут к огромным результатам или для сложных запросов LINQtoSQL, я буду использовать функцию делегата. Это даст мне весь контроль, который я хочу.

На низком уровне у меня есть IQueriable <Object> и никаких знаний об объекте, кроме Wat Reflection, может сказать мне. Я создаю таблицу результатов, которую я хочу использовать в определенном для управления формате. (класс записи). Для чего-то более сложного, с которым мой стандартный код не справляется, я предлагаю делегат, который должен привести к некоторому списку объектов, в которых объект должен иметь хотя бы свойство с именем "Показать" и свойство "Значение". Другие свойства возможны, но не будут использоваться.

Это решение, которое я окончательно получил:

public partial class MySelector : UserControl
{
    class Record
    {
        public object Display { get; set; }
        public object Value { get; set; }
    }

    ....

    public string MyDisplayMember { get; set; }
    public string MyValueMember { get; set; }
    public string MyExternalMember { get; set; }

    ....

    static Object Filter(MySelector sender, Object criterium)
    {
        IQueryable source = sender.MySource as IQueryable;
        if (source == null) return null;

        List<Record> result = new List<Record>();

        // drawback: this foreach loop will trigger a unfiltered SQL command.
        foreach (var record in source)
        {
            MethodInfo DisplayGetter = null;
            MethodInfo ValueGetter = null;
            bool AddRecord = false;

            foreach (PropertyInfo property in record.GetType().GetProperties())
            {
                if (property.Name == sender.MyDisplayMember) 
                {
                    DisplayGetter = property.GetGetMethod();
                }
                else if (property.Name == sender.MyValueMember)
                {
                    ValueGetter = property.GetGetMethod();
                }
                else if (property.Name == sender.MyExternalMember)
                {
                    MethodInfo ExternalGetter = property.GetGetMethod();
                    if (ExternalGetter == null)
                    {
                        break;
                    }
                    else
                    {
                        object external = ExternalGetter.Invoke(record, new object[] { });
                        AddRecord = external.Equals(criterium);
                        if (!AddRecord)
                        {
                            break;
                        }
                    }
                }
                if (AddRecord && (DisplayGetter != null) && (ValueGetter != null))
                {
                    break;
                }
            }
            if (AddRecord && (DisplayGetter != null) && (ValueGetter != null))
            {
                Record r = new Record();
                r.Display = (DisplayGetter == null) 
                    ? null 
                    : DisplayGetter.Invoke(record, new object[] { });
                r.Value = (ValueGetter == null) 
                    ? null 
                    : ValueGetter.Invoke(record, new object[] { });
                result.Add(r);
            }
        }
        return result;
    }
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню