Нет единого решения вашей проблемы. В игру вступают различные принципы, такие как идея предотвращения зависимых зависимостей , которая гласит, что компонент должен зависеть только от услуг с равным или более длительным сроком службы. Эта идея подталкивает к созданию MyLookup
класса, который имеет ограниченный или переходный образ жизни.
Эта идея сводится к применению модели составления замыкания , что означает, что вы составляете графы объектов, которые захватывают данные времени выполнения в переменных компонентов графа. Противоположной композиционной моделью является Ambient Composition Model , которая сохраняет состояние вне графа объекта и позволяет извлекать состояние (например, DbContext
) по требованию.
Но это все теория. На первых порах может быть трудно реализовать это на практике. (Опять же) теоретически применить модель композиции замыкания просто, потому что это просто означает дать MyLookup
более короткий образ жизни, например Scoped
. Но когда MyLookup
сам захватывает состояние, которое необходимо повторно использовать в течение всего срока действия приложения, это кажется невозможным.
Но это часто не так. Одним из решений является извлечение состояния из MyLookup
в зависимость, которая не содержит собственных зависимостей (или зависит только от синглетонов) и затем становится одиночной. MyLookup
затем можно «понизить» до Scoped
и передать данные времени выполнения в свою одноэлементную зависимость, которая выполняет кэширование. Я бы с удовольствием показал вам пример этого, но ваш вопрос требует более подробной информации, чтобы сделать это.
Но если вы хотите оставить MyLookup
синглтоном, определенно есть способы сделать это. Например, вы можете заключить одну операцию в область видимости. Пример:
public class MyLookup : IMyLookup
...
public MyLookup (IMemoryCache memoryCache, IServiceScopeFactory scopeFactory)
{
_cache = memoryCache;
_scopeFactory = scopeFactory;
}
private List<string> QueryNamesFromDB()
{
using (var scope = _scopeFactory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<MyDbContext>();
var allNames = context.Persons.Select(e => e.Name).Distinct().ToList<string>();
return allSInames;
}
}
}
В этом примере MyLookup
вводится с IServiceScopeFactory
. Это позволяет создавать (и уничтожать) IServiceScope
за один вызов. Недостатком этого подхода является то, что MyLookup
теперь требует зависимости от DI-контейнера. Только классы, которые являются частью Composition Root, должны знать о существовании контейнера DI.
Таким образом, вместо этого общий подход заключается во внедрении зависимости Func<MyDbContext>
. Но на самом деле это довольно сложно с MS.DI, потому что, когда вы пытаетесь это сделать, фабрика попадает в область действия root, в то время как ваша DbContext
всегда должна быть ограничена. Есть способы обойти это, но я не буду go в тех, из-за временных ограничений с моей стороны, и потому что это только усложнит мой ответ.
Чтобы отделить зависимость от контейнера DI от вашего бизнес-логика c, вам нужно будет либо: * переместить этот полный класс внутри вашей композиции Root * или разделить класс на две части, чтобы бизнес-логика c оставалась вне корня композиции; Например, вы можете достичь этого, используя подклассификацию или состав.