【发布时间】:2020-06-26 09:56:09
【问题描述】:
我有一个 .Net Core 服务(“MyLookup”),它执行数据库查询、一些 Active Directory 查找并将结果存储到内存缓存中。
对于我的第一个剪辑,我在 Startup.cs 中执行了.AddService<>(),将服务注入到每个使用该服务的控制器和视图的构造函数中......并且一切正常。
因为我的服务和它的依赖服务(IMemoryCache 和 DBContext)都是scoped,所以它起作用了。但现在我想将此服务设为singleton。我想在应用初始化时对其进行初始化(执行 DB 查询、AD 查找并将结果保存到内存缓存中)。
问:我该怎么做?
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyDBContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MyDBContext")));
services.AddMemoryCache();
services.AddSingleton<IMyLookup, MyLookup>();
...
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
// Q: Is this a good place to initialize my singleton (and perform the expensive DB/AD lookups?
app.ApplicationServices.GetService<IDILookup>();
OneOfMyClients.cs
public IndexModel(MyDBContext context, IMyLookup myLookup)
{
_context = context;
_myLookup = myLookup;
...
MyLookup.cs
public class MyLookup : IMyLookup
...
public MyLookup (IMemoryCache memoryCache)
{
// Perform some expensive lookups, and save the results to this cache
_cache = memoryCache;
}
...
private async void Rebuild() // This should only get called once, when app starts
{
ClearCache();
var allNames = QueryNamesFromDB();
...
private List<string>QueryNamesFromDB()
{
// Q: ????How do I get "_context" (which is a scoped dependency)????
var allNames = _context.MyDBContext.Select(e => e.Name).Distinct().ToList<string>();
return allSInames;
我尝试过的一些例外情况:
InvalidOperationException:无法使用单例“MyLookup”中的作用域服务“MyDBContext”。
...和...
InvalidOperationException:无法从根提供程序“MyLookup”解析范围服务“MyDBContext”
...或...
System.InvalidOperationException:无法从根提供商解析范围服务“IMyLookup”。
感谢 Steve 提供宝贵的见解。我终于能够:
创建一个“查找”,在应用的生命周期内,任何消费者在任何时间、任何会话中都可以使用它。
在程序启动时初始化一次。 仅供参考,不将初始化推迟到一些糟糕的用户触发它是可以接受的 - 初始化时间太长了。
使用依赖服务(IMemoryCache 和我的 DBContext),无论这些服务的生命周期如何。
我的最终代码:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyDBContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MyDBContext")));
services.AddMemoryCache();
// I got 80% of the way with .AddScoped()...
// ... but I couldn't invoke it from Startup.Configure().
services.AddSingleton<IMyLookup, MyLookup>();
...
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// This finally worked successfully...
app.ApplicationServices.GetService<IMyLookup>().Rebuild();
OneOfMyClients.cs
public IndexModel(MyDBContext context, IMyLookup myLookup)
{
// This remained unchanged (for all consumers)
_context = context;
_myLookup = myLookup;
...
MyLookup.cs
public interface IMyLookup
{
Task<List<string>> GetNames(string name);
Task Rebuild();
}
public class MyLookup : IMyLookup
{
private readonly IMemoryCache _cache;
private readonly IServiceScopeFactory _scopeFactory;
...
public MyLookup (IMemoryCache memoryCache, IServiceScopeFactory scopeFactory)
{
_cache = memoryCache;
_scopeFactory = scopeFactory;
}
private async void Rebuild()
{
ClearCache();
var allNames = QueryNamesFromDB();
...
private List<string>QueryNamesFromDB()
{
// .CreateScope() -instead of constructor DI - was the key to resolving the problem
using (var scope = _scopeFactory.CreateScope())
{
MyDBContext _context =
scope.ServiceProvider.GetRequiredService<MyDBContext>();
var allNames = _context.MyTable.Select(e => e.Name).Distinct().ToList<string>();
return allNames;
}
}
【问题讨论】:
-
你绝对应该阅读Captive Dependencies。
-
你应该阅读DI Composition Models。
-
我读了很多书,但我都没有看到这些链接。谢谢 :) 重要提示:大多数示例都有“视图”或“控制器”。我需要在调用任何视图或控制器之前初始化我的缓存。我需要使用.Net Core DI;我不能使用“Ninject”(如果重要的话)。也许我什至可以在没有任何 DI 的情况下获得我的 DBContext :)
-
@Steven - 您的一个链接指向this link,这表明
Instead of injecting the dependency, inject a factory for the creation of that dependency and call that factory every time an instance is required.问:听起来很有希望?问:我可以在 .Net Core 中使用我的 DBContext 执行此操作的任何链接? -
为什么你首先需要你的服务成为单身人士?
标签: c# .net-core dependency-injection