我正在搞一個我不了解的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
屬性似乎有可能。