我一直在努力寻找让RoleService
获得ConfigurationDbContext
(IdentityServer4)的最佳/首选方式。
我真的想要解耦,以便我的RoleService可以测试。
我找到ConfigurationDbContext
的唯一途径是通过在public static IServiceProvider
中创建Startup.cs
:
public class Startup
{
private readonly IHostingEnvironment _environment;
// THIS IS THE PROPERTY I USED
public static IServiceProvider ServiceProvider { get; private set; }
public ConfigurationDbContext GetConfigurationDbContext()
{
return null;
}
public Startup(ILoggerFactory loggerFactory, IHostingEnvironment environment)
{
loggerFactory.AddConsole(LogLevel.Debug);
_environment = environment;
}
public void ConfigureServices(IServiceCollection services)
{
var connectionString = DbSettings.IdentityServerConnectionString;
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
// configure identity server with in-memory stores, keys, clients and scopes
var identityServerConfig = services.AddIdentityServer()
.AddConfigurationStore(builder =>
builder.UseSqlServer(connectionString, options =>
options.MigrationsAssembly(migrationsAssembly)))
.AddOperationalStore(builder =>
builder.UseSqlServer(connectionString, options =>
options.MigrationsAssembly(migrationsAssembly)))
.AddSigningCredential(new X509Certificate2(Path.Combine(_environment.ContentRootPath, "certs", "IdentityServer4Auth.pfx"), "test"));
identityServerConfig.Services.AddTransient<IResourceOwnerPasswordValidator, ActiveDirectoryPasswordValidator>();
identityServerConfig.Services.AddTransient<IProfileService, CustomProfileService>();
services.AddDbContext<ConfigurationDbContext>(options => options.UseSqlServer(connectionString));
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
ServiceProvider = app.ApplicationServices;
// other code emitted
}
}
然后在 RoleService.cs
中使用此代码:
public class RoleService : IRoleService
{
public async Task<ApiResource[]> GetApiResourcesByIds(int[] ids)
{
ApiResource[] result;
using (var serviceScope = Startup.ServiceProvider.GetService<IServiceScopeFactory>().CreateScope())
{
var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
result =
context.ApiResources.Where(x => ids.Contains(x.Id))
.Include(x => x.Scopes)
.Include(x => x.UserClaims)
.ToArray();
return result;
}
}
}
这是获得RoleService.cs
依赖关系的最佳方法吗?
有没有办法抽象serviceScope
(因为它在using语句中,可能是IDisposable,我真的不知道是否有办法抽象获取context
?< / p>
任何其他建议或最佳做法?
答案 0 :(得分:3)
您可以通过创建具有所需依赖项的构造函数作为构造函数参数,将依赖项传递给RoleService
。
根据您RoleService
的生命周期(根据提供的代码无法判断),如果服务已经ConfigurationDbContext
,您可以将RoleService
直接传递给Scoped
1}}。或者您可以通过IServiceScopeFactory
在您的服务中手动创建范围。
类似的东西:
private readonly IServiceScopeFactory _scopeFactory;
public RoleService(IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
}
public async Task<ApiResource[]> GetApiResourcesByIds(int[] ids)
{
using (var scope = _scopeFactory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
}
}
由于你的方法很好地耦合到DbContext(这不是一件坏事),你可能最终会创建一个内存中的ConfigurationDbContext
。
您使用的IdentityServer4.EntityFramework存储库有一些用于测试here的示例。