Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
4.7k views
in Technique[技术] by (71.8m points)

c# - How to map JsonPatchDocument using Mapster?

I have my model:

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; }
}

And my 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; }
}

I'm using Entity Framework and JsonPatchDocument to make PATCH operation in my API, the code is following:

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;
}

As an output I get the following information:

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)

I know that the issue is in this line of code:

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

But I don't know how to change it to properly map it to desired type. Could you help me? :)

I know that at the end I should probably get the membership from DB again, map it and return it instead of just mapping editedMembership, but I'll fit it after dealing with this mapping issue.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
等待大神解答

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...