Передача дерева выражений в качестве параметра другому дереву выражений

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

private Expression<Func<TEntity, TPropertyResult>> PropertyAccessor { get; set; }

и

private Expression<Func<TPropertyResult, bool>> TestExpression { get; set; }

Мне нужно создать новое дерево выражений, которое будет эквивалентно:

var expression = p => this.TestExpression(this.PropertyAccessor(p));

При использовании Expression.Invoke(this.TestExpression, this.PropertyAccessor) я получаю следующую ошибку

{"Выражение типа 'System.Func`2[MyEntity,System.String]' нельзя использовать для параметра типа 'System.String'"}

TPropertyResult — это строка во время моего теста.

Я пробовал использовать Expression.Call или Expression.Invoke. Не повезло. Что я должен использовать?


person Pierre-Alain Vigeant    schedule 19.08.2010    source источник
comment
В чем была ошибка компилятора? Кроме того, код не так читаем. Вы уверены, что возможностей .Net 2.0 недостаточно для того, чего вы хотите достичь?   -  person Hamish Grubijan    schedule 19.08.2010
comment
При использовании Invoke пишет: {"Expression of type 'System.Func2[MyEntity,System.String]' нельзя использовать для параметра типа 'System.String'}. That is when I try to specify a string as the TPropertyResult`, но проблема не ограничивается строкой.   -  person Pierre-Alain Vigeant    schedule 19.08.2010


Ответы (2)


Я думаю, что это делает то, о чем вы просите:

Expression<Func<TEntity, bool>> Combined
{
    get
    {
        var entity = Expression.Parameter(typeof(TEntity));
        var pa = Expression.Invoke(PropertyAccessor, entity);
        var te = Expression.Invoke(TestExpression, pa);
        return (Expression<Func<TEntity, bool>>) Expression.Lambda(te, entity);
    }
}

Я проверил это, и это работает так, как я ожидал.

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

private Func<TEntity, TPropertyResult> PropertyAccessor { get; set; }
private Func<TPropertyResult, bool> TestExpression { get; set; }
private Func<TEntity, bool> Combined
{
    get
    {
        return entity => TestExpression(PropertyAccessor(entity));
    }
}

Пример использования:

// Set up the original functions
PropertyAccessor = entity => GenerateResult(entity);
TestExpression = result => result.IsCool();

// This stores a reference to the combined function
var fn = Combined;

// This actually evaluates the function
bool isCool = fn(myEntity);

// Alternatively, you could evaluate the function directly, without the variable
bool isCool = Combined(myEntity);
person Timwi    schedule 19.08.2010
comment
Вау, спасибо за этот рабочий ответ. Я превзошел себя, нырнув в выражение. Я предполагаю, что глядя на код ASP.NET MVC, вы думаете, что Expression<T> нужны везде. - person Pierre-Alain Vigeant; 19.08.2010
comment
У Марка Гравелла есть отличная статья на InfoQ о том, как и почему вы должны использовать Expression, и несколько хороших конкретных примеров. infoq.com/articles/expression-compiler - person David Robbins; 25.08.2010
comment
Спасибо, мне нужно использовать его в linq to sql, но получаю эту ошибку: The LINQ expression node type 'Invoke' is not supported in LINQ to Entities. не могли бы вы мне помочь? - person Mohsen; 18.10.2014