Повторное использование выражения Linq to Entities Expression <Func <T, TResult> в Select и Where calls

Предположим, что у меня есть объект сущности, определенный как

public partial class Article  
{  
    public Id
    {
        get;
        set;
    }  
    public Text
    {
        get;
        set;
    }  
    public UserId
    {
        get;
        set;
    }  
}

Основываясь на некоторых свойствах статьи, мне нужно определить, может ли статья быть удалена данным пользователем. Поэтому я добавляю статический метод для проверки. Что-то вроде:

public partial class Article  
{  
    public static Expression<Func<Article, bool>> CanBeDeletedBy(int userId)
    {  
        //Add logic to be reused here
        return a => a.UserId == userId;
    }  
}

Итак, теперь я могу сделать

using(MyEntities e = new MyEntities())  
{
    //get the current user id
    int currentUserId = 0;

    e.Articles.Where(Article.CanBeDeletedBy(currentUserid));  
}

Пока все хорошо. Теперь я хочу повторно использовать логику в CanBeDeletedBy, делая Select, что-то вроде:

using(MyEntities e = new MyEntities())  
{
    //get the current user id
    int currentUserId = 0;

    e.Articles.Select(a => new  
    {  
        Text = a.Text,  
        CanBeDeleted = ???  
    };  
}

Но независимо от того, что я пытаюсь, я не могу использовать выражение в методе select. Я предполагаю, что если я могу сделать

    e.Articles.Select(a => new  
    {  
        Text = a.Text,  
        CanBeDeleted = a => a.UserId == userId
    };  

Тогда я должен иметь возможность использовать одно и то же выражение. Пытался скомпилировать выражение и вызвать его, выполнив

    e.Articles.Select(a => new  
    {  
        Text = a.Text,  
        CanBeDeleted = Article.CanBeDeletedBy(currentUserId).Compile()(a)
    }; 

но он не будет работать.

Любые идеи о том, как заставить это работать? Или, если это невозможно, каковы альтернативы повторного использования бизнес-логики в обоих местах?

Спасибо

Педро

Ответ 1

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

Кроме того, если вы обманываете и используете Expression.Invoke, то он не поддерживается всеми провайдерами (наиболее заметно не на EF в .NET 3.5SP1).

Если это не серьезная болевая точка, я бы оставил ее с дублированием. Или вам нужно повторно использовать дерево выражений?

Ответ 2

Что я сделал, я использовал PredicateBuilder, который является классом в LinqKit, а также AsExpandable() http://www.albahari.com/nutshell/linqkit.aspx для создания выражений, и я сохранил их Статически как

public readonly Expression<Func<T,bool>>

в статическом классе. Каждое выражение основывалось на предыдущем выражении, что уменьшало количество дублирования.

Как и в предыдущем вопросе Марка Гравелла, эта любопытная вещь - черное искусство, но, к счастью, большая часть работы была сделана другим поэтом.