Simple Injector - пакетная/автоматическая регистрация новых классов

Сложно ли в простом инжекторе автоматически регистрировать эти классы:

public class RepositoryA : Repository<Hammers>, IRepositoryA
{
    ... implementing code here ...
}

public interface IRepositoryA: IRepository<Hammers>
{ //no methods
}


public class RepositoryB : Repository<Knives>, IRepositoryB
{
    ... implementing code here ...
}

public interface IRepositoryB: IRepository<Knives>
{ //no methods
}


public class RepositoryC : Repository<Swords>, IRepositoryC
{
    ... implementing code here ...
}


public interface IRepositoryC: IRepository<Swords>
{ //no methods
}

Я следил за документацией, описанной здесь: Simple Injector — пакетная регистрация

Но кажется, что это не сработает, если вы попытаетесь наследоваться от другого класса и его интерфейса. Я не могу разделить интерфейсы, то есть он получает Repository и IRespositoryX, где X — буква репозитория (A, B, C и т. д.). По сути выдает ошибку:

Последовательность содержит более одного элемента

Описание: во время выполнения текущего веб-запроса возникло необработанное исключение. Пожалуйста, просмотрите трассировку стека для получения дополнительной информации об ошибке и о том, где она возникла в коде.

Сведения об исключении: System.InvalidOperationException: последовательность содержит более одного элемента

Это работает, если я вручную регистрирую каждый класс, один за другим:

container.Register<IRepositoryA, RepositoryA>();
container.Register<IRepositoryB, RepositoryB>();
container.Register<IRepositoryC, RepositoryC>();
/* add more here */

Вот что у меня есть до сих пор:

var repositoryAssembly = typeof(StateRepository).Assembly;

var registrations = 
    from type in repositoryAssembly.GetExportedTypes()
    where type.Namespace == "MappingTool.Repository"
    where type.GetInterfaces().Any()
    where !type.IsAbstract
    select new
    {
        Service = type.GetInterfaces().Single(),
        Implementation = type
    };

foreach (var reg in registrations)
{
    container.Register(reg.Service, reg.Implementation, Lifestyle.Transient);
}

person sksallaj    schedule 16.01.2014    source источник


Ответы (1)


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

container.RegisterManyForOpenGeneric(
    typeof(IRepository<>), 
    typeof(StateRepository).Assembly);

По сути, вы регистрируете закрытую версию интерфейса IRepository<T> как службу с соответствующей реализацией. Другими словами, это будет эквивалентно следующим действиям:

container.Register<IRepository<A>, RepositoryA>();
container.Register<IRepository<B>, RepositoryB>();
container.Register<IRepository<C>, RepositoryC>();
/* add more here */

Поскольку вы не реализуете общий вариант использования, вам придется вернуться к выполнению этого вручную. Вы можете сделать это, написав запрос LINQ, как вы это уже делали, или вы можете использовать метод OpenGenericBatchRegistrationExtensions.GetTypesToRegister, чтобы сделать это проще:

var repositoryTypes = OpenGenericBatchRegistrationExtensions.GetTypesToRegister(
    container, typeof(IRepository<>), repositoryAssembly);

foreach (Type implementationType in repositoryTypes)
{
    Type serviceType = 
        implementationType.GetInterfaces().Where(i => !i.IsGenericType).Single();

    container.Register(serviceType, implementationType, Lifestyle.Transient);
}

Однако убедитесь, что вы понимаете, что нарушаете принципы SOLID, реализуя пользовательские интерфейсы в своих репозиториях. Было бы полезно предотвратить такое нарушение. Подробнее об этом читайте в этой статье.

person Steven    schedule 16.01.2014
comment
Ну, я больше забочусь о дизайне, чем просто о том, как заставить его работать. Считаете ли вы, что мне следует избавиться от необходимости иметь IRepositoryX и вместо этого просто использовать IRepository‹X›? Поэтому вместо StateRepository: Repository‹A›, IStateRepositor я должен изменить на StateRepository: IRepository‹A› - person sksallaj; 16.01.2014
comment
Кстати, я думал, что слежу за тем, как должен быть реализован шаблон репозитория. Считаете ли вы, что шаблон репозитория нарушает принципы SOLID? - person sksallaj; 16.01.2014
comment
@sksallaj: Как Мартин Фаулер описывает шаблон репозитория в своей книге (он использует Creatia с одним методом Matching), репозиторий не нарушает SOLID, но его использование ограничено, поскольку он использует Creatia. Когда вы начнете выставлять IQueryable<T> из своего репозитория, вы нарушите LSP, а когда вы начнете добавлять дополнительные методы запросов к конкретным реализациям репозитория (как предлагает Эрик Эвенс в DDD), вы нарушите SRP, OCP и ISP. Обратите внимание, что это, вероятно, имеет смысл при использовании DDD, но вы должны четко взвесить все за и против. - person Steven; 17.01.2014
comment
Привет, Стив, очень скоро я отмечу твой ответ как ответ. Как вы называете подход к дизайну в упомянутой вами статье? Это шаблон объекта запроса? Это шаблон спецификаций? Вы бы использовали этот шаблон в качестве общей замены шаблону репозитория? Это ваша статья, да? Или это название совпадение... потому что я добавил его в закладки, распечатал и повесил на стену в рамку. - person sksallaj; 17.01.2014
comment
Я также освежил в памяти хороший пост в блоге, упомянутый здесь: blog.lowendahl.net/data-access/ об использовании критериев, он не кажется ограничивающим и имеет сходство с шаблоном, упомянутым в статье в вашем посте. - person sksallaj; 17.01.2014
comment
Эта статья действительно моя. Шаблон репозитория, описанный Мартином Фаулером, содержит метод Matching(Criteria), в котором такие критерии можно комбинировать с другими критериями для построения запроса. Этот критерий является примером шаблона спецификации. Вы можете увидеть этот «шаблон запроса/обработчика» (как я его называю) как объект запроса, но без критериев. Это часть более широкой концепции программирования/архитектуры на основе сообщений. Я по-прежнему использую шаблон репозитория в своих приложениях (хотя в моем последнем приложении репозиторий — это просто фасад поверх общих запросов GetByIdQuery‹T›). - person Steven; 18.01.2014
comment
Но ваши пробеги могут отличаться. Эти объекты запроса явно не содержат никаких критериев. Это сохраняет полный контроль уровня бизнеса/данных над сгенерированными запросами и позволяет сериализовать эти запросы по сети. Разрешение потребителям создавать критерии перемещает бизнес-логику на уровень представления, что, по моему мнению, плохо. - person Steven; 18.01.2014
comment
на самом деле это довольно удивительно, но у меня осталось еще несколько вопросов :) Когда вы решите использовать шаблон репозитория вместо шаблона запроса/обработчика? Есть ли в этом какие-то преимущества? И поскольку вы упомянули об использовании репозитория в качестве фасада, можно ли в любом случае использовать уже реализованный шаблон репозитория для использования обработчиков? Например, есть ли преимущество в том, что шаблон репозитория абстрагирует обработчик запросов? Это то, что вы подразумеваете под фасадом, верно? - person sksallaj; 18.01.2014