Я ищу руководство о том, как правильно и безопасно удалять зарегистрированные одноэлементные экземпляры, когда мое приложение ASP.NET Core 2.0 закрывается.
Согласно следующему документу, если я зарегистрирую одноэлементный экземпляр (через IServiceCollection), контейнер никогда не будет пытаться создать экземпляр (и не будет избавляться от экземпляра), поэтому мне остается самому избавиться от этих экземпляров, когда приложение закрывается .
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.0 (у 2.1 такие же рекомендации)
Я прилагаю некоторый псевдокод, иллюстрирующий то, чего я пытаюсь достичь.
Примечание. Мне приходится поддерживать ссылку на IServiceCollection, поскольку IServiceProvider, предоставляемый методу OnShutDown, является простым локатором службы и не дает мне возможности выполнять сложные запросы.
Когда приложение закрывается, мне нужен общий способ гарантировать удаление всех одноэлементных экземпляров. Я мог бы поддерживать ссылку на все эти одноэлементные экземпляры напрямую, но это плохо масштабируется.
Первоначально я использовал фабричный метод, который обеспечивал бы, чтобы DI управлял временем жизни моих объектов, однако выполнение фабричного метода происходило во время выполнения в конвейере обработки запроса, что означало, что если он выдавал исключение, ответ был 500 InternalServerError. и была зарегистрирована ошибка. Создавая объект напрямую, я стремлюсь к более быстрой обратной связи, чтобы ошибки при запуске приводили к автоматическому откату во время развертывания. Мне это не кажется неразумным, но в то же время я не злоупотребляю DI.
Есть ли у кого-нибудь предложения, как сделать это более элегантно?
namespace MyApp
{
public class Program
{
private static readonly CancellationTokenSource cts = new CancellationTokenSource();
protected Program()
{
}
public static int Main(string[] args)
{
Console.CancelKeyPress += OnExit;
return RunHost(configuration).GetAwaiter().GetResult();
}
protected static void OnExit(object sender, ConsoleCancelEventArgs args)
{
cts.Cancel();
}
static async Task<int> RunHost()
{
await new WebHostBuilder()
.UseStartup<Startup>()
.Build()
.RunAsync(cts.Token);
}
}
public class Startup
{
public Startup()
{
}
public void ConfigureServices(IServiceCollection services)
{
// This has been massively simplified, the actual objects I construct on the commercial app I work on are
// lot more complicated to construct and span several lines of code.
services.AddSingleton<IDisposableSingletonInstance>(new DisposableSingletonInstance());
// See the OnShutdown method below
this.serviceCollection = services;
}
public void Configure(IApplicationBuilder app)
{
var applicationLifetime = app.ApplicationServices.GetRequiredService<IApplicationLifetime>();
applicationLifetime.ApplicationStopping.Register(this.OnShutdown, app.ApplicationServices);
app.UseAuthentication();
app.UseMvc();
}
private void OnShutdown(object state)
{
var serviceProvider = (IServiceProvider)state;
var disposables = this.serviceCollection
.Where(s => s.Lifetime == ServiceLifetime.Singleton &&
s.ImplementationInstance != null &&
s.ServiceType.GetInterfaces().Contains(typeof(IDisposable)))
.Select(s => s.ImplementationInstance as IDisposable).ToList();
foreach (var disposable in disposables)
{
disposable?.Dispose();
}
}
}
}