You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 17:44:50