【问题标题】:Linq "Could not translate expression... into SQL and could not treat it as a local expression."Linq“无法将表达式...转换为 SQL 并且无法将其视为本地表达式。”
【发布时间】:2010-07-13 13:51:48
【问题描述】:

我从this question 开始,我在某种程度上回答了there,现在我在这里提出更基本的问题。我已将查询简化为:

var q = from ent in LinqUtils.GetTable<Entity>()
        from tel in ent.Telephones.DefaultIfEmpty()
        select new {
          Name = ent.FormattedName,
          Tel = tel != null ? tel.FormattedNumber : "" // this is what causes the error
        };

tel.FormattedNumber 是一个将NumberExtension 字段组合成格式整齐的字符串的属性。这是导致的错误:

System.InvalidOperationException: Could not translate expression 'Table(Entity).SelectMany(ent => ent.Telephones.DefaultIfEmpty(), (ent, tel) => new <>f__AnonymousType0`2(Name = ent.FormattedName, Tel = IIF((tel != null), tel.FormattedNumber, "")))' into SQL and could not treat it as a local expression.

如果我将上面的引用从 FormattedNumber 更改为简单的 Number,一切正常。

但我确实希望格式化后的数字能很好地显示在我的列表中。作为最整洁、最干净的方式,您有什么建议?

【问题讨论】:

    标签: c# linq linq-to-sql


    【解决方案1】:

    您可以在实体上使用AsEnumerable,但这会强制它恢复所有列(即使未使用);也许是这样的:

    var q1 = from ent in LinqUtils.GetTable<Entity>()
             from tel in ent.Telephones.DefaultIfEmpty()
             select new {
               Name = ent.FormattedName,
               Number = (tel == null ? null : ent.Number),
               Extension = (tel == null ? null : ent.Extension)
             };
    
    var q2 = from row in q1.AsEnumerable()
             select new {
                 row.Name,
                 FormattedNumber = FormatNumber(row.Number, row.Extension)
             };
    

    FormatNumber 是一种将两者合并的方法,可能是从您的其他(属性)代码中重复使用的。

    使用 LINQ-to-SQL,另一种选择是在数据上下文中公开一个 UDF,该 UDF 在数据库内部进行格式化;一个稍微不同的例子:

    var qry = from cust in ctx.Customers // and tel
              select new {
                  cust.Name,
                  FormattedNumber = ctx.FormatNumber(tel.Number, tel.Extension)
              };
    

    (它将在数据库中完成工作;无论这是否是个好主意;-p)

    【讨论】:

      【解决方案2】:

      干净的方法是在表达式中声明你真正想要的字段,将它们放在你的中间层对象中,然后使用任何辅助函数来修改它们。

      我不确定您是否意识到代表 LINQ 的 SQL 表的类是 DTO 类 - 它定义了 LINQ-SQL 翻译器使用的语法。甚至不支持将属性注入到未映射到 SQL 表的 DTO 中——这意味着翻译器可以随意触发。属性定义了语法,任何没有被它们定义的东西都不存在于表达式翻译器中。

      在 from 子句中命名的实体不是对象 - 它们只是用于帮助拼写将要获取的实际表字段的符号。未在 select 中明确命名的字段是未获取的字段 - 至少这是翻译器的目标,它可能不得不让一些人溜走。例如,如果那个 ent.FormattedName 没有被声明,那就是一个失误,以后可能会爆炸。

      因此,注入 DTO 类的 FormattedNumber 属性甚至不存在于语法中。它不是“计算字段”——该术语严格用于 SQL 表定义,如果你有一个,它将在 DTO 的语法中。请注意,错误非常准确地说是“局部表达式” - 范围非常有限。

      您可以尝试通过在整个“tel”上调用静态函数的嵌套 lambda 表达式来欺骗它,这可能会触发获取整个记录 - 或引发另一个异常。

      其他不是翻译器的 LINQ-s 可以有宽松的规则。 LINQ-SQL 必须非常严格或非常慢,而且已经足够慢了 :-)

      【讨论】:

        【解决方案3】:

        @Marc Gravell 击败了我,也感谢this question 的各种回答者,他们让我走上了正轨。

        我非常喜欢 Marc 的第一个建议,就像这样:

        var q1 = from ent in LinqUtils.GetTable<Entity>()
                 from tel in ent.Telephones.DefaultIfEmpty()
                 select new { ent, tel };
        var q2 = from q in q1.AsEnumerable()
                 select new {
                   Name = q.ent.FormattedName,
                   Tel = q.tel != null ? q.tel.FormattedNumber : ""
                 };
        

        然后就做到了! 谢谢大家!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2022-01-19
          • 1970-01-01
          • 2015-06-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多