C#: объект с тем же ключом был уже добавлен при компиляции выражения

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

Я создаю выражения, которые я использую для фильтрации запросов. Для упрощения того процесса, у меня есть несколько Expression> дополнительные методы, который делает мой инструмент для очистки кода и до сих пор они работали приятно. Я записал тесты для всех них кроме одного, которого я записал тому на сегодняшний день. И тот тест перестал работать полностью с ArgumentException с длинным отслеживанием стека. И я просто не получаю его. Тем более, что я использовал тот метод некоторое время успешно в моих запросах!

Так или иначе вот отслеживание стека, которое я получаю при запущении теста:

failed: System.ArgumentException : An item with the same key has already been added.
    at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
    at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
    at System.Linq.Expressions.ExpressionCompiler.PrepareInitLocal(ILGenerator gen, ParameterExpression p)
    at System.Linq.Expressions.ExpressionCompiler.GenerateInvoke(ILGenerator gen, InvocationExpression invoke, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateBinary(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateUnliftedAndAlso(ILGenerator gen, BinaryExpression b)
    at System.Linq.Expressions.ExpressionCompiler.GenerateAndAlso(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateBinary(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateUnliftedOrElse(ILGenerator gen, BinaryExpression b)
    at System.Linq.Expressions.ExpressionCompiler.GenerateOrElse(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateBinary(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateInvoke(ILGenerator gen, InvocationExpression invoke, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateUnliftedAndAlso(ILGenerator gen, BinaryExpression b)
    at System.Linq.Expressions.ExpressionCompiler.GenerateAndAlso(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateBinary(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateLambda(LambdaExpression lambda)
    at System.Linq.Expressions.ExpressionCompiler.CompileDynamicLambda(LambdaExpression lambda)
    at System.Linq.Expressions.Expression`1.Compile()
    PredicateTests.cs(257,0): at Namespace.ExpressionExtensionsTests.WhereWithin_CollectionIsFilteredAsExpected()

Сам тест похож на следующее, это перестало работать в операторе Compile:

[Test]
public void WhereWithin_CollectionIsFilteredAsExpected()
{
    var range = new[] { Range.Create(2, 7), Range.Create(15, 18) };

    var predicate = Predicate
        .Create(x => x % 2 == 0)
        .AndWithin(range, x => x)
        .Compile();

    var actual = Enumerable.Range(0, 20)
        .Where(predicate)
        .ToArray();

    Assert.That(actual, Is.EqualTo(new[] { 2, 4, 6, 16, 18 }));
}

Я просто не понимаю сообщение об ошибке. Я думал, что это могло бы иметь отношение к тому, что я всегда использую x как название параметра, но, казалось, не помог, когда я пытался подкачать их вокруг. Что делает, еще более странный мне случается так, что я использовал этот точный метод некоторое время уже в больших запросах Linq2Sql, и они всегда работали приятно. Таким образом в моем тесте я пытался не скомпилировать выражение и использование AsQueryable таким образом, я мог использовать его на этом вместо этого. Но это просто заставило исключение произойти на ToArray вместо этого. Что продолжается здесь? Как я могу зафиксировать это?

Можно найти оскорбление и раздражающий код в zip-файле ниже строки:


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


Обновление: Упрощенный проект в качестве примера еще больше с некоторыми предложениями от @Mark. Как удаление класса диапазона и вместо этого просто трудно кодирование единственного постоянного диапазона. Также добавленный другой пример, где использование того же самого метода на самом деле хорошо работает. Так, использование метода AndWithin делает сбой приложения, в то время как использование метода WhereWithin на самом деле хорошо работает. Я чувствую себя в значительной степени невежественным!

17
задан Svish 21 January 2010 в 10:16
поделиться

2 ответа

Это не ответ, но я надеюсь, что это поможет кому-то найти ответ. Я упростил код дальше, чтобы он был только одним файлом, и все же не проходит так же. Я переименовал переменные, чтобы «X» не используется дважды. Я удалил класс диапазона и заменил его жесткозедированными константами 0 и 1.

using System;
using System.Linq;
using System.Linq.Expressions;

class Program
{
    static Expression<Func<int, bool>> And(Expression<Func<int, bool>> first,
                                           Expression<Func<int, bool>> second)
    {
        var x = Expression.Parameter(typeof(int), "x");
        var body = Expression.AndAlso(Expression.Invoke(first, x), Expression.Invoke(second, x));
        return Expression.Lambda<Func<int, bool>>(body, x);
    }

    static Expression<Func<int, bool>> GetPredicateFor(Expression<Func<int, int>> selector)
    {
        var param = Expression.Parameter(typeof(int), "y");
        var member = Expression.Invoke(selector, param);

        Expression body =
            Expression.AndAlso(
                Expression.GreaterThanOrEqual(member, Expression.Constant(0, typeof(int))),
                Expression.LessThanOrEqual(member, Expression.Constant(1, typeof(int))));

        return Expression.Lambda<Func<int, bool>>(body, param);
    }

    static void Main()
    {
        Expression<Func<int, bool>> predicate = a => true;
        predicate = And(predicate, GetPredicateFor(b => b)); // Comment out this line and it will run without error
        var z = predicate.Compile();
    }
}

Выражение выглядит так, как это в отладчике:

x => (Invoke(a => True,x) && Invoke(y => ((Invoke(b => b,y) >= 0) && (Invoke(b => b,y) <= 1)),x))

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

using System;
using System.Linq;
using System.Linq.Expressions;

class Program
{
    static void Main()
    {
        Expression<Func<int, bool>> selector = b => true;
        ParameterExpression param = Expression.Parameter(typeof(int), "y");
        InvocationExpression member = Expression.Invoke(selector, param);
        Expression body = Expression.AndAlso(member, member);
        Expression<Func<int, bool>> predicate = Expression.Lambda<Func<int, bool>>(body, param);
        var z = predicate.Compile();
    }
}
2
ответ дан 30 November 2019 в 14:48
поделиться

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

public static Expression<Func<TSubject, bool>> AndWithin<TSubject, TField>(
    this Expression<Func<TSubject, bool>> original,
    IEnumerable<Range<TField>> range, Expression<Func<TSubject, TField>> field) where TField : IComparable<TField>
{
  return original.And(range.GetPredicateFor(field));
}


static Expression<Func<TSource, bool>> GetPredicateFor<TSource, TValue>
    (this IEnumerable<Range<TValue>> range, Expression<Func<TSource, TValue>> selector) where TValue : IComparable<TValue>
{
  var param = Expression.Parameter(typeof(TSource), "x");

  if (range == null || !range.Any())
    return Expression.Lambda<Func<TSource, bool>>(Expression.Constant(false), param);

  Expression body = null;
  foreach (var r in range)
  {
    Expression<Func<TValue, TValue, TValue, bool>> BT = (val, min, max) => val.CompareTo(min) >= 0 && val.CompareTo(max) <= 0;
    var newPart = Expression.Invoke(BT, param,
                                      Expression.Constant(r.Start, typeof(TValue)),
                                      Expression.Constant(r.End, typeof(TValue)));

    body = body == null ? newPart : (Expression)Expression.OrElse(body, newPart);
  }

  return Expression.Lambda<Func<TSource, bool>>(body, param);
}

оба имеют дополнительное ограничение Imparable (единственный Изменить на первый метод).

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

Отказ от ответственности: Я до сих пор не полностью понимаю, почему ваш предыдущий метод не работал, но этот альтернативный подход обходит проблему. Дайте мне знать, если это не то, что вы после того, как мы попробуем что-то еще.

Кроме того, kudos на спрашивают вопрос хорошо, образец проекта является примерным.

3
ответ дан 30 November 2019 в 14:48
поделиться
Другие вопросы по тегам:

Похожие вопросы: