-1

I have an ASP.NET Core Web API project. In order to configure AutoMapper, I added the following code in my Program.cs file:

 builder.Services.AddAutoMapper(typeof(Program));

I inject AutoMapper in my service, BpmsServices, as follows:

public class BpmsServices(
    IMapper mapper,
    IRepository<Workflow> workflowRepository)
{
    public IMapper DataMapper { get;  } = mapper;
    public IRepository<Workflow> WorkflowRepository { get; } = workflowRepository;
}

In the API file, I have:

 public static IEndpointRouteBuilder MapWorkflowDesignApiV1(this IEndpointRouteBuilder app)
 {
     var api = app.MapGroup("api/workflow")
         .HasApiVersion(1.0);

     api.MapPut("/Node/CreateUpdate", CreateUpdateNode);

     return app;
 }       

 public static async Task<Workflow> CreateUpdateNode([AsParameters] BpmsServices services, NodeDto node)
 {
     var  = await services.WorkflowRepository
          .Include("Nodes")
          .FirstOrDefaultAsync(s => s.Id == node.WorkflowId);

     if (node.Id != 0)
     {
         .AddNode(services.DataMapper.Map<NodeDto, Node>(node));
     }
     else
     {
         .UpdateNode(services.DataMapper.Map<NodeDto, Node>(node));
     }

     await services.WorkflowRepository.SaveChangesAsync();

     return;
 }

And here is the exception I get:

System.InvalidOperationException: The public parameterized constructor must contain only parameters that match the declared public properties for type 'BpmsServices'.

What is the problem?

4
  • 1
    Can you include the definition of the Node entity, including the constructor(s). I suspect you have only parameterized constructors but Automapper is unhappy with trying to resolve matching public properties from your NodeDto class. (Not sure why the error would mention BpmsServices though. Also, when it comes to updates, I wouldn't use that approach. Automapper has a Map<src,dest>(src, dest) method to go direct from a Dto to a tracked entity. Commented Aug 30, 2024 at 1:24
  • 2
    Why are you using [AsParameters] rather than [FromServices]? [AsParameters] works with the incoming request, you're not sending your mapper and repository via the request. Commented Aug 30, 2024 at 5:56
  • as @StevePy says constructor parameters of a class should directly match the public properties of that class.You are using auto-properties with an initializer (public IMapper DataMapper { get; } = mapper;) to directly set the properties from the constructor parameters. While this is valid C# syntax, it can confuse the DI system, which expects the property values to be set explicitly within the constructor rather than through initializers. Commented Aug 30, 2024 at 8:10
  • 1
    You could try this code: public class BpmsServices { public IMapper DataMapper { get; } public IRepository<Workflow> WorkflowRepository { get; } public BpmsServices(IMapper dataMapper, IRepository<Workflow> workflowRepository) { DataMapper = dataMapper; WorkflowRepository = workflowRepository; } } Commented Aug 30, 2024 at 8:10

1 Answer 1

1

As mentioned in my comment, this is due to you annotating your BpmsService using [AsParameters] which tries binding the argument from the request. You're not obtaining your BpmsService from the request but the service container, so you want to be using [FromServices] instead.

Strictly speaking the annotations are not required here as ASP.NET will attempt to automatically locate and bind but sometimes annotations can be helpful due to them being explicit.

builder.Services.AddAutoMapper(typeof(Program));

builder.Services.AddScoped<IRepository<Workflow>, WorkflowRepository>();
builder.Services.AddScoped<IBpmsServices, BpmsServices>();

public static IEndpointRouteBuilder MapWorkflowDesignApiV1(this IEndpointRouteBuilder app)
 {
     var api = app.MapGroup("api/workflow")
         .HasApiVersion(1.0);

     api.MapPut("/Node/CreateUpdate", CreateUpdateNode);

     return app;
 }       

 public static async Task<Workflow> CreateUpdateNode([FromServices] BpmsServices services, [FromBody] NodeDto node)
 {
     var  = await services.WorkflowRepository
          .Include("Nodes")
          .FirstOrDefaultAsync(s => s.Id == node.WorkflowId);

     if (node.Id != 0)
     {
         .AddNode(services.DataMapper.Map<NodeDto, Node>(node));
     }
     else
     {
         .UpdateNode(services.DataMapper.Map<NodeDto, Node>(node));
     }

     await services.WorkflowRepository.SaveChangesAsync();

     return;
 }

I have made assumptions about the lifetime of your services, so feel free to adjust to whatever makes sense for your situation.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.