在我的C#代码中,我有两个WHERE查询,我可以在IQueryable上调用它们,并将整个过程编译成SQL,这两个查询都有很多共同的逻辑。
我认为这不是与此相似问题的重复: Using Function in Select Clause of Entity Framework Query,因为在我的情况下,所涉及的函数可以转换为SQL - EF只是没有意识到它可以这样做。
这些查询大概是:
因此可以减少重复(太棒了!),但是EF会抱怨它不知道如何处理UserOwnsTemplate,尽管它可以在SQL中完美地处理逻辑。
据我所知,没有什么好的解决方法。我认为我的选择有:
- 将UserOwnsTemplate转换为UDF,即在数据库中定义的SQL函数。 - 但是我不能从C# lambda创建UDF,我必须定义SQL,这将更加麻烦。 - 将Expression>由UserOwnsTemplate定义的分配为变量,然后手动构建与DataReturn版本相关的Expression>,使用Expression.AndAlso将两个“子句”粘合在一起。 - 元编程。呕吐。我以前在另一个项目中做过这个,那是恶心的,而且难以维护。 - 接受重复。 - 除非SO能提供其他建议,否则可能会发生这种情况。;) 你们有其他可用的选项吗?
我能做些什么来强制EF解析函数成SQL?(我想到了“内联”这个词,但我不确定我对它的理解是否正确?)
有人能想到一种将ret.Request.Template转换为IQueryable的方法,以便我只需在其上调用另一个WhereIsOwnedBy扩展方法吗?
还有任何其他建议吗?
我认为这不是与此相似问题的重复: Using Function in Select Clause of Entity Framework Query,因为在我的情况下,所涉及的函数可以转换为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));
}
AND
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));
}
因此,"拥有模板"的基本业务逻辑规则和针对公司匹配且拥有模板的"拥有DataReturn"的推论可以很容易地在C#中进行重构:
如您所见,仅考虑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)
);
}
因此可以减少重复(太棒了!),但是EF会抱怨它不知道如何处理UserOwnsTemplate,尽管它可以在SQL中完美地处理逻辑。
据我所知,没有什么好的解决方法。我认为我的选择有:
- 将UserOwnsTemplate转换为UDF,即在数据库中定义的SQL函数。 - 但是我不能从C# lambda创建UDF,我必须定义SQL,这将更加麻烦。 - 将Expression>由UserOwnsTemplate定义的分配为变量,然后手动构建与DataReturn版本相关的Expression>,使用Expression.AndAlso将两个“子句”粘合在一起。 - 元编程。呕吐。我以前在另一个项目中做过这个,那是恶心的,而且难以维护。 - 接受重复。 - 除非SO能提供其他建议,否则可能会发生这种情况。;) 你们有其他可用的选项吗?
我能做些什么来强制EF解析函数成SQL?(我想到了“内联”这个词,但我不确定我对它的理解是否正确?)
有人能想到一种将ret.Request.Template转换为IQueryable的方法,以便我只需在其上调用另一个WhereIsOwnedBy扩展方法吗?
还有任何其他建议吗?
set.Where(expression).Where(anotherExpression)
。困难在于您的表达式取决于用户,并且您想将其应用于与IQueryable(temp vs. ret.Request.Template)的根路径不同的路径。我无法看出如何解决这些问题而不构造自己的表达式。可能还有其他的解决办法,但我预计这将涉及到相当大量对你所做查询的更改。 - hgcummings