我正在搞一个我不了解的InvalidCastException,它与Entity Framework Plus库及其特别是IncludeFilter扩展方法有关。
总而言之,我有3个实体:项目,测试和TestRun:-每个项目都有一组测试; -每个测试都有一个TestRuns集合。我有一个ProjectService类,该类实现了一种从数据库中检索Project的方法,并具有选择所需的选项的选项。这是该方法的代码(我将其分解为仍会产生相同异常的最小代码,再加上另一个可用于比较的代码):
private IQueryable<Project> NewQuery(ProjectIncludeOptions includes = ProjectIncludeOptions.NONE)
{
IQueryable<Project> query = base.NewQuery();
/* NO PROBLEM HERE: just left it for comparison. */
if (includes.HasFlag(ProjectIncludeOptions.DOMAINS))
{
query =
query
.IncludeFilter(p => p.TestDomains.Where(td => !td.IsArchived).Select(td => td.Children.Where(tdc => !tdc.IsArchived)))
.IncludeFilter(p => p.TestDomains.Where(td => !td.IsArchived).Select(td => td.Parent));
}
/* EXCEPTION CAUSED BY THE CODE BELOW */
if (includes.HasFlag(ProjectIncludeOptions.TESTS))
{
query =
query
/* In the below code, if I remove the Where clause, or use a non-calculated property in it, then the exception disappears. */
.IncludeFilter(p => p.Tests.Where(t => !t.IsArchived).Select(t => t.TestRuns));
}
return query;
}
IsArchived属性的实现(在Test类中)如下:
[NotMapped]
public virtual bool IsArchived
{
get { return ArchivingDate.HasValue; }
set { ArchivingDate = value ? System.DateTime.Now : (System.DateTime?)null; }
}
还有我实际得到InvalidCastException的地方(来自SingleOrDefault调用):
Project project = NewQuery(includes).SingleOrDefault(p => p.Id == projectId);
完整的异常消息是:
System.InvalidCastExceptionÂ:'无法将类型为'System.String'的对象转换为类型为'System.Int32'。
错误发生时的堆栈跟踪:
在System.Data.SqlClient.SqlBuffer.get_Int32()在System.Data.SqlClient.SqlDataReader.GetInt32(Int32 i)在lambda_method(Closure,DbDataReader)在Microsoft.EntityFrameworkCore.Storage.Internal.TypedRelationalValueBufferFactory.Create(DbDataReader dataReader)在Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute
1.Enumerator.BufferlessMoveNext(DbContext _, Boolean buffer) at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func
3操作,Func3 verifySucceeded) at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable
1.Enumerator.BufferlessMoveNext(DbContext _, Boolean buffer) at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func
Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable1.Enumerator.BufferlessMoveNext(DbContext _, Boolean buffer) at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func
3 verifySucceeded) at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable
1.Enumerator.MoveNext()的验证2.System.Linq.Enumerable.JoinIterator [CreateForJoin(2.CreateForJoin(IEnumerable
1 source,Func2 keySelector, IEqualityComparer
1比较器) TOuter,TInner,TKey,TResult](IEnumerable1 outer, IEnumerable
1内部,Func2 outerKeySelector, Func
2 innerKeySelector,Func3 resultSelector, IEqualityComparer
1比较器)+ MoveNext()在System.Linq.Enum erable.GroupJoinIterator [TOuter,TInner,TKey,TResult](IEnumerable1 outer, IEnumerable
1内部,Func2 outerKeySelector, Func
2 innerKeySelector,Func3 resultSelector, IEqualityComparer
1比较器)+ MoveNext()在System.Linq.Enumerable.SelectManyIterator [ TSource,TCollection,TResult](IEnumerable1 source, Func
2 collectionSelector,Func3 resultSelector)+MoveNext() at System.Linq.Enumerable.SelectEnumerableIterator
2.MoveNext()在Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider._TrackEntities [TOut ,TIn](IEnumerable1 results, QueryContext queryContext, IList
1个EntityTrackingInfos,IList1 entityAccessors)+MoveNext() at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ExceptionInterceptor
1.EnumeratorExceptionInterceptor.MoveNext()在Z.EntityFramework.Plus.QueryFutureEnumerable1.SetResult(IEnumerator
1枚举器)位于Z.EntityFramework.Plus.QueryFutureEnumerable1.SetResult(DbDataReader reader) at Z.EntityFramework.Plus.QueryFutureBatch.ExecuteQueries() at Z.EntityFramework.Plus.QueryFutureValue
1.get_Value()在Z.EntityFramework.Plus.QueryIncludeFilterProvider1.Execute[TResult](Expression expression) at System.Linq.Queryable.SingleOrDefault[TSource](IQueryable
处执行1.Execute[TResult](Expression expression) at System.Linq.Queryable.SingleOrDefault[TSource](IQueryable
1源,表达式1.Execute[TResult](Expression expression) at System.Linq.Queryable.SingleOrDefault[TSource](IQueryable
1谓词)位于Tresse.Service.Impl.ProjectService.Get(Int32 projectId,包括ProjectIncludeOptions)
最让我感到困惑的是,当我使用ProjectIncludeOptions.DOMAINS部分时,我没有得到任何异常,这显然是通过完全相同的方式实现的(IsArchived属性在TestDomain对象上也相同)。
而且, 我所有的实体 (Project,Test,TestRun和TestDomain) 都具有DateTime属性 ,并且它们似乎在此问题中发挥了作用。确实,如果我将Test和TestRun的所有DateTime属性标记为[NotMapped]
(同时将所有代码保留在ProjectService中,如上所示),则该异常消失!如果我仅保留一个映射的DateTime属性(无论哪个),那么将触发异常。但是,它们似乎不会对TestDomain和上面的代码造成任何类型的问题。
这对你们所有人有意义吗?
我设法通过完全删除IncludeFilter中的WHERE子句来解决这种情况(因为这对我的项目而言并不重要),但是我很乐意至少了解正在发生的事情并找到解决方案。 :)
我不确定这是否正是问题所在,但是IncludeFilter
的目标是生成查询并在数据库端进行过滤。
但是,未映射IsArchived
属性。这意味着不可能创建将在数据库侧执行的查询(由于客户端评估,在EF Core 2.x中可能是可能的)。
确保过滤部分可以全部在数据库中完成。
直接使用ArchivingDate
属性似乎有可能。