我试图在配置方法中获取一些服务,这样我就可以更轻松地为我们的数据库设置种子。
所以我在我的Configure方法中注入了 IServiceProvider ,但每次执行:var service = serviceProvider.GetService<UserService>();
它都返回null ...
这是我的代码:
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(this.Configuration.GetConnectionString("DbConnectionString")));
// Add framework services.
services.AddIdentity<IdentityUser, IdentityRole>().AddEntityFrameworkStores<IdentityDbContext>().AddDefaultTokenProviders();
string connection = this.Configuration.GetConnectionString("DbConnectionString");
services.AddEntityFramework();
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(connection));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.Configure<AppSettings>(this.Configuration.GetSection("AppSettings"));
services.AddScoped<IsAuthorized>();
services.AddSingleton<UserManager<ApplicationUser>>();
services.AddTransient<IUsersService, UserService>();
services.AddMvc(config => { config.Filters.Add(typeof(SingletonUsers)); });
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider services)
{
loggerFactory.AddConsole(this.Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseMvc();
// This returns the context.
var context = services.GetService<ApplicationDbContext>();
// This returns null.
var userService = services.GetService<UserService>();
// Can't work without the UserService
MyDbInitializer.Initialize(context, userService);
}
}
答案 0 :(得分:8)
作为注入IServiceProvider
的替代方法,您只需将服务作为参数请求Configure
:
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory,
ApplicationDbContext context,
IUserService userService)
{
}
答案 1 :(得分:8)
在您使用
注册UserService
时
services.AddTransient<IUsersService, UserService>();
而不是
var userService = services.GetService<UserService>();
您需要询问界面类型:
var userService = services.GetService<IUserService>();
答案 2 :(得分:4)
IServiceProvider
未在Di / IoC中注册,因为IServiceProvider
是IoC / DI (或使用第三方DI时的包装器)。
创建IServiceProvider
后,其依赖关系配置将无法再进行更改(这适用于开箱即用的IoC)。
当您需要Configure
中的依赖项时,您应该将此依赖项作为方法参数(方法注入)传递并获取实例。如果您仍需要访问IServiceProvider
,则可以致电app.ApplicationServices
。
但请注意,当您使用app.ApplicationServices
时,您正在从应用程序范围的容器中解析。每个作用域或临时服务都以这种方式解决,在应用程序结束前保持活动状态。这是因为在应用程序启动期间没有作用域容器,它是在请求期间创建的。
这意味着,您必须在Configure
方法中创建范围,实例化您需要的服务,调用它们,然后在离开之前处置范围内容。
// Example of EF DbContext seeding I use in my application
using (var scope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
using (var context = scope.ServiceProvider.GetRequiredService<MyDbContext>())
{
if(context.Database.EnsureCreated())
{
context.SeedAsync().Wait();
}
}
}
这可以确保所有服务都清楚地处理,并且您没有内存泄漏。如果/在初始化DbContext
而不创建范围容器时会产生严重影响,例如将DbContext转换为单个(因为它将从父容器中解析)或导致内存泄漏,因为在应用程序范围内解析的服务保持活动状态应用程序的生命周期。