Linq“无法将表达式转换为SQL,无法将其视为本地表达式。”

时间:2009-08-12 08:45:55

标签: c# linq linq-to-sql

我从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,那么一切正常。

但我确实希望格式化的数字在我的列表中很好地显示。你推荐什么作为最干净,最干净的方式?

3 个答案:

答案 0 :(得分:12)

您可以在实体上使用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)

答案 1 :(得分:3)

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

我不确定你是否意识到代表LINQ的SQL表的类是一个DTO类 - 它定义了LINQ-SQL转换器使用的语法。甚至不支持将属性注入到未映射到SQL表的DTO中 - 这意味着翻译器可以随意触发。属性定义语法,并且表达式转换器不存在任何未定义的语法。

from子句中指定的实体不是对象 - 它们只是用于帮助拼写将要获取的实际表字段的符号。未在select中明确命名的字段是未获取的字段 - 至少这是翻译器的目标,它可能必须让一些漏掉。例如,如果没有声明ent.FormattedName,那就是一个漏洞并且可以爆炸后者。

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

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

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

答案 2 :(得分:1)

@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 : ""
         };

就这样做了! 谢谢,全部!