C#依赖注入:将多个接口注入到其他服务中



我想向另一个服务注入一些接口。让我们来看看我希望注入依赖项的两个服务。

内部Term.cs

private readonly IWSConfig WSConfig;
private readonly IMemoryCache MemCache;
public Term(IWSConfig wsConfig, IMemoryCache memoryCache)
{
WSConfig = wsConfig;
MemCache = memoryCache;
}

public async Task LoadData()
{
List<ConfigTerm> configTerm = await WSConfig.GetData(); // This is a web service call
...
}

Inside Person.cs

private readonly PersonRepo PersonRepository;
private readonly IMemoryCache MemCache;
private readonly ITerm Term;
private readonly IWSLoadLeave LoadLeave;
private readonly IWSLoadPartics LoadPartics;
public Person(PersonRepo personRepository, IMemoryCache memCache, ITerm term, IWSLoadLeave loadLeave, IWSLoadPartics loadPartics)
{
PersonRepository = personRepository;
MemCache = memCache;
Term = term;
LoadLeave = loadLeave;
LoadPartics = loadPartics;
}

Startup.cs 中的代码

services.AddDbContext<DBContext>(opts => opts.UseOracle(RegistryReader.GetRegistryValue(RegHive.HKEY_LOCAL_MACHINE, Configuration["AppSettings:RegPath"], "DB.ConnectionString", RegWindowsBit.Win64)));
services.AddTransient<ILogging<ServiceLog>, ServiceLogRepo>();
services.AddSingleton<IMemoryCache, MemoryCache>();    
services.AddHttpClient<IWSConfig, WSConfig>();
services.AddHttpClient<IWSLoadLeave, WSLoadLeave>();
services.AddHttpClient<IWSLoadPartics, WSLoadPartics>();
var optionsBuilder = new DbContextOptionsBuilder<DBContext>(); // Can we omit this one and just use the one in AddDbContext?
optionsBuilder.UseOracle(RegistryReader.GetRegistryValue(RegHive.HKEY_LOCAL_MACHINE, Configuration["AppSettings:RegPath"], "DB.ConnectionString", RegWindowsBit.Win64));
services.AddSingleton<ITerm, Term>((ctx) => {
WSConfig wsConfig = new WSConfig(new System.Net.Http.HttpClient(), new ServiceLogRepo(new DBContext(optionsBuilder.Options))); // Can we change this to the IWSConfig and the ILogging<ServiceLog>
IMemoryCache memoryCache = ctx.GetService<IMemoryCache>();
return new Term(wsConfig, memoryCache);
});
services.AddSingleton<IPerson, Person>((ctx) => {
PersonRepo personRepo = new PersonRepo(new DBContext(optionsBuilder.Options)); // Can we change this?
IMemoryCache memoryCache = ctx.GetService<IMemoryCache>();
ITerm term = ctx.GetService<ITerm>();
WSLoadLeave loadLeave = new WSLoadLeave(new System.Net.Http.HttpClient(), new ServiceLogRepo(new DBContext(optionsBuilder.Options))); // Can we change this?
WSLoadPartics loadPartics = new WSLoadPartics(new System.Net.Http.HttpClient(), new ServiceLogRepo(new DBContext(optionsBuilder.Options))); // Can we change this?
return new Person(personRepo, memoryCache, term, loadLeave, loadPartics);
});

但是这里和那里有一些重复。我已经在上面的代码中标记为注释。如何纠正?

[更新1]:如果我用以下内容将声明从singleton更改为:

services.AddScoped<ITerm, Term>();
services.AddScoped<IPerson, Person>();

我在尝试使用DbContext插入记录时遇到以下错误。

{System.ObjectDisposedException:无法访问已释放的对象。a该错误的常见原因是处理已解决的上下文从依赖项注入,然后稍后尝试使用应用程序中其他位置的上下文实例。如果您正在对上下文调用Dispose((,或将上下文包装在using语句。如果您正在使用依赖项注入,您应该让依赖注入容器负责处理上下文实例。对象名称:"DBContext"。

在我的WSConfig中,它将继承一个基类。这个基类还引用了ServiceLogRepo,它将调用DbContext将记录插入数据库

在WSConfig 中

public class WSConfig : WSBase, IWSConfig
{
private HttpClient WSHttpClient;
public WSConfig(HttpClient httpClient, ILogging<ServiceLog> serviceLog) : base(serviceLog)
{
WSHttpClient = httpClient;
//...
}
//...
}

WSBase类:

public class WSBase : WSCall
{    
private readonly ILogging<ServiceLog> ServiceLog;
public WSBase(ILogging<ServiceLog> serviceLog) : base(serviceLog)
{
}
...
}

WSCall类:

public class WSCall 
{
private readonly ILogging<ServiceLog> ServiceLog;
public WSCall(ILogging<ServiceLog> serviceLog)
{
ServiceLog = serviceLog;
}
....
}

和ServiceLogRepo代码

public class ServiceLogRepo : ILogging<ServiceLog>
{
private readonly DBContext _context;
public ServiceLogRepo(DBContext context)
{
_context = context;
}
public async Task<bool> LogRequest(ServiceLog apiLogItem)
{
await _context.ServiceLogs.AddAsync(apiLogItem);
int i = await _context.SaveChangesAsync();
return await Task.Run(() => true);
}
}

我在Startup.cs中也有以下内容,用于在应用程序加载时进行web服务调用。

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, ITerm term)
{
....
System.Threading.Tasks.Task.Run(async () => await term.LoadData());
}

这似乎是进入学期的时候。LoadData((,DBContext已被释放。

首先使用适当的提升时间范围在ConfigureServices中正确注册所有必要的依赖项

services.AddDbContext<DBContext>(opts => opts.UseOracle(RegistryReader.GetRegistryValue(RegHive.HKEY_LOCAL_MACHINE, Configuration["AppSettings:RegPath"], "DB.ConnectionString", RegWindowsBit.Win64)));
services.AddTransient<ILogging<ServiceLog>, ServiceLogRepo>();
services.AddSingleton<IMemoryCache, MemoryCache>();    
services.AddHttpClient<IWSConfig, WSConfig>();
services.AddHttpClient<IWSLoadLeave, WSLoadLeave>();
services.AddHttpClient<IWSLoadPartics, WSLoadPartics>();
services.AddScoped<ITerm, Term>();
services.AddScoped<IPerson, Person>();

考虑到在Configure中调用的方法的异步性质,在处理完DbContext之前,它将被处理掉

现在,理想情况下,考虑到您正在努力实现的目标,您应该使用后台服务IHostedServive,该服务将在应用程序启动时启动。

public class TermHostedService : BackgroundService {
private readonly ILogger<TermHostedService> _logger;
public TermHostedService(IServiceProvider services, 
ILogger<ConsumeScopedServiceHostedService> logger) {
Services = services;
_logger = logger;
}
public IServiceProvider Services { get; }
protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
_logger.LogInformation("Term Hosted Service running.");
using (var scope = Services.CreateScope()) {
var term = scope.ServiceProvider.GetRequiredService<ITerm>();    
await term.LoadData();
_logger.LogInformation("Data Loaded.");
}
}
public override async Task StopAsync(CancellationToken stoppingToken) {
_logger.LogInformation("Term Hosted Service is stopping.");    
await Task.CompletedTask;
}
}

在启动时注册

services.AddHostedService<TermHostedService>();

ASP.NET Core 中托管服务的参考后台任务