c# - 使用自定义 ValueConverter 时选择 System.InvalidCastException
问题描述
问题
我正在尝试在我的一个实体中引入强类型 ID。为此,我按照https://andrewlock.net/using-strongly-typed-entity-ids-to-avoid-primitive-obsession-part-3/上的说明进行操作
与上述说明的唯一区别是,我试图将我的 ID 保留为字符串而不是 Guid。
说明的简短摘要(可以在此处下载完整示例:https ://drive.google.com/file/d/1_mtYp1c8W6qJoEAY-8NPNjYvsSAif4mz/view?usp=sharing ):
- 创建一个带有属性“Value”的结构“OrderId”,它返回 ID 的字符串表示形式
- 为 Order 实体创建类并添加类型为“OrderId”的属性“ID”
- 使用 DbSet 创建 DbContext
- 创建一个自定义 ValueConverter,将 OrderId 转换为字符串,反之亦然
- 将值转换器应用于 Order 实体的 Id 列
- 将一个从 OrderId 到字符串的显式转换运算符添加到 OrderId 以避免客户端评估
使用此设置,将条目添加到 Orders 表中没有问题。但是:当我尝试阅读它们时,会引发以下异常:
System.InvalidCastException
HResult=0x80004002
Message=Invalid cast from 'System.String' to 'StronglyTypedIds.OrderId'.
Source=System.Private.CoreLib
StackTrace:
at System.Convert.DefaultToType(IConvertible value, Type targetType, IFormatProvider provider) in E:\A\_work\644\s\src\mscorlib\shared\System\Convert.cs:line 309
at Microsoft.EntityFrameworkCore.Storage.ValueConversion.ValueConverter`2.Sanitize[T](Object value) in /_/src/EFCore/Storage/ValueConversion/ValueConverter`.cs:line 52
at Microsoft.EntityFrameworkCore.Storage.ValueConversion.ValueConverter`2.<>c__DisplayClass3_0`2.<SanitizeConverter>b__0(Object v) in /_/src/EFCore/Storage/ValueConversion/ValueConverter`.cs:line 43
at Microsoft.EntityFrameworkCore.Storage.RelationalTypeMapping.CreateParameter(DbCommand command, String name, Object value, Nullable`1 nullable) in /_/src/EFCore.Relational/Storage/RelationalTypeMapping.cs:line 515
at Microsoft.EntityFrameworkCore.Storage.Internal.TypeMappedRelationalParameter.AddDbParameter(DbCommand command, Object value) in /_/src/EFCore.Relational/Storage/Internal/TypeMappedRelationalParameter.cs:line 66
at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalParameterBase.AddDbParameter(DbCommand command, IReadOnlyDictionary`2 parameterValues) in /_/src/EFCore.Relational/Storage/Internal/RelationalParameterBase.cs:line 45
at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.CreateCommand(IRelationalConnection connection, IReadOnlyDictionary`2 parameterValues) in /_/src/EFCore.Relational/Storage/Internal/RelationalCommand.cs:line 375
at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.Execute(IRelationalConnection connection, DbCommandMethod executeMethod, IReadOnlyDictionary`2 parameterValues) in /_/src/EFCore.Relational/Storage/Internal/RelationalCommand.cs:line 149
at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.ExecuteReader(IRelationalConnection connection, IReadOnlyDictionary`2 parameterValues) in /_/src/EFCore.Relational/Storage/Internal/RelationalCommand.cs:line 119
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.BufferlessMoveNext(DbContext _, Boolean buffer) in /_/src/EFCore.Relational/Query/Internal/QueryingEnumerable.cs:line 111
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.MoveNext() in /_/src/EFCore.Relational/Query/Internal/QueryingEnumerable.cs:line 93
at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable`1 source, Boolean& found) in E:\A\_work\286\s\corefx\src\System.Linq\src\System\Linq\First.cs:line 63
at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ResultEnumerable`1.GetEnumerator() in /_/src/EFCore/Query/Internal/LinqOperatorProvider.cs:line 294
at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.<_TrackEntities>d__17`2.MoveNext() in /_/src/EFCore/Query/Internal/LinqOperatorProvider.cs:line 185
at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext() in /_/src/EFCore/Query/Internal/LinqOperatorProvider.cs:line 143
at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable`1 source, Boolean& found) in E:\A\_work\286\s\corefx\src\System.Linq\src\System\Linq\First.cs:line 63
at System.Linq.Enumerable.First[TSource](IEnumerable`1 source) in E:\A\_work\286\s\corefx\src\System.Linq\src\System\Linq\First.cs:line 14
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass15_1`1.<CompileQueryCore>b__0(QueryContext qc) in /_/src/EFCore/Query/Internal/QueryCompiler.cs:line 132
at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source, Expression`1 predicate) in E:\A\_work\286\s\corefx\src\System.Linq.Queryable\src\System\Linq\Queryable.cs:line 741
at StronglyTypedIds.Program.Main() in <path_to_project>\StronglyTypedIds\StronglyTypedIds\Program.cs:line 29
奇怪的是,它似乎取决于我如何构建我的 Where-query:
var order = dbContext.Orders.Where(o => (string)o.Id == "1").ToList(); // works
const string idAsStringConstant = "1";
order = dbContext.Orders.Where(o => (string)o.Id == idAsStringConstant).ToList(); // works
var idAsStringVariable = "1";
order = dbContext.Orders.Where(o => (string)o.Id == idAsStringVariable).ToList(); // doesn't work
order = dbContext.Orders.Where(o => (string)o.Id == OrderId.Parse("1").StringValue).ToList(); // doesn't work
重现步骤
这是一个带有示例的小型 VS2019 解决方案:https ://drive.google.com/file/d/1_mtYp1c8W6qJoEAY-8NPNjYvsSAif4mz/view?usp=sharing
更多技术细节
EF Core 版本:2.2.6 数据库提供程序:Microsoft.EntityFrameworkCore.SqlServer 和 Microsoft.EntityFrameworkCore.Sqlite 操作系统:Windows 10 1809 IDE:(例如 Visual Studio 2019 16.1.6)
解决方案
好的,这是实体框架中的错误。如果我安装 .NET Core 和 Entity Framework Core 3.0 版的预览版,它会按预期工作。此外,似乎还有一个针对尚未发布的 2.2 版的修复(请参阅https://github.com/aspnet/EntityFrameworkCore/issues/17610)。