【发布时间】:2020-06-05 01:39:06
【问题描述】:
我尝试使用 x => set.Contains(x.attr) 等集合中的过滤器构建动态表达式,并在 EF Core 3.1 中使用下面的 .ToSQL() 方法获取生成的 SQL。
但它会抛出一个InvalidCastException:
异常:System.InvalidCastException:无法转换类型为“System.Collections.Generic.List
1[System.Int32]' to type 'System.Collections.Generic.IEnumerable1[System.Object]”的对象。在 Microsoft.EntityFrameworkCore.Query.QuerySqlGenerator.VisitIn(InExpression inExpression)
在 Microsoft.EntityFrameworkCore.Query.SqlExpressionVisitor.VisitExtension(Expression extensionExpression)
在 System.Linq.Expressions.Expression.Accept(ExpressionVisitor 访问者)
在 Microsoft.EntityFrameworkCore.Query.QuerySqlGenerator.VisitSqlBinary(SqlBinaryExpression sqlBinaryExpression)
在 Microsoft.EntityFrameworkCore.Query.SqlExpressionVisitor.VisitExtension(Expression extensionExpression)
在 System.Linq.Expressions.Expression.Accept(ExpressionVisitor 访问者)
在 Microsoft.EntityFrameworkCore.Query.QuerySqlGenerator.VisitSelect(SelectExpression selectExpression)
在 Microsoft.EntityFrameworkCore.Query.QuerySqlGenerator.GetCommand(SelectExpression selectExpression)
在 Microsoft.EcoManager.Domain.Core.QueryableExtensions.ToSql[TEntity](IQueryable`1 查询)
这是我构建过滤器集动态表达式的代码:
private static Expression<Func<TData, bool>> CreateSetFilterExpression<TData, TProperty>(string property, IEnumerable<TProperty> values)
{
var type = typeof(TData);
var arg = Expression.Parameter(type, "x");
var propertyInfo = type.GetProperty(property);
Expression exp = Expression.Property(arg, propertyInfo);
exp = Expression.Convert(exp, typeof(TProperty));
var methodInfo = typeof(Enumerable)
.GetMethods()
.Single(x => x.Name == nameof(Enumerable.Contains) &&
x.IsGenericMethodDefinition &&
x.GetGenericArguments().Length == 1 &&
x.GetParameters().Length == 2)
.MakeGenericMethod(typeof(TProperty));
var valuesExpr = Expression.Constant(values);
exp = Expression.Call(null, methodInfo, valuesExpr, exp);
var resultLambda = Expression.Lambda<Func<TData, bool>>(exp, arg);
return resultLambda;
}
似乎与EF Core中QuerySqlGenerator的VisitIn方法有关:
https://github.com/dotnet/efcore/blob/2e8ef3516d2bed2f934eea6e2cb92f7a9ff40ab3/src/EFCore.Relational/Query/QuerySqlGenerator.cs#L624
例如,当我尝试使用CreateSetFilterExpression<SomeEntity, int>(SomePropertyName, values) 构造过滤器集动态表达式时,值的类型是List<int>。执行到sqlGenerator.GetCommand in ToSQL() 行时会抛出InvalidCastException:
异常:System.InvalidCastException:无法转换类型为“System.Collections.Generic.List
1[System.Int32]' to type 'System.Collections.Generic.IEnumerable1[System.Object]”的对象。在 Microsoft.EntityFrameworkCore.Query.QuerySqlGenerator.VisitIn(InExpression inExpression)
...
我搜索了 int cast to object,并检查了 C# 中的装箱和拆箱概念,但我仍然不知道如何解决它。
有人遇到过同样的问题吗?
如果有关于这个问题的任何线索,请帮助我?
这里是.ToSql()方法的代码:
public static string ToSql<TEntity>(this IQueryable<TEntity> query) where TEntity : class
{
var enumerator = query.Provider.Execute<IEnumerable<TEntity>>(query.Expression).GetEnumerator();
var relationalCommandCache = enumerator.Private("_relationalCommandCache");
var selectExpression = relationalCommandCache.Private<SelectExpression>("_selectExpression");
var factory = relationalCommandCache.Private<IQuerySqlGeneratorFactory>("_querySqlGeneratorFactory");
var sqlGenerator = factory.Create();
var command = sqlGenerator.GetCommand(selectExpression);
string sql = command.CommandText;
return sql;
}
private static object Private(this object obj, string privateField) => obj?.GetType().GetField(privateField, BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(obj);
private static T Private<T>(this object obj, string privateField) => (T)obj?.GetType().GetField(privateField, BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(obj);
【问题讨论】:
标签: c# sql-server ef-core-3.1