.NET6+EF6升级后调用IQueryable CreateQuery报类型不匹配错误
问题描述
升级到.NET 6 + EF Core 6后,调用代码创建查询时抛出类型转换错误,该逻辑在.NET 5 + EF Core 5中可正常运行:
核心代码
获取Provider:
(from p in Context.Set<PointOfInterest>() select p).Provider
创建查询的代码:
var m = typeof(IQueryProvider).GetMethods().First(x => x.Name == "CreateQuery" && x.IsGenericMethod); var gm = m.MakeGenericMethod(queryType); var a = (IQueryable)gm.Invoke(provider, new object[] { expression });
其中queryType为PointOfInterestDto,对应的表达式如下:
.Call System.Linq.Queryable.Where( .Call System.Linq.Queryable.Select( .Extension<Microsoft.EntityFrameworkCore.Query.QueryRootExpression>, '(.Lambda #Lambda1<System.Func`2[BibeCoffee.Logic.PointsOfInterest.Domain.PointOfInterestAggregate.PointOfInterest,BibeCoffee.Logic.PointsOfInterest.DtoModels.PointOfInterestDto]>)) , '(.Lambda #Lambda2<System.Func`2[BibeCoffee.Logic.PointsOfInterest.DtoModels.PointOfInterestDto,System.Boolean]>)) .Lambda #Lambda1<System.Func`2[BibeCoffee.Logic.PointsOfInterest.Domain.PointOfInterestAggregate.PointOfInterest,BibeCoffee.Logic.PointsOfInterest.DtoModels.PointOfInterestDto]>(BibeCoffee.Logic.PointsOfInterest.Domain.PointOfInterestAggregate.PointOfInterest $pointOfInterest) { .New BibeCoffee.Logic.PointsOfInterest.DtoModels.PointOfInterestDto(){ Name = $pointOfInterest.Name, Id = $pointOfInterest.Id, ExtraFields = (System.Collections.Generic.IDictionary`2[System.String,System.Object])$pointOfInterest.ExtraFields } } .Lambda #Lambda2<System.Func`2[BibeCoffee.Logic.PointsOfInterest.DtoModels.PointOfInterestDto,System.Boolean]>(BibeCoffee.Logic.PointsOfInterest.DtoModels.PointOfInterestDto $o) { .Call System.Linq.Enumerable.Any( (System.Collections.Generic.IEnumerable`1[System.Object]).Call ($o.ExtraFields).get_Item("coffeeChannels"), .Lambda #Lambda3<System.Func`2[System.Object,System.Boolean]>) } .Lambda #Lambda3<System.Func`2[System.Object,System.Boolean]>(System.Object $o) { .Call ((System.Collections.Generic.IDictionary`2[System.String,System.Object])$o).ContainsKey("id") && (System.Guid).Call ((System.Collections.Generic.IDictionary`2[System.String,System.Object])$o).get_Item("id") == .Constant<System.Guid>(d5471682-df51-480b-a367-35b7a918f172) }
错误信息
Expression of type 'System.Object' cannot be used for parameter of type 'System.Collections.Generic.IEnumerable`1[System.Object]' of method 'System.Linq.IQueryable`1[System.Object] AsQueryable[Object](System.Collections.Generic.IEnumerable`1[System.Object])' (Parameter 'arg0')
注:ExtraFields是IDictionary<string, object>类型,coffeeChannels是其中的数组类型,需要对该数组进行查询。
解决方案
1. 显式声明类型转换表达式
EF Core 6对表达式的类型检查更严格,原表达式中直接强制转换get_Item结果的方式不再兼容。需使用Expression.Convert生成合法的类型转换节点:
// 构建获取coffeeChannels的表达式 var oParam = Expression.Parameter(typeof(PointOfInterestDto), "o"); var coffeeChannelsExpr = Expression.PropertyOrField(oParam, "ExtraFields"); coffeeChannelsExpr = Expression.Call(coffeeChannelsExpr, "get_Item", null, Expression.Constant("coffeeChannels")); // 显式转换为IEnumerable<object> var enumerableExpr = Expression.Convert(coffeeChannelsExpr, typeof(IEnumerable<object>)); // 构建Lambda3 var itemParam = Expression.Parameter(typeof(object), "item"); var dictItemExpr = Expression.Convert(itemParam, typeof(IDictionary<string, object>)); var containsKeyCall = Expression.Call(dictItemExpr, nameof(IDictionary<string, object>.ContainsKey), null, Expression.Constant("id")); var idValueExpr = Expression.Call(dictItemExpr, "get_Item", null, Expression.Constant("id")); var idGuidExpr = Expression.Convert(idValueExpr, typeof(Guid)); var equalsCall = Expression.Equal(idGuidExpr, Expression.Constant(Guid.Parse("d5471682-df51-480b-a367-35b7a918f172"))); var lambda3 = Expression.Lambda<Func<object, bool>>(Expression.AndAlso(containsKeyCall, equalsCall), itemParam); // 构建Any调用 var anyCall = Expression.Call( typeof(Enumerable), nameof(Enumerable.Any), new[] { typeof(object) }, enumerableExpr, lambda3 ); var lambda2 = Expression.Lambda<Func<PointOfInterestDto, bool>>(anyCall, oParam);
2. 避免反射调用CreateQuery的不规范写法
直接反射调用容易忽略EF Core内部处理逻辑,建议改用泛型方法调用:
// 替代原反射代码的写法 var query = provider.CreateQuery<PointOfInterestDto>(expression); // 或保留反射但更精准的调用 var createQueryMethod = typeof(IQueryProvider).GetMethod(nameof(IQueryProvider.CreateQuery)) .MakeGenericMethod(queryType); var query = (IQueryable)createQueryMethod.Invoke(provider, new object[] { expression });
3. 使用EF Core原生查询逻辑重构
直接通过LINQ查询让EF Core自动处理类型转换和翻译,避免手动构建复杂表达式:
var targetGuid = Guid.Parse("d5471682-df51-480b-a367-35b7a918f172"); var query = Context.Set<PointOfInterest>() .Select(p => new PointOfInterestDto { Id = p.Id, Name = p.Name, ExtraFields = p.ExtraFields }) .Where(dto => ((IEnumerable<object>)dto.ExtraFields["coffeeChannels"]) .Any(item => { var itemDict = item as IDictionary<string, object>; return itemDict != null && itemDict.ContainsKey("id") && (Guid)itemDict["id"] == targetGuid; }));
4. 适配EF Core 6的类型转换规则
EF Core 6对object类型的隐式转换处理更严格,所有跨类型转换必须通过Expression.Convert显式声明,不能仅在表达式字符串中写强制转换语法。
内容的提问来源于stack exchange,提问作者SpyrosT
相关产品推荐
相关产品推荐

