【问题标题】:Convert Method to Linq Expression for query将方法转换为 Linq 表达式以进行查询
【发布时间】:2015-03-12 13:05:30
【问题描述】:

在我们的应用程序中,我们希望为数据库中的各种条件提供标准方法。例如,我们有不同类型的事务,我们想创建标准方法来在其他查询中检索它们。但是,这给了我们错误:

方法 '' 没有支持的 SQL 转换

该方法可能如下所示:

public static bool IsDividend(this TransactionLog tl)
{
    return tl.SourceTypeID == (int)JobType.Dividend || tl.SourceTypeID == (int)JobType.DividendAcct;
}

这样使用:

var dividends = ctx.TransactionLogs.Where(x => x.IsDividend());

当然,如果我将 IsDividend() 中的逻辑复制到 Where 子句中,这可以正常工作,但我最终会在很多地方复制此逻辑,并且如果该逻辑发生更改,则很难追踪。

我认为如果我将其转换为这样的表达式,它会起作用,但这不如能够使用方法的设置更可取:

public Expression<Func<TransactionLog, bool>> IsDividend = tl => tl.SourceTypeID == (int)JobType.Dividend || tl.SourceTypeID == (int)JobType.DividendAcct;

var dividends = ctx.TransactionLogs.Where(IsDividend);

有没有办法强制 Linq 将方法评估为表达式?或者将方法调用“转换”为 linq 查询中的表达式?像这样的:

var dividends = ctx.TransactionLogs.Where(tl => ToExpression(tl.IsDividend));

我们在应用程序中使用 Linq-to-SQL。

【问题讨论】:

  • 表达式可以很容易地转换为方法(这就是它们的设计目的),但相反的操作是相当于反编译的任务。

标签: c# linq linq-to-sql expression-trees


【解决方案1】:

拥有包含表达式的静态属性对我来说似乎很好。

使它与方法一起工作的唯一方法是创建一个返回此表达式的方法,然后在其中调用它:

public class TransactionLog 
{
    Expression<Func<TransactionLog, bool>> IsDividend() {
        Expression<Func<TransactionLog, bool>> expression = tl => tl.SourceTypeID == (int)JobType.Dividend || tl.SourceTypeID == (int)JobType.DividendAcct;
        return expression;
    }
}

public class TransactionLogExtension
{
    Expression<Func<TransactionLog, bool>> IsDividend(this TransactionLog log) {
        Expression<Func<TransactionLog, bool>> expression = tl => tl.SourceTypeID == (int)JobType.Dividend || tl.SourceTypeID == (int)JobType.DividendAcct;
        return expression;
    }
}

并通过

使用它
var dividends = ctx.TransactionLogs.Where(TransactionLog.IsDividend());

或作为扩展方法

var dividends = ctx.TransactionLogs.Where(x.IsDividend());

但它们都不适用于var dividends = ctx.TransactionLogs.Where(x =&gt; x.IsDividend());,因为x =&gt; x.IsDividend(); 本身是一个表达式树,您的数据库提供程序无法将“IsDividend”转换为 SQL 语句。

但其他两个选项至少允许您传入参数(如果您将表达式存储为实例或静态属性,这将不起作用)。

【讨论】:

    【解决方案2】:

    我认为 LINQ to SQL 甚至不完全支持常用函数和内置函数。 (至少 EF 不这样做)。而且 - 当它处理用户定义的方法时。我预测,除非您在枚举(ToList 或类似方法)之后调用它,否则您的带有表达式的变体以及带有方法调用的变体都会下降。我建议在 1) 服务器上的存储过程、2) C# 中 Where 子句中硬编码的常见条件、3) C# 中的表达式树生成之间保持平衡。当然,所有这些点都相对复杂(在我看来,没有简单而通用的解决方案)。

    【讨论】:

      【解决方案3】:

      尝试使用扩展方法,如下所示:

      public static class TransactionLogExtensions {
          public static IQueryable<TransactionLog> OnlyDividends(this IQueryable<TransactionLog> tl)
          {
              return tl.Where(t=>t.SourceTypeID == (int)JobType.Dividend || t.SourceTypeID == (int)JobType.DividendAcct);
          }
      }
      

      这样称呼它:

      var dividends=db.TransactionLogs.OnlyDividends();
      

      var dividends=db.TransactionLogs.OnlyDividends().OrderBy(...);
      

      【讨论】:

      • 另一种选择是创建扩展方法来创建数据的虚拟视图。使用您的 ctx 对象,并在其上创建一个仅返回股息的扩展方法... public static class ctxobjectypeExtension { public static IQueryable&lt;TransactionLog&gt; Dividends(this ctxobjecttype ctx) { return ctx.TransactionLogs.Where(tl=&gt;tl.SourceTypeID == (int)JobType.Dividend || tl.SourceTypeID == (int)JobType.DividendAcct)); }} 称为 var dividends=ctx.Dividends();
      【解决方案4】:

      对于这种情况,您可以使用 Func。 Linq 非常适合这些。 这是在 Linq 查询中使用 Func 的简单示例。随意修改和使用。

      Func<int,bool> isDivident = x => 3==x;
      int[] values = { 3, 7, 10 };
      var result = values.Select (isDivident );
      

      【讨论】:

      • 这将无法将查询转换为 SQL,因为问题正在询问。
      • 没错,直接针对 SQL 不会,但是当您执行 select ToList() 时,它可以完美运行,并且与其他可能的解决方案具有相同的性能影响。
      • 不,它不能完美运行。它根本不会在数据库中执行查询。它的性能明显更差,因为这意味着将数据库的全部内容拉到内存中,然后在内存中对其进行操作,结果是它很可能根本不起作用(那里可能内存不够,可能从数据库中提取的数据太多,很容易导致操作超时等)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-19
      相关资源
      最近更新 更多