Как сопоставить JsonPatchDocument с помощью Mapster?

#c# #entity-framework #asp.net-core #mapster

#c# #entity-framework #asp.net-core #mapster

Вопрос:

У меня есть моя модель:

 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, код следующий:

 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(Stateamp; next, Scopeamp; scope, Objectamp; state, Booleanamp; 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>>();
 

Но я не знаю, как изменить его, чтобы правильно сопоставить его с нужным типом. Не могли бы вы мне помочь? 🙂

Я знаю, что в конце мне, вероятно, следует снова получить членство из базы данных, сопоставить его и вернуть вместо простого сопоставления editedMembership, но я подгоню его после решения этой проблемы с сопоставлением.