2015-07-02 2 views
0

В моем слое доступа к данным у меня есть хранилище иерархии, которая выглядит следующим образом:Как сделать Simple Injector предпочтительными реализациями «самого производного» интерфейса?

<TEntity>         
IEntityRepository<---------+ICustomerRepository 
     ^      ^  
     |       |   
     |       |   
     |       |   
     +       |   
    <TEntity>      +   
EntityRepository<----------+CustomerRepository 

IEntityRepository<TEntity> Интерфейс определяет основные операции CRUD, которые будут полезны независимо от типа объекта. EntityRepository<TEntity> - это конкретная реализация этих операций.

Кроме того, существуют типы репозитория для операций, которые относятся к конкретному объекту. В приведенном выше примере у меня есть объект Customer, а интерфейс ICustomerRepository определяет операции, такие как GetByPhoneNumber. ICustomerRepository также получен из IEntityRepository<Customer>, так что общие операции CRUD также будут доступны для экземпляра ICustomerRepository. Наконец, CustomerRepository - это конкретная реализация для операций ICustomerRepository, и она также наследует от EntityRepository<Customer> для реализации общих операций.

Итак, перейдем к моему фактическому вопросу: я использую Simple Injector для ввода экземпляров в свое приложение. Я регистрирую каждый из специализированных типов репозитория в моем контейнере: CustomerRepository как реализация ICustomerRepository и так далее.

Чтобы обеспечить новые типы объектов могут быть добавлены в систему и использовать без необходимости создания новой, конкретной реализации хранилища, а также, я хотел бы быть в состоянии служить по основанию EntityRepository<> реализации, когда IEntityRepository<> нового объекта является просил. Я понял, что могу использовать метод RegisterOpenGeneric для этого.

Что я не могу понять, когда запрашивается общий репозиторий, как я могу обслуживать специализированный репозиторий для этого типа, если он существует, и общий репозиторий только в качестве резервной копии?

Например, предположим, что я делаю это в моем приложении:

container.Register<ICustomerRepository, CustomerRepository>(); 
container.RegisterOpenGeneric(typeof(IEntityRepository<>), typeof(EntityRepository<>)); 

Большинство классов, опирающихся на хранилищах будет просить ICustomerRepository непосредственно. Тем не менее, может быть класса в моем приложении запрашивает базовый интерфейс, например:

public ContractValidator(IEntityRepository<Customer> customerRepository, 
         IEntityRepository<Contract> contractRepository) 
{ 
    ... 

Что происходит в приведенном выше примере:

  • customerRepository получает экземпляр EntityRepository<Customer>
  • contractRepository получает пример EntityRepository<Contract>

Что я хочу сказать:

  • customerRepository получает экземпляр CustomerRepository
  • contractRepository получает экземпляр EntityRepository<Contract>

Есть ли способ сообщить разрешение Simple инжектора, что если вывод конкретного интерфейса существует, то это должно быть подано вместо?Поэтому для IDerived : IBase запросы на IBase должны вернуть реализацию IDerived, если она существует. И я не хочу эту резолюцию по всем направлениям, только для этих репозиториев. Можно ли это сделать разумным образом, или мне нужно будет вручную перебирать все регистрационные данные в предикате и проверять вручную?

ответ

1

Если предположить, что ваши классы выглядеть следующим образом

public class CustomerRepository : 
    ICustomerRepository, 
    IEntityRepository<Customer> { } 

Вы можете зарегистрировать все общие реализаций IEntityRepository<> с использованием RegisterManyForOpenGeneric и регистрация запасного варианта остается неизменной.

UPDATE: обновление с версии 3 синтаксиса

// Simple Injector v3.x 
container.Register<ICustomerRepository, CustomerRepository>(); 
container.Register(
    typeof(IEntityRepository<>), 
    new[] { typeof(IEntityRepository<>).Assembly }); 
container.RegisterConditional(
    typeof(IEntityRepository<>), 
    typeof(EntityRepository<>), 
    c => !c.Handled); 
// Simple Injector v2.x 
container.Register<ICustomerRepository, CustomerRepository>(); 
container.RegisterManyForOpenGeneric(
    typeof(IEntityRepository<>), 
    new[] { typeof(IEntityRepository<>).Assembly }); 
container.RegisterOpenGeneric(
    typeof(IEntityRepository<>), 
    typeof(EntityRepository<>)); 

Но следует иметь в виду, что если вы используете какой-либо образ жизни, то эти отдельные регистрации не могут решить, как можно было бы ожидать. Это называется torn lifestyle.

+0

Это, кажется, разрешает это для меня. Подобно тому, как вы прокомментировали другой ответ, это действительно порождает проблему с порванным стилем жизни, и я могу получить несколько экземпляров репозитория в своем приложении. Я не считаю, что это может нанести какой-либо ущерб в моем случае, поскольку хранилища представляют собой тонкие абстракции над базовым поточно-безопасным сервисом, который имеет только один экземпляр. Но это все еще не идеально, и что-то нужно помнить о будущих изменениях архитектуры. Спасибо! –

1

Вы не можете использовать RegisterOpenGeneric или RegisterManyForOpenGeneric для этого. Вам придется вручную написать код, чтобы отразить систему типов и найти реализации для регистрации по их конкретному интерфейсу.

По-моему, однако, у вас не должно быть настраиваемого хранилища. Эти одноточечные сопоставления вызывают у вас это горе и, кроме того, вы нарушаете принципы SOLID, делая это. Если можно, рассмотрите проект, как описано here.

+0

Похоже, что 'RegisterManyForOpenGeneric' будет работать, если бы у него была перегрузка, которая позволяла мне указывать явный образ жизни, отличный от переходного. Есть ли веская причина, почему такой перегрузки не существует? Помимо этого, я прочитал статью, которую вы связали, и я согласен с тем, что предлагаемая архитектура лучше соответствует принципам SOLID. К сожалению, сейчас нет возможности для такого рефакторинга, поэтому мне придется работать с текущей архитектурой. –

+0

@MartinWedvich: Пожалуйста, смотрите ближе. Есть перегрузки, которые принимают 'Lifestyle'. – Steven

+0

О, ты абсолютно прав, мне очень жаль! Я путался с порядком параметров, так как метод 'RegisterOpenGeneric' добавляет параметр' Lifestyle' в конце, пока он находится между типами для 'RegisterManyForOpenGeneric'. Спасибо! –

Смежные вопросы