System.InvalidOperationException: Unable to resolve service for type - Dependency Injection

36.1k Views Asked by At

I am developing a web application - Blazor WebAssembly hosted on ASP.NET. I am trying to start getting value from database (Entity Framework using). I am using Repository and UnitOfWork patterns in my solution. So, I faced this error:

An unhandled exception has occurred while executing the request.
      System.InvalidOperationException: Unable to resolve service for type 'ReportApp.Core.Services.TaskService' while attempting to activate 'ReportApp.Server.Controllers.TaskController'.
         at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
         at lambda_method8(Closure , IServiceProvider , Object[] )
         at Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider.<>c__DisplayClass4_0.<CreateActivator>b__0(ControllerContext controllerContext)
         at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.<CreateControllerFactory>g__CreateController|0(ControllerContext controllerContext)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
      --- End of stack trace from previous location ---
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
      --- End of stack trace from previous location ---
         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.Builder.Extensions.MapWhenMiddleware.Invoke(HttpContext context)
         at Microsoft.AspNetCore.Builder.Extensions.MapMiddleware.Invoke(HttpContext context)
         at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

The structure of my project is as follows:

DbContext class:

public class ReportAppContext : DbContext
{
    public DbSet<TaskEntity> Tasks { get; set; }
        public DbSet<ReportEntity> Reports { get; set; }
        public DbSet<EmployeeEntity> Employees { get; set; }
        
        public ReportAppContext()
        {
        }

        public ReportAppContext(DbContextOptions<ReportAppContext> options) : base(options)
        {
        }
}

Repository interface:

public interface ITaskRepository : IGenericRepository<TaskEntity>
    {
    }

public interface IGenericRepository<TEntity> where TEntity : class
    {
        Task<TEntity> GetByIdAsync(Int32 id);
        Task InsertAsync(TEntity entity);
        Task UpdateAsync(TEntity entity);
        Task DeleteAsync(Int32 id);
        Task<IEnumerable<TEntity>> GetAllAsync();
        Task SaveAsync();
    }

public class TaskRepository : ITaskRepository
    {
        private readonly ReportAppContext _context;

        public TaskRepository(ReportAppContext context)
        {
            _context = context;
        }

Then I have UnitOfWork pattern:

public class UnitOfWork : ReportAppContext, IUnitOfWork
    {
        private readonly ITaskRepository _taskRepository;

And controller on server:

[Route("api/[controller]")]
    [ApiController]
    public class TaskController : ControllerBase
    {
        private readonly TaskService _taskService;

        public TaskController(TaskService taskService)
        {
            _taskService = taskService;
        }

        [HttpGet("get-all")]
        public async Task<ActionResult<List<TaskDto>>> GetAllTasksAsync()
        {
            var result = await _taskService.GetAllAsync();
            return Ok(result);
        }

Finally, Startup class configuration:

public void ConfigureServices(IServiceCollection services)
        {
            var connection = Configuration.GetConnectionString("DefaultConnection");
            services.AddDbContext<ReportAppContext>(options => options.UseSqlServer(connection));
            services.AddCors();

            services.AddServerSideBlazor().AddCircuitOptions(options => { options.DetailedErrors = true; });
            services.AddControllersWithViews();
            services.AddRazorPages();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseWebAssemblyDebugging();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseBlazorFrameworkFiles();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapRazorPages();
                endpoints.MapControllers();
                endpoints.MapFallbackToFile("index.html");
            });
        }

I've tried to add this:

services.AddTransient<ITaskRepository, TaskRepository>();

and the same with AddScoped, but it didn't change anything...

3

There are 3 best solutions below

2
On BEST ANSWER

But in your TaskController you are injecting TaskService, not TaskRepository. I think you need to register TaskService also (i assume that TaskService consumes TaskRepository. Both can be registered as Scoped).

services.AddTransient<ITaskRepository, TaskRepository>(); OR
services.AddScoped<ITaskRepository, TaskRepository>();
services.AddTransient<TaskService>(); OR
services.AddScoped<TaskService>(); 

The difference between scoped and transient is that when you have registered service as transient every time when it is resolved return a new instance while scoped will return you same instance during the scope.

0
On

The error is telling you that TaskService is not registered but your Controller needs it as an injected service.

In ConfigureServices, try something like this:

services.AddScoped<TaskService>(sp => {
    // Build your Context Options
    DbContextOptionsBuilder<ReportAppContext> optsBuilder = new DbContextOptionsBuilder<ReportAppContext>();
    optsBuilder.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
    // Build your context (using the options from the builder)
    ReportAppContext ctx = new ReportAppContext(optsBuilder.options);
    // Build your unit of work (and pass in the context)
    UnitOfWork uow = new UnitOfWork(ctx);
    // Build your service (and pass in the unit of work)
    TaskService svc = new TaskService(uow)
    // Return your Svc
    return svc;
});

Then, your controller will receive a fully configured TaskService, ready for use.

You can put each item in the DI container in turn, if you wish, but the UOW, Repository & Context do not need to be accessed outside of the TaskService, so its a bit of waste of time.

Just create the configured TaskService as above and that's all that needs to be injected by the DI container.

0
On

I also encountered the same error but the above solutions didn't help in my case. I was accidentally trying to inject the concrete class ReviewRepository instead of the interface IReviewRepository when I had the following registration:

services.AddScoped<IReviewRepository, ReviewRepository>();

And the following consuming code:

private IReviewRepository _reviewRepository;
private IPokemonRepository _pokemonRepository;
private readonly IReviewerRepository _reviewerRepository;
private IMapper _mapper;

public ReviewController(ReviewRepository reviewRepository, 
    IMapper mapper, 
    IPokemonRepository pokemonRepository,
    ReviewerRepository reviewerRepository)
{
    _reviewRepository = reviewRepository;
    _pokemonRepository = pokemonRepository;
    _reviewerRepository = reviewerRepository;
    _mapper = mapper;
}

Here, ReviewRepository needs to be replaced by IReviewRepository.

So as well as checking for missing dependency injection registrations, it's also worth checking that existing ones are correct!