首页 > 解决方案 > 如何使用 Mapster 映射 JsonPatchDocument?

问题描述

我有我的模型:

public class Membership  
{
    [Key]
    [Required]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [Required]
    [MinLength(3)]
    [MaxLength(30)]
    public string Name { get; set; }

    [Required] public int PeriodType { get; set; }
    
    [Required] public int Duration { get; set; }
    
    [Required] public int TerminationPeriod { get; set; }
    
    [Required] public float InstallmentPrice { get; set; }
}

还有我的 Dto:

public class MembershipDto
{
    public int Id { get; private set; }
    
    public string Name { get; set; }

    public int PeriodType { get; set; }
    
    public int Duration { get; set; }
    
    public int TerminationPeriod { get; set; }
    
    public float InstallmentPrice { get; set; }
}

我正在使用 Entity Framework 和 JsonPatchDocument 在我的 API 中进行 PATCH 操作,代码如下:

public async Task<MembershipDto> Handle(EditMembershipCommand request, CancellationToken cancellationToken)
{
    var membershipEntity = await _dbContext
        .Memberships
        .SingleOrDefaultAsync(m => m.Id == request.Id);

    if (membershipEntity is null)
        throw new NullReferenceException($"Membership [Id: {request.Id}] not found");

    var editedMembership = request.NewMembershipDto.Adapt<JsonPatchDocument<Membership>>(); //request.NewMembershipDto is type of JsonPatchDocument<MembershipDto>

    editedMembership.ApplyTo(membershipEntity, ModelState);
    _ = await _dbContext.SaveChangesAsync();

    var membershipDto = editedMembership.Adapt<MembershipDto>();

    return membershipDto;
}

作为输出,我得到以下信息:

ProblemDetails.ProblemDetailsMiddleware[1]
  An unhandled exception has occurred while executing the request.
  Mapster.CompileException: Error while compiling
  source=Microsoft.AspNetCore.JsonPatch.JsonPatchDocument`1[AgreementApi.Dtos.MembershipDto]
  destination=Microsoft.AspNetCore.JsonPatch.JsonPatchDocument`1[AgreementApi.Models.Membership]
  type=Map
   ---> Mapster.CompileException: Error while compiling
  source=Newtonsoft.Json.Serialization.IContractResolver
  destination=Newtonsoft.Json.Serialization.IContractResolver
  type=Map
   ---> System.InvalidOperationException: No default constructor for type 'IContractResolver', please use 'ConstructUsing' or 'MapWith'
     at Mapster.Utils.DynamicTypeGenerator.CreateTypeForInterface(Type interfaceType)
     at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
     at Mapster.Utils.DynamicTypeGenerator.GetTypeForInterface(Type interfaceType)
     at Mapster.Adapters.RecordTypeAdapter.CreateInstantiationExpression(Expression source, Expression destination, CompileArgument arg)
     at Mapster.Adapters.BaseAdapter.CreateInstantiationExpression(Expression source, CompileArgument arg)
     at Mapster.Adapters.RecordTypeAdapter.CreateInlineExpression(Expression source, CompileArgument arg)
     at Mapster.Adapters.BaseAdapter.CreateInlineExpressionBody(Expression source, CompileArgument arg)
     at Mapster.Adapters.BaseAdapter.CreateExpressionBody(Expression source, Expression destination, CompileArgument arg)
     at Mapster.Adapters.BaseAdapter.CreateAdaptFunc(CompileArgument arg)
     at Mapster.TypeAdapterConfig.CreateMapExpression(CompileArgument arg)
     --- End of inner exception stack trace ---
     at Mapster.TypeAdapterConfig.CreateMapExpression(CompileArgument arg)
     at Mapster.TypeAdapterConfig.CreateInlineMapExpression(Type sourceType, Type destinationType, MapType mapType, CompileContext context, MemberMapping mapping)
     at Mapster.Adapters.BaseAdapter.CreateAdaptExpressionCore(Expression source, Type destinationType, CompileArgument arg, MemberMapping mapping, Expression destination)
     at Mapster.Adapters.BaseAdapter.CreateAdaptExpression(Expression source, Type destinationType, CompileArgument arg, MemberMapping mapping, Expression destination)
     at Mapster.Adapters.ClassAdapter.CreateInlineExpression(Expression source, CompileArgument arg)
     at Mapster.Adapters.BaseAdapter.CreateInlineExpressionBody(Expression source, CompileArgument arg)
     at Mapster.Adapters.BaseAdapter.CreateExpressionBody(Expression source, Expression destination, CompileArgument arg)
     at Mapster.Adapters.BaseAdapter.CreateAdaptFunc(CompileArgument arg)
     at Mapster.TypeAdapterConfig.CreateMapExpression(CompileArgument arg)
     --- End of inner exception stack trace ---
     at Mapster.TypeAdapterConfig.CreateMapExpression(CompileArgument arg)
     at Mapster.TypeAdapterConfig.CreateMapExpression(TypeTuple tuple, MapType mapType)
     at Mapster.TypeAdapterConfig.CreateDynamicMapExpression(TypeTuple tuple)
     at Mapster.TypeAdapterConfig.<GetDynamicMapFunction>b__66_0[TDestination](TypeTuple tuple)
     at Mapster.TypeAdapterConfig.<>c__DisplayClass55_0`1.<AddToHash>b__0(TypeTuple types)
     at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
     at Mapster.TypeAdapterConfig.AddToHash[T](ConcurrentDictionary`2 hash, TypeTuple key, Func`2 func)
     at Mapster.TypeAdapterConfig.GetDynamicMapFunction[TDestination](Type sourceType)
     at Mapster.TypeAdapter.Adapt[TDestination](Object source, TypeAdapterConfig config)
     at Mapster.TypeAdapter.Adapt[TDestination](Object source)
     at Fitverse.AgreementApi.Handlers.EditMembershipHandler.Handle(EditMembershipCommand request, CancellationToken cancellationToken) in /Users/wonsu/Repositories/Fitverse/Fitverse.AgreementApi/Handlers/EditMembershipHandler.cs:line 34
     at MediatR.Pipeline.RequestExceptionProcessorBehavior`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next)
     at MediatR.Pipeline.RequestExceptionProcessorBehavior`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next)
     at MediatR.Pipeline.RequestExceptionActionProcessorBehavior`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next)
     at MediatR.Pipeline.RequestExceptionActionProcessorBehavior`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next)
     at MediatR.Pipeline.RequestPostProcessorBehavior`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next)
     at MediatR.Pipeline.RequestPreProcessorBehavior`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next)
     at Fitverse.AgreementApi.Controllers.SettingsController.EditMembership(Int32 id, JsonPatchDocument`1 membershipDto) in /Users/wonsu/Repositories/Fitverse/Fitverse.AgreementApi/Controllers/SettingsController.cs:line 52
     at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(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.<InvokeFilterPipelineAsync>g__Awaited|19_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.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
     at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
     at Hellang.Middleware.ProblemDetails.ProblemDetailsMiddleware.Invoke(HttpContext context)

我知道问题出在这行代码中:

var editedMembership = request.NewMembershipDto.Adapt<JsonPatchDocument<Membership>>();

但我不知道如何更改它以正确地将其映射到所需的类型。你可以帮帮我吗?:)

我知道最后我可能应该再次从 DB 获取成员资格,映射它并返回它,而不是仅仅映射editedMembership,但在处理此映射问题后我会适应它。

标签: c#entity-frameworkasp.net-coremapster

解决方案


推荐阅读