.NET 8升级后EventFlow ReadModelByIdQuery数据库数据解析失败
.NET 8升级后EventFlow读取ReadModel时SqlGuidCaster加载失败问题
基于EventFlow的WebApi在.NET Core 2.1环境运行正常,升级至.NET 8后调用ReadModelByIdQuery时抛出InvalidOperationException,提示解析CorrelationId列失败,深层原因是无法加载System.Data.SqlClient程序集中的SqlGuidCaster类型,该类型存在字段对齐或重叠错误。
异常信息
Exception thrown: 'System.InvalidOperationException' in System.Private.CoreLib.dll Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware: Error: An unhandled exception has occurred while executing the request. System.InvalidOperationException: Error parsing column 6 (CorrelationId=CH - String) ---> System.TypeLoadException: Could not load type 'SqlGuidCaster' from assembly 'System.Data.SqlClient, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' because it contains an object field at offset 0 that is incorrectly aligned or overlapped by a non-object field. at System.Data.SqlTypes.SqlTypeWorkarounds.SqlGuidCtor(Byte[] value, Boolean ignored) at System.Data.SqlClient.TdsParser.TryReadSqlValueInternal(SqlBuffer value, Byte tdsType, Int32 length, TdsParserStateObject stateObj) at System.Data.SqlClient.TdsParser.TryReadSqlValue(SqlBuffer value, SqlMetaDataPriv md, Int32 length, TdsParserStateObject stateObj) at System.Data.SqlClient.SqlDataReader.TryReadColumnInternal(Int32 i, Boolean readHeaderOnly) at System.Data.SqlClient.SqlDataReader.TryReadColumn(Int32 i, Boolean setTimeout, Boolean allowPartiallyReadColumn) at System.Data.SqlClient.SqlDataReader.GetValueInternal(Int32 i) at System.Data.SqlClient.SqlDataReader.GetValue(Int32 i) at System.Data.SqlClient.SqlDataReader.get_Item(Int32 i) at Deserialize5ef26d5d-2eb7-4a7d-9c32-0119a057d718(IDataReader) --- End of inner exception stack trace --- at Dapper.SqlMapper.ThrowDataException(Exception ex, Int32 index, IDataReader reader, Object value) at Deserialize5ef26d5d-2eb7-4a7d-9c32-0119a057d718(IDataReader) at Dapper.SqlMapper.QueryAsync[T](IDbConnection cnn, Type effectiveType, CommandDefinition command) at EventFlow.Sql.Connections.SqlConnection`3.<>c__DisplayClass16_0`1.<<WithConnectionAsync>b__0>d.MoveNext() --- End of stack trace from previous location --- at EventFlow.Core.TransientFaultHandler`1.TryAsync[T](Func`2 action, Label label, CancellationToken cancellationToken) at EventFlow.Sql.Connections.SqlConnection`3.QueryAsync[TResult](Label label, CancellationToken cancellationToken, String sql, Object param) at EventFlow.MsSql.ReadStores.MssqlReadModelStore`1.GetAsync(String id, CancellationToken cancellationToken) at EventFlow.Queries.ReadModelByIdQueryHandler`2.ExecuteQueryAsync(ReadModelByIdQuery`1 query, CancellationToken cancellationToken) at EventFlow.Queries.QueryProcessor.ProcessAsync[TResult](IQuery`1 query, CancellationToken cancellationToken) at WebApplicationforSG.Controllers.DictionaryController.GetById(String id) at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
Program.cs配置
builder.Services.AddEventFlow(opt => { opt.AddAspNetCore(); opt.Configure(c => { c.ThrowSubscriberExceptions = true; c.IsAsynchronousSubscribersEnabled = true; }); opt.ConfigureMsSql(MsSqlConfiguration.New .SetConnectionString(connectionString) .SetTransientRetryCount(2)); opt.AddDefaults(Assembly.GetExecutingAssembly()); opt.UseConsoleLog(); opt.UseMssqlEventStore(); opt.UseMssqlReadModel<DictionaryEntryReadModel>(); opt.RegisterServices(r => { r.RegisterGeneric(typeof(ISearchableReadModelStore<>), typeof(EfSearchableReadStore<>)); r.Register(x => new EmployeeVerificationRequestReadModelLocator()); }); });
ReadModel类定义
public class DictionaryEntryReadModel : IReadModel, IAmReadModelFor<DictionaryEntryAggregate, DictionaryEntryId, DictionaryEntryCreatedEvent>, IAmReadModelFor<DictionaryEntryAggregate, DictionaryEntryId, DictionaryEntryRemovedEvent>, IAmReadModelFor<DictionaryEntryAggregate, DictionaryEntryId, DictionaryEntryEditedEvent> { [MsSqlReadModelIdentityColumn] [DatabaseGenerated(DatabaseGeneratedOption.None)] [Key] [MaxLength(52)] public string AggregateId { get; set; } [MsSqlReadModelVersionColumn] public int Version { get; set; } public string CustomId { get; set; } public DictionaryType EntryType { get; set; } public string EntryValue { get; set; } public string EntryRegExp { get; set; } public Guid CorrelationId { get; set; } public void Apply(IReadModelContext context, IDomainEvent<DictionaryEntryAggregate, DictionaryEntryId, DictionaryEntryCreatedEvent> domainEvent) { AggregateId = domainEvent.AggregateIdentity.Value; CustomId = domainEvent.AggregateEvent.CustomId; EntryType = domainEvent.AggregateEvent.EntryType; EntryValue = domainEvent.AggregateEvent.EntryValue; EntryRegExp = domainEvent.AggregateEvent.EntryRegExp; CorrelationId = domainEvent.AggregateEvent.CorrelationId; } public void Apply(IReadModelContext context, IDomainEvent<DictionaryEntryAggregate, DictionaryEntryId, DictionaryEntryRemovedEvent> domainEvent) { context.MarkForDeletion(); } public void Apply(IReadModelContext context, IDomainEvent<DictionaryEntryAggregate, DictionaryEntryId, DictionaryEntryEditedEvent> domainEvent) { CustomId = domainEvent.AggregateEvent.CustomId; EntryValue = domainEvent.AggregateEvent.EntryValue; EntryRegExp = domainEvent.AggregateEvent.EntryRegExp; } }
数据库表结构
TABLE [dbo].[ReadModel-DictionaryEntry]( [AggregateId] [nvarchar](52) NOT NULL, [Version] [int] NOT NULL, [EntryType] [int] NOT NULL, [EntryValue] [nvarchar](max) NULL, [EntryRegExp] [nvarchar](max) NULL, [CustomId] [nvarchar](max) NULL, [CorrelationId] [uniqueidentifier] NOT NULL, [TimeStamp] [timestamp] NULL,
项目依赖配置(csproj)
<ItemGroup> <PackageReference Include="EventFlow.DependencyInjection" Version="0.83.4713"/> <PackageReference Include="EventFlow.AspNetCore" Version="0.83.4713"/> <PackageReference Include="EventFlow.MsSql" Version="0.83.4713"/> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.2"/> </ItemGroup>
解决方案
1. 替换SQL客户端库
旧版System.Data.SqlClient在.NET 8下存在兼容性问题,强制切换为Microsoft.Data.SqlClient:
在csproj中添加以下配置:
<ItemGroup> <PackageReference Include="Microsoft.Data.SqlClient" Version="5.1.5" /> </ItemGroup> <ItemGroup> <RuntimeHostConfigurationOption Include="System.Data.SqlClient.UseMicrosoftDataSqlClient" Value="true" /> </ItemGroup>
2. 升级EventFlow版本
当前使用的EventFlow 0.83.4713版本对.NET 8支持有限,升级至NuGet上的最新稳定版,新版本通常会修复跨.NET版本的兼容性问题。
3. 手动指定Dapper类型映射
若上述方法无效,添加自定义Dapper类型处理器绕过SqlGuidCaster:
在Program.cs的初始化代码中添加:
Dapper.SqlMapper.AddTypeHandler(new GuidTypeHandler()); public class GuidTypeHandler : Dapper.SqlMapper.TypeHandler<Guid> { public override Guid Parse(object value) { return Guid.Parse(value.ToString()); } public override void SetValue(System.Data.IDbDataParameter parameter, Guid value) { parameter.Value = value; } }
4. 检查数据库数据合法性
异常提示CorrelationId=CH - String,说明数据库中该列存在非Guid格式的无效数据,排查并修复这些值,确保所有CorrelationId都是合法的uniqueidentifier类型。
内容的提问来源于stack exchange,提问作者Daniel K
相关产品推荐
相关产品推荐

