Ben Krueger
Ben Krueger

Reputation: 1536

Middleware DI Error

I am attempting to implement the API Key Validator mentioned in this post. I am running into an issue where the injected service I am using to do validation in the middleware class is returning:

InvalidOperationException: Cannot resolve 'FoosballKeepr.Services.Interfaces.ILeagueService' from root provider because it requires scoped service 'FoosballKeepr.Data.FoosballKeeprContext'.

I believe I am registering my dbContext, services, and repositories correctly in Startup.cs.

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        //MVC
        services.AddMvc();

        //Database
        var connection = @"Server=localhost\SQLEXPRESS;Database=FoosballKeepr;Trusted_Connection=True;";
        services.AddDbContext<FoosballKeeprContext>(options => options.UseSqlServer(connection));

        //Services
        services.AddTransient<IPlayerService, PlayerService>();
        services.AddTransient<ILeagueService, LeagueService>();

        //Repositories
        services.AddTransient<IPlayerRepository, PlayerRepository>();
        services.AddTransient<ILeagueRepository, LeagueRepository>();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseMiddleware<ApiKeyValidatorMiddleware>();

        app.UseMvc();
    }
}

Custom middleware validator:

public class ApiKeyValidatorMiddleware
{
    private readonly RequestDelegate _next;
    private ILeagueService _leagueService;

    public ApiKeyValidatorMiddleware(RequestDelegate next, ILeagueService leagueService)
    {
        _next = next;
        _leagueService = leagueService;
    }

    public async Task Invoke(HttpContext context)
    {
        if (!context.Request.Headers.Keys.Contains("x-api-key"))
        {
            context.Response.StatusCode = 400;              
            await context.Response.WriteAsync("API Key Missing.");
            return;
        }
        else
        {
            int leagueId = _leagueService.ValidateApiKey(context.Request.Headers["x-api-key"]);

            if (leagueId == 0)
            {
                context.Response.StatusCode = 401;
                await context.Response.WriteAsync("Invalid API Key");
                return;
            }
            else
            {
                context.Items["LeagueId"] = leagueId;
            }
        }

        await _next.Invoke(context);
    }
}

Service

    public class LeagueService : ILeagueService
{
    private readonly ILeagueRepository _leagueRepository;

    public LeagueService(ILeagueRepository leagueRepository)
    {
        _leagueRepository = leagueRepository;
    }

    public int ValidateApiKey(string apiKey)
    {
        return _leagueRepository.ValidateApiKey(apiKey);
    }
}

Repository

public class LeagueRepository : ILeagueRepository
{
    private readonly FoosballKeeprContext _context;

    public LeagueRepository(FoosballKeeprContext context)
    {
        _context = context;
    }

    public int ValidateApiKey(string apiKey)
    {
        var query = from l in _context.League
                    where l.ApiKey == apiKey
                    select l.LeagueId;

        return query.FirstOrDefault();
    }
}

This is my first time implementing custom middleware functionality so I feel like my issue is not correctly setting something up in the correct context, but nothing is popping up as obvious. Does this look familiar to anyone??

Upvotes: 1

Views: 828

Answers (1)

Camilo Terevinto
Camilo Terevinto

Reputation: 32068

The problem is that middlewares don't have a scope, given that:

Middleware is constructed once per application lifetime

So, when you need to inject scoped services, you do it at the Invoke operation (what's known as method injection):

public async Task Invoke(HttpContext context, ILeagueService service)
{
    //...
}

Upvotes: 7

Related Questions