[英]ASP .NET Core Inject Service in custom page filter
我已经设置了一个过滤器来处理特定文件夹及其中的所有页面。 我需要使用声明来访问数据库。 问题是我似乎无法在启动服务上使用DI注册我的过滤器,因为它找不到数据库连接
services.AddMvc()
.AddRazorPagesOptions(options =>
{
options.AllowAreas = true;
options.Conventions.AuthorizeAreaFolder("Administration", "/Account");
options.Conventions.AuthorizeAreaFolder("Production", "/Account");
options.Conventions.AuthorizeAreaFolder("Robotics", "/Account");
options.Conventions.AddAreaFolderApplicationModelConvention("Production", "/FrontEnd",
model => model.Filters.Add(
new LockdownFilter(
new ProducaoRegistoService(new ProductionContext()),
new UrlHelperFactory(),
new HttpContextAccessor())));
})
过滤器。
public class LockdownFilter : IAsyncPageFilter
{
private readonly IProducaoRegistoService _producaoRegistoService;
private readonly IUrlHelperFactory _urlHelperFactory;
private readonly IHttpContextAccessor _httpContextAccessor;
public LockdownFilter(IProducaoRegistoService producaoRegistoService, IUrlHelperFactory urlHelperFactory, IHttpContextAccessor httpContextAccessor)
{
_producaoRegistoService = producaoRegistoService;
_urlHelperFactory = urlHelperFactory;
_httpContextAccessor = httpContextAccessor;
}
public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next)
{
int registoId;
if(!int.TryParse(_httpContextAccessor.HttpContext.User.GetRegistoId(), out registoId))
{
// TODO
}
var registo = _producaoRegistoService.GetById(registoId);
await next.Invoke();
}
public async Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context)
{
await Task.CompletedTask;
}
}
错误是
InvalidOperationException:尚未为此DbContext配置任何数据库提供程序。 可以通过覆盖DbContext.OnConfiguring方法或在应用程序服务提供程序上使用AddDbContext来配置提供程序。 如果使用AddDbContext,那么还要确保您的DbContext类型在其构造函数中接受DbContextOptions对象,并将其传递给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)
{
services.AddAuthentication(options =>
{
})
.AddCookie("ProductionUserAuth", options =>
{
options.ExpireTimeSpan = TimeSpan.FromDays(1);
options.LoginPath = new PathString("/Production/FrontEnd/Login");
options.LogoutPath = new PathString("/Production/FrontEnd/Logout");
options.AccessDeniedPath = new PathString("/Production/FrontEnd/AccessDenied");
options.SlidingExpiration = true;
options.Cookie.Name = "NoPaper.ProductionUser";
options.Cookie.Expiration = TimeSpan.FromDays(1);
})
.AddCookie("ProductionAdminAuth", options =>
{
options.ExpireTimeSpan = TimeSpan.FromDays(1);
options.LoginPath = new PathString("/Production/BackOffice/Login");
options.LogoutPath = new PathString("/Production/BackOffice/Logout");
options.AccessDeniedPath = new PathString("/Production/BackOffice/AccessDenied");
options.SlidingExpiration = true;
options.Cookie.Name = "NoPaper.ProductionAdmin";
options.Cookie.Expiration = TimeSpan.FromDays(1);
})
.AddCookie("AdministrationAuth", options =>
{
options.ExpireTimeSpan = TimeSpan.FromDays(1);
options.LoginPath = new PathString("/Administration/Index");
options.LogoutPath = new PathString("/Administration/Logout");
options.AccessDeniedPath = new PathString("/Administration/AccessDenied");
options.SlidingExpiration = true;
options.Cookie.Name = "NoPaper.Administration";
options.Cookie.Expiration = TimeSpan.FromDays(1);
});
services.AddAuthorization();
services.AddMemoryCache();
services.AddAutoMapper(typeof(Startup));
services.AddMvc()
.AddRazorPagesOptions(options =>
{
options.AllowAreas = true;
options.Conventions.AuthorizeAreaFolder("Administration", "/Account");
options.Conventions.AuthorizeAreaFolder("Production", "/Account");
options.Conventions.AddAreaFolderApplicationModelConvention("Production", "/FrontEnd",
model => model.Filters.Add(
new LockdownFilter(
new ProducaoRegistoService(new ProductionContext(new DbContextOptions<ProductionContext>())),
new UrlHelperFactory(),
new HttpContextAccessor())));
})
.AddNToastNotifyToastr(new ToastrOptions()
{
ProgressBar = true,
TimeOut = 3000,
PositionClass = ToastPositions.TopFullWidth,
PreventDuplicates = true,
TapToDismiss = true
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddRouting(options =>
{
options.LowercaseUrls = true;
options.LowercaseQueryStrings = true;
});
services.AddDbContext<DatabaseContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.EnableRetryOnFailure(
maxRetryCount: 2,
maxRetryDelay: TimeSpan.FromSeconds(1),
errorNumbersToAdd: null);
sqlOptions.MigrationsHistoryTable("hEFMigrations", "Admin");
});
});
services.AddDbContext<ProductionContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), c => c.MigrationsHistoryTable("hEFMigrations", "Admin")
));
services.AddHttpContextAccessor();
services.AddSingleton<IFileProvider>(new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/files")));
services.AddTransient<IAuthorizationHandler, HasArranqueActivoHandler>();
services.AddTransient<IAuthorizationHandler, HasArranqueInactivoHandler>();
services.AddTransient<IAuthorizationHandler, IsParagemNotOnGoingHandler>();
services.AddTransient<IAuthorizationHandler, IsParagemOnGoingHandler>();
services.AddTransient<Services.Interfaces.IUserService, Services.UserService>();
#region AreaProduction
services.AddTransient<Production.Interfaces.IComponenteService, Production.ComponenteService>();
services.AddTransient<Production.Interfaces.IReferenciaService, Production.ReferenciaService>();
services.AddTransient<Production.Interfaces.IProducaoRegistoService, Production.ProducaoRegistoService>();
services.AddTransient<Production.Interfaces.IParagemService, Production.ParagemService>();
services.AddTransient<Production.Interfaces.ICelulaService, Production.CelulaService>();
services.AddTransient<Production.Interfaces.IUapService, Production.UapService>();
services.AddTransient<Production.Interfaces.ICelulaTipoService, CelulaTipoService>();
services.AddTransient<Production.Interfaces.IMatrizService, MatrizService>();
services.AddTransient<Production.Interfaces.IOperadorService, Production.OperadorService>();
services.AddTransient<Production.Interfaces.IEtiquetaService, Production.EtiquetaService>();
services.AddTransient<Production.Interfaces.IPokayokeService, Production.PokayokeService>();
services.AddTransient<Production.Interfaces.IGeometriaService, Production.GeometriaService>();
services.AddTransient<Production.Interfaces.IEmpregadoService, Production.EmpregadoService>();
services.AddTransient<Production.Interfaces.IPecaService, Production.PecaService>();
services.AddTransient<Production.Interfaces.IDefeitoService, Production.DefeitoService>();
services.AddTransient<Production.Interfaces.ITurnoService, Production.TurnoService>();
#endregion
}
// 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();
}
else
{
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
var exceptionHandlerPathFeature =
context.Features.Get<IExceptionHandlerPathFeature>();
// Use exceptionHandlerPathFeature to process the exception (for example,
// logging), but do NOT expose sensitive error information directly to
// the client.
if (exceptionHandlerPathFeature.Path.Contains("/Administration/") ||
exceptionHandlerPathFeature.Path.Contains("/administration/"))
{
context.Response.Redirect("/Administration/Error");
}
if (exceptionHandlerPathFeature.Path.Contains("/Production/") ||
exceptionHandlerPathFeature.Path.Contains("/production/"))
{
context.Response.Redirect("/Production/Error");
}
});
});
}
app.UseNToastNotify();
app.UseAuthentication();
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "areas",
template: "{area:exists}/{controller=Home}/{action=Index}/{id?}"
);
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
我的背景
public class ProductionContext : DbContext
{
//static LoggerFactory object
public static readonly ILoggerFactory loggerFactory = new LoggerFactory(new[] {
new ConsoleLoggerProvider((_, __) => true, true)
});
public ProductionContext()
{
}
public ProductionContext(DbContextOptions<ProductionContext> options) : base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseLoggerFactory(loggerFactory) //tie-up DbContext with LoggerFactory object
.EnableSensitiveDataLogging();
}
...
}
你的问题中有很多代码,所以我先强调一下感兴趣的代码:
options.Conventions.AddAreaFolderApplicationModelConvention("Production", "/FrontEnd", model => model.Filters.Add( new LockdownFilter( new ProducaoRegistoService(new ProductionContext()), new UrlHelperFactory(), new HttpContextAccessor())));
现在,让我们再看看错误消息:
InvalidOperationException:尚未为此DbContext配置任何数据库提供程序。 可以通过覆盖DbContext.OnConfiguring方法或在应用程序服务提供程序上使用AddDbContext来配置提供程序。 如果使用AddDbContext,那么还要确保您的DbContext类型在其构造函数中接受DbContextOptions对象,并将其传递给DbContext的基础构造函数。
在您的情况下, 没有配置在我调出的代码中创建的ProductionContext
实例。 你可能会认为它被配置,因为您如何使用AddDbContext
在别处ConfigureServices
方法,但事实并非如此。
AddDbContext
设置了DI所需的一切,为您提供根据您的设置配置的ProductionContext
实例(使用带有DefaultConnection
连接字符串的SQL Server)。 但是,通过创建自己的ProductionContext
实例并将其传递到过滤器,根本不会使用DI配置的实例。
这里一个明显的解决方案是将DI用于这些服务,但这并不是那么直接,因为在创建LockdownFilter
实例时您无法访问DI。 这是TypeFilterAttribute
和ServiceFilterAttribute
TypeFilterAttribute
, 在ASP.NET Core中的过滤器中有详细记录:依赖注入 。 这是我调出的代码的更新版本,它使用TypeFilterAttribute
:
options.Conventions.AddAreaFolderApplicationModelConvention("Production", "/FrontEnd",
model => model.Filters.Add(new TypeFilterAttribute(typeof(LockdownFilter))));
使用此方法,传递给LockdownFilter
构造函数的参数将从DI解析。 从你的问题中可以清楚地看出,这三个服务都是在DI容器中注册的,所以这应该按原样运行。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.