在Entity Framework Query中使用C#函数

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函数.
    • 但我无法从C#lamda创建UDF,我必须定义SQL,这将更麻烦.
  • 分配Expression<Func<Template,bool>>UserOwnsTemplate定义为一个变量,然后生成相关Expression<Func<DataReturn ,bool>>使用DataReturn版本用手Expression.AndAlso粘上两个"条款"在一起.
    • 元编程.Ughhh.我之前在另一个项目中做过这件事,这样做很卑鄙,而且是一场噩梦.
  • 与重复一起生活.
    • 可能会发生什么,除非SO可以另有建议.;)

任何人都可以看到任何其他选项吗?

我可以做任何事情来强制EF将函数解析为SQL吗?("inling"这个短语浮现在脑海中,但我不知道我认为我的意思是什么?)

任何人都可以看到一种方法将ret.Request.Template转换为IQueryable,以便我可以调用其他WhereIsOwnedBy扩展方法吗?

还有其他任何建议吗?

Rol*_*rgi 0

问题是您的方法成为表达式树的一部分,并且 EF 无法对其求值。原则上,可以在触发查询之前评估表达式树的部分内容。看看 Re-Linq: https: //relinq.codeplex.com/它有一个 PartialEvaluatingExpressionTreeVisitor 类,它可以评估所有部分表达式树,即它将找到您的方法,评估它,并注入实际的表达式树。这将带来一定的性能成本,但可能并不重要,您必须权衡干净的设计与性能。