在具有 DateTime 属性 的实体上使用 IncludeFilter 时出现 InvalidCastException”
InvalidCastException when using IncludeFilter on entity with DateTime property"
我遇到了一个我不明白的 InvalidCastException,它与 Entity Framework Plus 库及其 IncludeFilter 扩展方法有关。
总而言之,我有 3 个实体:项目、测试和测试运行:
- 每个项目都有一组测试;
- 每个测试都有一组测试运行。
我有一个 ProjectService class,它实现了一种从数据库中检索项目的方法,其中包含所需的 select 选项。这是此方法的代码(我将其剥离为仍然产生相同异常的最小代码段,以及另一段用于比较的代码):
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属性(在测试class中)的实现如下:
[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 : 'Unable to cast object of type 'System.String' to type 'System.Int32'.'
以及发生错误时的堆栈跟踪:
at System.Data.SqlClient.SqlBuffer.get_Int32()
at System.Data.SqlClient.SqlDataReader.GetInt32(Int32 i)
at lambda_method(Closure , DbDataReader )
at Microsoft.EntityFrameworkCore.Storage.Internal.TypedRelationalValueBufferFactory.Create(DbDataReader dataReader)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable1.Enumerator.BufferlessMoveNext(DbContext _, Boolean buffer)
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func
3 operation, Func3 verifySucceeded)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable
1.Enumerator.MoveNext()
at System.Linq.Lookup2.CreateForJoin(IEnumerable
1 source, Func2 keySelector, IEqualityComparer
1 comparer)
at System.Linq.Enumerable.JoinIterator[TOuter,TInner,TKey,TResult](IEnumerable1 outer, IEnumerable
1 inner, Func2 outerKeySelector, Func
2 innerKeySelector, Func3 resultSelector, IEqualityComparer
1 comparer)+MoveNext()
at System.Linq.Enumerable.GroupJoinIterator[TOuter,TInner,TKey,TResult](IEnumerable1 outer, IEnumerable
1 inner, Func2 outerKeySelector, Func
2 innerKeySelector, Func3 resultSelector, IEqualityComparer
1 comparer)+MoveNext()
at System.Linq.Enumerable.SelectManyIterator[TSource,TCollection,TResult](IEnumerable1 source, Func
2 collectionSelector, Func3 resultSelector)+MoveNext()
at System.Linq.Enumerable.SelectEnumerableIterator
2.MoveNext()
at 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()
at Z.EntityFramework.Plus.QueryFutureEnumerable1.SetResult(IEnumerator
1 enumerator)
at Z.EntityFramework.Plus.QueryFutureEnumerable1.SetResult(DbDataReader reader)
at Z.EntityFramework.Plus.QueryFutureBatch.ExecuteQueries()
at Z.EntityFramework.Plus.QueryFutureValue
1.get_Value()
at Z.EntityFramework.Plus.QueryIncludeFilterProvider1.Execute[TResult](Expression expression)
at System.Linq.Queryable.SingleOrDefault[TSource](IQueryable
1 source, Expression`1 predicate)
at Tresse.Service.Impl.ProjectService.Get(Int32 projectId, ProjectIncludeOptions includes)
最让我困惑的是,当我使用 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
属性.
看起来是可行的
我遇到了一个我不明白的 InvalidCastException,它与 Entity Framework Plus 库及其 IncludeFilter 扩展方法有关。
总而言之,我有 3 个实体:项目、测试和测试运行: - 每个项目都有一组测试; - 每个测试都有一组测试运行。 我有一个 ProjectService class,它实现了一种从数据库中检索项目的方法,其中包含所需的 select 选项。这是此方法的代码(我将其剥离为仍然产生相同异常的最小代码段,以及另一段用于比较的代码):
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属性(在测试class中)的实现如下:
[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 : 'Unable to cast object of type 'System.String' to type 'System.Int32'.'
以及发生错误时的堆栈跟踪:
at System.Data.SqlClient.SqlBuffer.get_Int32() at System.Data.SqlClient.SqlDataReader.GetInt32(Int32 i) at lambda_method(Closure , DbDataReader ) at Microsoft.EntityFrameworkCore.Storage.Internal.TypedRelationalValueBufferFactory.Create(DbDataReader dataReader) 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
3 operation, Func3 verifySucceeded) at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable
1.Enumerator.MoveNext() at System.Linq.Lookup2.CreateForJoin(IEnumerable
1 source, Func2 keySelector, IEqualityComparer
1 comparer) at System.Linq.Enumerable.JoinIterator[TOuter,TInner,TKey,TResult](IEnumerable1 outer, IEnumerable
1 inner, Func2 outerKeySelector, Func
2 innerKeySelector, Func3 resultSelector, IEqualityComparer
1 comparer)+MoveNext() at System.Linq.Enumerable.GroupJoinIterator[TOuter,TInner,TKey,TResult](IEnumerable1 outer, IEnumerable
1 inner, Func2 outerKeySelector, Func
2 innerKeySelector, Func3 resultSelector, IEqualityComparer
1 comparer)+MoveNext() at System.Linq.Enumerable.SelectManyIterator[TSource,TCollection,TResult](IEnumerable1 source, Func
2 collectionSelector, Func3 resultSelector)+MoveNext() at System.Linq.Enumerable.SelectEnumerableIterator
2.MoveNext() at 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() at Z.EntityFramework.Plus.QueryFutureEnumerable1.SetResult(IEnumerator
1 enumerator) at Z.EntityFramework.Plus.QueryFutureEnumerable1.SetResult(DbDataReader reader) at Z.EntityFramework.Plus.QueryFutureBatch.ExecuteQueries() at Z.EntityFramework.Plus.QueryFutureValue
1.get_Value() at Z.EntityFramework.Plus.QueryIncludeFilterProvider1.Execute[TResult](Expression expression) at System.Linq.Queryable.SingleOrDefault[TSource](IQueryable
1 source, Expression`1 predicate) at Tresse.Service.Impl.ProjectService.Get(Int32 projectId, ProjectIncludeOptions includes)
最让我困惑的是,当我使用 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
属性.