You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 23:45:57