Rei*_*l-- 8 c# sql-server entity-framework function
在我的C#代码中,我有2个WHERE查询,我可以在IQueryable上调用它们,并将整个事务编译成SQL,并且这两个查询都有很多共同的逻辑.
我相信这不是这个类似问题的重复: 在实体框架查询的选择子句中使用函数,因为在我的场景中,有问题的函数可以转换为SQL - EF只是没有意识到它可以这样做.
查询大约是:
public static IQueryable<Template> WhereIsOwnedByUser(this IQueryable<Template> set, User user)
{
return set.Where(temp =>
temp.Requests
.Where(req => req.WasSent)
.OrderByDescending(req => req.DueDate)
.Take(2)
.SelectMany(req => req.RequestRecipients.Select(reqRecip => reqRecip.Recipient.Id))
.Contains(user.Id));
}
Run Code Online (Sandbox Code Playgroud)
和
public static IQueryable<Template> WhereIsOwnedByUser(this IQueryable<DataReturn> set, User user)
{
return set.Where(ret=>
ret.Entity.Id == user.Entity.Id
&&
ret.Request.Template.Requests
.Where(req => req.WasSent)
.OrderByDescending(req => req.DueDate)
.Take(2)
.SelectMany(req => req.RequestRecipients.Select(reqRecip => reqRecip.Recipient.Id))
.Contains(user.Id));
}
Run Code Online (Sandbox Code Playgroud)
因此,"拥有模板"的基本BusinessLogic规则,然后是"拥有DataReturn,如果公司匹配AND拥有模板"的必然结果
正如您所看到的,只考虑C#,这些可以很容易地重构为:
private static bool UserOwnsTemplate(User user, Template temp)
{
return temp.Requests
.Where(req => req.WasSent)
.OrderByDescending(req => req.DueDate)
.Take(2)
.SelectMany(req => req.RequestRecipients.Select(reqRecip => reqRecip.Recipient.Id))
.Contains(user.Id);
}
public static IQueryable<Template> WhereIsOwnedByUser(this IQueryable<Template> set, User user)
{
return set.Where(temp => UserOwnsTemplate(user, temp));
}
public static IQueryable<DataReturn> WhereIsOwnedByUser(this IQueryable<DataReturn> set, User user)
{
return set.Where(
ret =>
ret.Entity.Id == user.Entity.Id
&&
UserOwnsTemplate(user, ret.Request.Template)
);
}
Run Code Online (Sandbox Code Playgroud)
从而减少重复(耶!)
但是 EF会抱怨它不知道该怎么做UserOwnsTemplate
,尽管它可以很好地处理SQL中的逻辑.
AFAICT没有很好的方法来解决这个问题.我认为我的选择是:
UserOwnsTemplate
成UDF,在数据库中定义的SQL函数.
Expression<Func<Template,bool>>
其UserOwnsTemplate
定义为一个变量,然后生成相关Expression<Func<DataReturn ,bool>>
使用DataReturn版本用手Expression.AndAlso
粘上两个"条款"在一起.
任何人都可以看到任何其他选项吗?
我可以做任何事情来强制EF将函数解析为SQL吗?("inling"这个短语浮现在脑海中,但我不知道我认为我的意思是什么?)
任何人都可以看到一种方法将ret.Request.Template转换为IQueryable,以便我可以调用其他WhereIsOwnedBy扩展方法吗?
还有其他任何建议吗?
问题是您的方法成为表达式树的一部分,并且 EF 无法对其求值。原则上,可以在触发查询之前评估表达式树的部分内容。看看 Re-Linq: https: //relinq.codeplex.com/它有一个 PartialEvaluatingExpressionTreeVisitor 类,它可以评估所有部分表达式树,即它将找到您的方法,评估它,并注入实际的表达式树。这将带来一定的性能成本,但可能并不重要,您必须权衡干净的设计与性能。