我从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
是一个将Number
和Extension
字段组合成一个整齐格式化字符串的属性。这是导致的错误:
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
,那么一切正常。
但我确实希望格式化的数字在我的列表中很好地显示。你推荐什么作为最干净,最干净的方式?
答案 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 : ""
};
就这样做了! 谢谢,全部!