【问题标题】:Reusing Linq to Entities' Expression<Func<T, TResult> in Select and Where calls在 Select 和 Where 调用中重用 Linq to Entities 的 Expression<Func<T, TResult>
【发布时间】:2010-03-15 11:44:09
【问题描述】:

假设我有一个实体对象定义为

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));  
}

到目前为止一切顺利。现在我想在执行 Select 时重用 CanBeDeletedBy 中的逻辑,例如:

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)
    }; 

但它也不起作用。

关于如何让它发挥作用的任何想法?或者如果不可能,在这两个地方重用业务逻辑的替代方法是什么?

谢谢

佩德罗

【问题讨论】:

  • 编译表达式是正确的选择,它编译并为我工作。如果是我,我也会考虑编译。你得到什么错误?
  • 是的,它编译得很好,但会引发 NotSupportedException 异常:“LINQ to Entities 不支持 LINQ 表达式节点类型 'Invoke'。”尝试将 Select 之外的表达式编译为 Func
    并在内部使用它,结果相同。
  • 顺便说一句,如果我使用普通的 Func
    ,并在 Where 方法中使用它,那么查询将在客户端执行,这不是预期的目的。
  • 啊,对不起,我的错 - 错过了你正在使用 EF 的事实 - 它适用于 linq to objects :)

标签: c# linq delegates lambda


【解决方案1】:

重用表达式树是一门黑魔法;你可以做到,但你需要将大量代码切换到反射,你会失去所有的静态检查。特别是,使用匿名类型会变成一场噩梦(尽管 4.0 中的 dynamic 可能可行)。

此外,如果您作弊并使用 Expression.Invoke,则并非所有提供商都支持它(最明显的是 .NET 3.5SP1 中的 EF 不支持)。

除非这是一个主要的痛点,否则我会留下重复的内容。或者您是否需要重用表达式树?

【讨论】:

  • 实际上不需要重用表达式树。这只是为了尽量不重复与安全相关的代码。但我现在就这样做,看看我以后是否可以重构代码。谢谢
【解决方案2】:

我所做的是我使用 PredicateBuilder(它是 LinqKit 中的一个类)和 AsExpandable() http://www.albahari.com/nutshell/linqkit.aspx 来构建表达式并将它们静态存储为

public readonly Expression<Func<T,bool>>

在静态类中。每个表达式都建立在前一个表达式的基础上,从而减少了重复量。

正如 Marc Gravell 的上一个问题所暗示的那样,这种事情是一种黑色艺术,但谢天谢地,其他人已经完成了很多工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-11
    相关资源
    最近更新 更多