假设我有一个实体对象定义为
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)
};
但它也不起作用。
有关如何使其发挥作用的任何想法?或者,如果不可能,在两个地方重用业务逻辑的替代方案是什么?
由于
佩德罗
答案 0 :(得分:4)
重复使用表达式树是一种黑色艺术;你可以做到,但你需要将大量代码切换到反射,你将失去所有的静态检查。特别是,使用匿名类型变成了一场噩梦(虽然4.0中的dynamic
可能是可行的。)
此外,如果你作弊并使用Expression.Invoke
,那么所有提供商都不支持它(最明显的是不支持.NET 3.5SP1中的EF)。
除非这是一个主要的痛点,否则我会留下重复。或者你需要来重用表达式树吗?
答案 1 :(得分:3)
我做的是我使用了PredicateBuilder,它是LinqKit中的一个类,也是AsExpandable()http://www.albahari.com/nutshell/linqkit.aspx来构建表达式,我将它们静态存储为
public readonly Expression<Func<T,bool>>
在静态类中。每个表达式都建立在先前的表达式上,从而减少了重复的数量。
正如马克·格拉维尔(Marc Gravell)之前的问题所暗示的那样,这件事是黑色艺术,但值得庆幸的是,其他一些人已经完成了很多工作。