.NET — Заменить объект, введенный DI как одноэлементный

#c# #dependency-injection #dependencies #singleton #code-injection

#c# #внедрение зависимостей #зависимости #одноэлементный #внедрение кода

Вопрос:

У моего поставщика услуг DI есть следующие регистрации:

 services.AddScoped<IProducer, Producer>();
services.AddScoped<IConsumer, Consumer>();
services.AddSingleton<IProduct>(new Product(){Prop1 = "Value 1"});
services.AddSingleton<IFinisherService, FinisherService>();
  

И я использую эти сервисы где-то в своем приложении следующим образом (я внедряю, но на самом деле не использую finisherService в конструкторе, это здесь только для иллюстрации):

 Producer(IProduct product, IFinisherService finisherService)
{
var finishedProduct = finisherService.FinishAsync(product);

product.Prop2 = finishedProduct.Prop2;
product.Prop3 = finishedProduct.Prop3;
//...
product.PropN = finishedProduct.PropN;
}
  
 Consumer(IProduct product)
{
  propN = product.PropN;
}
  

Это работает нормально, но было бы намного проще сделать что-то подобное (опять же, здесь, в конструкторе, просто для иллюстрации):

 Producer(IProduct product, IFinisherService finisherService)
{
  product = finisherService.FinishAsync(product);
}
  

Это не работает, потому что «готово» product видно только внутри Producer области видимости. Product введенный DI в Consumer далее по строке, все еще имеет значения null Prop2 ... PropN . Я думаю, я мог бы ввести ServiceProvider в свой Producer , но я даже не уверен, можно ли изменить IOC после его создания. Есть ли лучший шаблон?

Комментарии:

1. Таким образом, система работает только с одним логическим продуктом одновременно для всех пользователей по всем запросам?

2. Если у вас есть один пользователь и один продукт, и вы можете одновременно обслуживать только один запрос, вы можете внедрить службу, которая предоставляет изменяемое свойство, и обновить это свойство. interface IProductContext { IProduct Product { get; set; } } . Все это кажется немного сомнительным, хотя, поскольку у вас есть services.AddScoped регистрации

3. Не можете ли вы изменить метод FinishAsync, чтобы вернуть ту же ссылку, которая была передана?

4. Точно. Я должен сказать, что меня не волнует общий дизайн, но это самое простое решение, если вы придерживаетесь его. Я бы не стал обращаться к текущему продукту через DI, я бы создал сервисы, у которых были сигнатуры методов, такие как IFinisherService { Task<IProduct> FinishAsync(IProduct product);} , и цепные вызовы вместе. Это делает их все намного более тестируемыми и упрощает систему для людей. Другими словами, ничто не сравнится с проверенным param => result шаблоном, и он ортогональен DI.

5. Из описания кажется, что он IProduct состоит из данных времени выполнения . Как описано здесь , вы должны предотвратить внедрение данных среды выполнения в свои классы обслуживания. Вместо этого введите абстракцию (например, IProductRepository ), которая позволяет извлекать нужное IProduct во время выполнения.