#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
во время выполнения.