【问题标题】:Middleware DI Error中间件 DI 错误
【发布时间】:2018-01-08 20:20:02
【问题描述】:

我正在尝试实现post 中提到的 API 密钥验证器。我遇到了一个问题,我用来在中间件类中进行验证的注入服务正在返回:

InvalidOperationException:无法从根提供程序解析“FoosballKeepr.Services.Interfaces.ILeagueService”,因为它需要范围服务“FoosballKeepr.Data.FoosballKeeprContext”。

我相信我在 Startup.cs 中正确注册了我的 dbContext、服务和存储库。

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

自定义中间件验证器:

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

服务

    public class LeagueService : ILeagueService
{
    private readonly ILeagueRepository _leagueRepository;

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

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

存储库

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

这是我第一次实现自定义中间件功能,所以我觉得我的问题是没有在正确的上下文中正确设置某些东西,但没有什么明显的弹出。有没有人觉得这很眼熟??

【问题讨论】:

  • 附带说明:除非您有特定原因(即将来会放弃 EF)将 EF 放在存储库后面,否则不要这样做。另一个注意事项:EF6 和 EF Core 包含用于 I/O 绑定操作的 Async 方法,您应该使用它们而不是同步版本。另一个注意事项(可能会解决您的问题),您的服务和存储库应该是 AddScoped 而不是 AddTransient
  • 只是一个想法,但如果 services.AddTransient&lt;ILeagueRepository, LeagueRepository&gt;();services.AddTransient&lt;ILeagueService, LeagueService&gt;(); 之前会有所不同,因为该服务依赖于存储库。
  • @CamiloTerevinto 将 EF 放在存储库后面有什么问题。它抽象了逻辑并提供了简单的方法来调用以访问数据。
  • @pmcilreavy 问题是大多数人都弄错了,成为一个巨大的维护负担,而 EF 已经实现了存储库(DbContext 本身)和工作单元(每个 DbSet)。 EF 非常简单,我不知道如何通过将其隐藏在另一层之后使其更简单。
  • @SBFrancies 我尝试重新订购仍然收到完全相同的错误。

标签: c# dependency-injection asp.net-core


【解决方案1】:

问题在于中间件没有范围,因为:

Middleware is constructed once per application lifetime

所以,当您需要注入作用域服务时,您可以在Invoke 操作中进行(所谓的方法注入):

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

【讨论】:

  • 是的,这就是问题所在!非常感谢,我会仔细看看那篇中间件文章,以及你上面提到的我的数据设计模式。
  • 如果你有可选的依赖,你可以通过context.RequestServices获取。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-11
  • 1970-01-01
  • 2017-08-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多