我设置了一个过滤器来处理特定文件夹及其内的所有页面。我需要使用声明访问数据库。问题是我似乎无法在启动服务上向 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: No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions object in its constructor and passes it to the base constructor for 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: No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions object in its constructor and passes it to the base constructor for DbContext.
在您的情况下,在我调用的代码中创建的 ProductionContext
实例尚未配置。您可能认为它正在被配置,因为您在 ConfigureServices
方法中的其他地方使用了 AddDbContext
,但事实并非如此。 p>
AddDbContext
设置 DI 所需的一切,为您提供一个 ProductionContext
实例,该实例根据您的设置进行配置(使用 SQL Server 和 DefaultConnection
连接字符串)。但是,通过创建您自己的 ProductionContext
实例并将其传递到过滤器中,根本不会使用 DI 配置的实例。
这里一个明显的解决方案是对这些服务使用 DI,但这并不是那么简单,因为在创建 LockdownFilter
实例时您无权访问 DI。这就是 TypeFilterAttribute
和 ServiceFilterAttribute
发挥作用的地方,Filters in ASP.NET Core: Dependency injection 中有详细记录。 。这是我调用的代码的更新版本,它使用 TypeFilterAttribute
:
options.Conventions.AddAreaFolderApplicationModelConvention("Production", "/FrontEnd",
model => model.Filters.Add(new TypeFilterAttribute(typeof(LockdownFilter))));
使用这种方法,传入 LockdownFilter
构造函数的参数将从 DI 解析。从您的问题可以清楚地看出,这三个服务都已在 DI 容器中注册,因此应该按原样工作。
关于c# - 自定义页面过滤器中的 ASP .NET Core 注入(inject)服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57539964/