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

我从这个问题开始,我在那里回答,现在我在这里问更基本的问题。 我已将查询简化为:

var q = from ent in LinqUtils.GetTable() 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 ,一切正常。

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

您可以在实体上使用AsEnumerable ,但这会强制它带回所有列(即使未使用); 或许改为:

 var q1 = from ent in LinqUtils.GetTable() 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)

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

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

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

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

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

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

@Marc Gravell打败了我的答案,也归功于这个让我走上正轨的问题的各种回答者。

我做的很像马克的第一个建议,就像这样:

 var q1 = from ent in LinqUtils.GetTable() 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 : "" }; 

那就做到了! 谢谢,全部!