Golide
Golide

Reputation: 1009

How do I resolve "Unable to resolve service for type '' while attempting to activate ''"

When I attempt requests to a .net core 3.1 WebAPI from Postman I am getting error

System.InvalidOperationException: Unable to resolve service for type 'PaymentsAPI.Repository.PaymentService' while attempting to activate 'PaymentsAPI.Controllers.PaymentController' '

Startup.cs

public void ConfigureServices(IServiceCollection services)
{      
    services.AddControllers();
  
    services.AddCors(c =>
    {
        c.AddPolicy("AllowOrigin", options => options.AllowAnyOrigin());
    });
    services.AddDbContext<ApplicationDbContext>(o => o.UseSqlServer(Configuration.GetConnectionString("SqlSvrConn")));
    services.AddTransient<IAsyncPaymentsService<PaymentDetail>, PaymentService>();
}

IAsyncPaymentsService.cs

public interface IAsyncPaymentsService<TEntity>
{        
    Task<IEnumerable<TEntity>> GetAllAsync();
}

PaymentService.cs

public class PaymentService : IAsyncPaymentsService<PaymentDetail>
{
    private readonly ApplicationDbContext _dbContext;
    

    public async Task<IEnumerable<PaymentDetail>> GetAllAsync()
    {
        return await _dbContext.PaymentDetails.ToListAsync();
    }
}

PaymentController.cs

[ApiController]
[Route("[controller]")]
public class PaymentController : ControllerBase
{
    private readonly ApplicationDbContext _context;
    private readonly PaymentService _service;
    public PaymentController(ApplicationDbContext context, PaymentService service)
    {
        _context = context;
        _service = service;
    }

    [HttpGet]
    public async Task<ActionResult<IEnumerable<PaymentDetail>>> GetAsync()
    {
        var items = (await _service.GetAllAsync());
        return Ok(items);
    }
}

I have tried rearranging the order of services in the container but the error still persists. What am I missing ?

Upvotes: 7

Views: 40669

Answers (2)

Kit
Kit

Reputation: 21699

The only thing you should have to change to make this work is to accept the interface into your controller instead of the concrete service.

public PaymentController(ApplicationDbContext context, IAsyncPaymentsService<PaymentDetail> service)
{...}

This is recommended over taking the concrete type for various reasons such as testing. If you truly need the concrete type, you'd have to instead change your registration to

services.AddTransient<PaymentService>();

and leave your controller's constructor as is.

Upvotes: 1

Nkosi
Nkosi

Reputation: 246998

The quick fix would be to change the controller constructor to depend on the abstraction instead of the implementation since the abstraction is what was registered with the container.

//...

private readonly ApplicationDbContext _context;
private readonly IAsyncPaymentsService<PaymentDetail> _service;

public PaymentController(ApplicationDbContext context, IAsyncPaymentsService<PaymentDetail> service)
{
    _context = context;
    _service = service;
}

//...

However, the generic abstraction could derived to a closed type if so desired

public interface IPaymentService :  IAsyncPaymentsService<PaymentDetail> {

}

applied to the implementation

public class PaymentService : IPaymentService {
    //...omitted for brevity
}

registered with the container

services.AddTransient<IPaymentService, PaymentService>();

and refactored in the controller

//...

private readonly ApplicationDbContext _context;
private readonly IPaymentService _service;

public PaymentController(ApplicationDbContext context, IPaymentService service)
{
    _context = context;
    _service = service;
}

//...

Upvotes: 7

Related Questions