Вам придется использовать все ключевые службы, если вы не собираетесь делать IDbContext
родовым.Есть несколько вещей, которые вы должны учитывать при разработке глубоких уровней вещей в DI.
Примечание: это FAQ на сайте Autofac , ноЯ постараюсь развернуть несколько вещей здесь, чтобы выручить.
Концепция 1: Победа последним
Точка возможности регистрировать различные типы водин и тот же интерфейс предназначен для двух целей:
- Вам необходимо разрешить список этих вещей (например,
IEnumerable<IDbContext>
) ИЛИ - Вы переопределяете значение по умолчаниюрешаемая вещь.
Ваша регистрация здесь ...
builder.RegisterType<ReconciliationDbContext>().As<IDbContext>();
builder.RegisterType<GenevaDataDbContext>().As<IDbContext>();
builder.RegisterType<OpenStaarsDbContext>().As<IDbContext>();
... говорят две вещи:
- Если я разрешу
IEnumerable<IDbContext>
Я хочу, чтобы эти три вещи были созданы и возвращены мне. - Если я разрешу один
IDbContext
Я хочу ReconciliationDbContext
... нет, подождите, я действительно хочу GenevaDataDbContext
... нетЯ имел в виду, я хочу OpenStaarsDbContext
.Да.Каждый раз, когда я решаю один IDbContext
, я хочу OpenStaarsDbContext
.
Концепция 2: Потребители интерфейса не знают о базовых реализациях
ВесьТочка интерфейса против реализации такова, что вы не знаете что такое базовая реализация.Это принцип подстановки Лискова.Если UnitOfWork<ReconciliationDbContext>
должен иметь ReconciliationDbContext
и может работать только с этим типом ... тогда поместите ReconciliationDbContext
в конструктор для этого класса и не используйте интерфейс.Полная остановка.Если вы должны иметь определенный тип и не можете обрабатывать все IDbContext
типы одинаково, тогда возникает проблема проектирования.
Концепция 3: Серьезно, потребители интерфейсов не знают о базовых реализациях
В какой-то момент, пытаясь выяснить, как заставить эту работу работать в стеке, вы подумаете: «Эй, я мог бы передать параметр в класс единицы работы и получитькоторый прошел весь путь вниз по стеку разрешения и установил какой-то «контекст», который ... "НЕТ. Это также часто задаваемые вопросы. Вы не можете сделать это, потому что, опять же, вы не должны знать или заботиться о том, какова вся цепочка стека разрешений.Если вам это нужно, вы не используете DI или «инвертирующий контроль» - вы также можете обновить все по-старому.
-
Опять же, это FAQ на сайте Autofac , и я рекомендую прочитать, чтобы глубже понять, почему то, что вы запрашиваете, будет довольно сложно и почему, в некоторых случаях, это намеренно сложно.
Однако, если вы хотите использовать службы с ключами, вам придется делать это буквально на всем протяжении стека.
CommentService
это хорошо, новам также понадобятся такие вещи, как ...
public class ReconciliationUnitOfWork : UnitOfWork<ReconciliationDbContext>
{
public ReconciliationUnitOfWork([KeyFilter(ContextKey.Recon)]IDbContext context)
{ /* ... */ }
}
А затем вам придется обновить свои регистрации, чтобы включить фильтрацию, например ...
builder.RegisterType<ReconciliationDbContext>()
.Keyed<IDbContext>(ContextKey.Recon);
builder.RegisterType<ReconciliationUnitOfWork>()
.Keyed<IUnitOfWork>(ContextKey.Recon)
.InstancePerLifetimeScope();
Да, это больно,Вы можете сделать это немного проще, не используя по умолчанию один и тот же интерфейс.
public class ReconciliationUnitOfWork : UnitOfWork<ReconciliationDbContext>
{
public ReconciliationUnitOfWork(ReconciliationDbContext context)
: base(context)
{ /* ... */ }
}
Тогда у вас не будет ключевого материала для контекстов базы данных, но вам придется сделать ...
builder.RegisterType<ReconciliationDbContext>()
.AsSelf()
.As<IDbContext>();
builder.RegisterType<ReconciliationUnitOfWork>()
.Keyed<IUnitOfWork>(ContextKey.Recon)
.InstancePerLifetimeScope();
ОК, теперь вы все еще можете разрешить все IDbContext
объекты, если вам нужно, но вы также можете разрешить ReconciliationDbContext
как конкретный тип для поддержки нового ReconciliationUnitOfWork
там.
Как сделать его еще более универсальным?Добавьте дженерики обратно в том, что вы сказали, что вы удалили из вашего другого вопроса.UnitOfWork<TContext>
должен иметь параметр конструктора типа TContext
вместо IDbContext
.Вы можете добавить ограничение, например:
public class UnitOfWork<TContext> where TContext : IDbContext
Теперь вы уверены, что получите реализацию IDbContext
, но это будет сильный тип, который вам нужен.
Затем вы могли бы сделать его еще более общим, не утруждая себя регистрацией каждой комбинации контекстов и типов.
builder.RegisterGeneric(typeof(UnitOfWork<>))
.AsSelf()
.InstancePerLifetimeScope();
И тогда CommentsService
должен принять точный тип единицы работы, потому что он тоже не является 'не может использовать любую старую единицу работы взаимозаменяемо.
public class CommentsService : ICommentsService
{
public CommentsService(UnitOfWork<ReconciliationDbContext> reconciliationUoW)
{ /* ... */ }
}
Теперь вы фактически используете систему типов и не нарушаете принцип подстановки Лискова.Ваша жизнь станет проще, и вы получите то, что хотите.