Объединение нескольких выражений (Expression <Func <T, bool >>) не работает с переменными. Зачем? - PullRequest
2 голосов
/ 04 декабря 2009

хорошо, ребята, голые со мной. Сначала я подведу итоги, а затем подробно расскажу.

Я написал несколько методов (.WhereOr, .WhereAnd), которые в основном позволяют мне «складывать» кучу лямбда-запросов, а затем применять их к коллекции. Например, использование с наборами данных было бы немного похоже на это (хотя это работает с любым классом с использованием обобщений):

С LINQ TO DATASETS (с использованием .NET DataSetExtensions)

DataTable Result;

List<Expression<Func<DataRow, bool>> Queries = new List<Expression<Func<DataRow, bool>>();

Queries.Add(dr=> dr.Field<string>("field1") == "somestring");
Queries.Add(dr=> dr.Field<string>("field2") == "somestring"); 
Queries.Add(dr=> dr.Field<string>("field3") == "somestring"); 

Result = GetSomeTable().AsEnumarable().WhereOr(Queries).CopyToDataTable();

Теперь скажите, что в приведенном выше примере только одна строка в коллекции соответствует "somestring" и находится в поле "field2".

Это означает, что значение Result должно быть равно 1.

Теперь, скажем, я немного переписал код выше к этому:

DataTable Result;

List<Expression<Func<DataRow, bool>> Queries = new List<Expression<Func<DataRow, bool>>();

List<string> columns = new string[]{"field1","field2","field3"}.ToList();

string col;

foreach(string c in columns){
    col = c;
    Queries.Add(dr=> dr.Field<string>(col) == "somestring");
}

Result = GetSomeTable().AsEnumarable().WhereOr(Queries).CopyToDataTable();

Я не очень понимаю выражения, но для меня оба приведенных выше примера делают одно и то же.

За исключением того, что "Result" в первом примере имеет счетчик 1, а "Result" во втором примере имеет счетчик 0.

Кроме того, в столбцах списка во втором примере, если вы поставите «field2» последним, а не вторым, тогда «Result» правильно будет иметь счет 1.

Итак, из всего этого я пришел к какому-то выводу, но я не совсем понимаю, что происходит, и как это исправить ..? Могу ли я «оценить» эти выражения раньше ... или их часть?

ВЫВОД:

По сути, кажется, что если я посылаю туда буквальные значения, например, "field1", это работает. Но если я отправлю переменные, такие как «col», это не сработает, потому что эти «выражения» оцениваются намного позже в коде.

это также объясняет, почему это работает, когда я перемещаю «field2» в последнюю позицию. это работает, потому что переменная "col" была наконец назначена "field2", таким образом, к тому времени, когда выражения оценивают "col", равно "field2".

Ладно, есть ли способ обойти это?

Вот код для моего метода WhereOr (это метод расширения IENumerable):

public static IQueryable<T> WhereOr<T>(this IEnumerable<T> Source, List<Expression<Func<T, bool>>> Predicates) {

        Expression<Func<T, bool>> FinalQuery;

        FinalQuery = e => false;

        foreach (Expression<Func<T, bool>> Predicate in Predicates) {
            FinalQuery = FinalQuery.Or(Predicate);
        }

        return Source.AsQueryable<T>().Where(FinalQuery);
    }

public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> Source, Expression<Func<T, bool>> Predicate) {
        InvocationExpression invokedExpression = Expression.Invoke(Predicate, Source.Parameters.Cast<Expression>());
        return Expression.Lambda<Func<T, bool>>(Expression.Or(Source.Body, invokedExpression), Source.Parameters);
    }

Ответы [ 4 ]

6 голосов
/ 04 декабря 2009

Ответ «как исправить». Изменить это:

string col;
foreach(string c in columns) {
    col = c;
    Queries.Add(dr=> dr.Field<string>(col) == "somestring");
} 

к этому:

foreach(string c in columns) {
    string col = c;
    Queries.Add(dr=> dr.Field<string>(col) == "somestring");
} 

Наслаждайтесь. Ответ «что и почему» дал Брайан.

6 голосов
/ 04 декабря 2009

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

См

http://lorgonblog.spaces.live.com/blog/cns!701679AD17B6D310!689.entry

и

http://blogs.msdn.com/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx

1 голос
/ 04 декабря 2009

О Боже, после того, как я прокомментировал, я увидел проблему. Вы используете одну и ту же переменную «col» в каждой итерации цикла. Когда вы создаете лямбда-выражение, оно не привязывается к значению переменной, оно ссылается на саму переменную. К тому времени, когда вы выполняете запрос, «Col» устанавливается равным последнему значению. Попробуйте создать временную строку внутри цикла, установить ее значение и использовать ее.

0 голосов
/ 12 сентября 2014

Я нашел отличную статью, чтобы выполнить ваши требования.
Ссылка предоставляет And<T> и Or<T> метод расширения, чтобы сделать это.

http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx

...