C# 7.0 Value Tuple编译与运行时错误求助
解决表达式树中使用ValueTuple的编译/运行时错误
这个问题我之前也碰到过,核心原因是:当你针对IQueryable(比如EF的查询)调用Select时,代码会被转换为表达式树,而表达式树对ValueTuple的处理逻辑和普通LINQ to Objects(IEnumerable)完全不同,这就是你遇到两个错误的根源。
为什么之前的写法会报错?
- 第一个错误:
'An expression tree may not contain a tuple literal.'
表达式树的语法节点体系早期并没有专门支持元组字面量的类型,所以直接用(x.Title, x.Author)这种语法在IQueryable.Select里不被允许。 - 第二个错误:
Cannot resolve method Void .ctor(System.String, System.String)...
直接实例化ValueTuple<string, string>时,因为它是泛型结构体,旧版本的EF(或其他ORM)在解析表达式树中的泛型构造函数调用时会出现类型识别问题,无法正确匹配泛型参数对应的构造方法。
有效的解决方案
方案1:先用匿名类,再转换为元组(最推荐)
这是兼容性最好的方式,EF对匿名类的支持非常完善。我们可以先在表达式树阶段用匿名类做投影,再通过AsEnumerable()切换到内存中的LINQ to Objects环境,之后再转换为元组:
// 先通过IQueryable投影匿名类,再转成IEnumerable处理元组 var posts = iPostService.GetAll() .Select(x => new { x.Title, x.Author }) .AsEnumerable() .Select(x => (x.Title, x.Author));
解释:AsEnumerable()之后的代码不再走数据库查询,而是在内存中执行,这时候元组字面量就完全被支持了。
方案2:用静态方法包装元组创建
如果必须在表达式树阶段就生成元组,可以创建一个静态方法来封装元组的创建逻辑——表达式树对静态方法调用的支持是完善的:
// 先定义一个静态辅助类 public static class TupleCreator { public static (string Title, string Author) CreatePostTuple(string title, string author) { return (title, author); } } // 然后在查询中调用这个方法 var posts = iPostService.GetAll() .Select(x => TupleCreator.CreatePostTuple(x.Title, x.Author));
这样EF会把这个静态方法调用解析为表达式树的一部分,不会出现构造函数解析失败的问题。
方案3:升级ORM版本(如果可行)
如果你使用的是EF6或更早的版本,ValueTuple在表达式树中的支持确实有限。升级到EF Core 3.0+的版本后,官方已经完善了对表达式树中元组的支持,这时候直接使用Select(x => (x.Title, x.Author))就能正常编译和运行了。
内容的提问来源于stack exchange,提问作者Tan Sang
相关产品推荐
相关产品推荐

