Автоматически разрешить интерфейс <T>для реализации <T>в StructureMap (отличаются только универсальным типом T) - PullRequest
22 голосов
/ 25 января 2011

У меня есть интерфейс (IRepository<T>), который в настоящее время расширяется для каждого конкретного хранилища, то есть: IUserRepository : IRepository<User>.

Каждый из этих интерфейсов имеет соответствующие конкретные классы, а именно: UserRepository : Repository<User>, IUserRepository.

Эти отдельные репозитории не добавляют никакой дополнительной функциональности, все они являются пустыми интерфейсами / классами, которые используются просто для передачи обобщений.

Я использую StructureMap для преобразования IUserRepository в UserRepository, используя реестр со сканером сборки и некоторыми соглашениями об именах.

Я бы хотел, чтобы это перешло в более оптимизированное состояние, где вместо прохождения экземпляров IUserRepository и получения разрешения до UserRepository я могу обойти IRepository<User> и разрешить до Repository<User>.

Это избавит от необходимости создавать эти дополнительные пустые интерфейсы и классы.

Я не могу найти способ использовать конфигурацию StructureMap для настройки этого общего отображения. Примерно так:

For(typeof(IRepository<>).Use(typeof(Repository<>)).WithTheGenericTypeFromTheInterfaceSuppliedAsATypeParameter();

Редактировать

Получив первые пару ответов, я хочу уточнить это немного подробнее.

Я не хочу создавать отдельные классы для бита For конфигурации. Я хочу, чтобы в моем коде были следующие классы / интерфейсы:

  • IRepository<T> where T : Entity
  • Repository<T> : IRepository<T> where T : Entity
  • Person : Entity
  • Product : Entity
  • Order : Entity
  • Whatever : Entity

И получить следующие сопоставления, достигнутые с условным обозначением:

IRepository<Person> => Repository<Person>
IRepository<Product> => Repository<Product>
IRepository<Order> => Repository<Order>
IRepository<Whatever> => Repository<Whatever>

Но я не хочу создать сопоставление для каждого, ала:

For<IRepository<Person>>().Use<Repository<Person>>();
For<IRepository<Product>>().Use<Repository<Product>>();
For<IRepository<Order>>().Use<Repository<Order>>();
For<IRepository<Whatever>>().Use<Repository<Whatever>>();

Я хочу одно отображение, которое будет работать для любого IRepository:

For<IRepository<>>().Use<Repository<>>().WithTheSameGenericType();

Я бы затем использовал это для добавления репозиториев в сервисы:

public MyService(IRepository<User> userRepository)

И ожидайте, что это будет разрешено до Repository<User> во время выполнения.

Ответы [ 5 ]

24 голосов
/ 25 января 2011

Оказывается, нет никакого причудливого метода для вызова или какой-либо причудливой проводки, вы просто используете For и Use (не универсальные версии):

public class DataRegistry : Registry
{
    public DataRegistry()
    {
        For(typeof (IRepository<>)).Use(typeof(Repository<>));
    }
}

Когда я ввожуIRepository<Person> теперь он разрешается как Repository<Person>.

Я обнаружил ошибку 104, в которой говорится, что хранилище не подключается к IRepository.Это было потому, что хранилище было помечено abstract.Если сделать его неабстрактным, то исправим эту ошибку, и она будет работать как нужно.

6 голосов
/ 08 декабря 2011

Предлагаю вам взглянуть на метод AddAllTypesOf.У меня был похожий код, и я достиг своих целей, используя его (и сохранил работоспособность функции автоматической регистрации).

В вашем случае вам просто нужно изменить

For<IRepository<Person>>().Use<Repository<Person>>();
For<IRepository<Product>>().Use<Repository<Product>>();
For<IRepository<Order>>().Use<Repository<Order>>();
For<IRepository<Whatever>>().Use<Repository<Whatever>>();

на

AddAllTypesOf(typeof(IRepository<>));

В итоге ваш контейнер будет похож на:

return new Container(x =>
        {
            x.Scan(y =>
            {
                y.TheCallingAssembly();
                y.AddAllTypesOf(typeof(IRepository<>));
                y.WithDefaultConventions();
            });
        });
1 голос
/ 25 января 2011

Вот пример. Карта структуры позволит вам сделать и то и другое.

 //IRepository
 For<IMemberRepository>().Add<MemberRepository>();

 //IRepository<T>
 For<IRepository<Member>>().Add<MemberRepository>();

Тогда полезно запрашивать типы, просто зная общий тип во время выполнения:

Type repositoryType = typeof(IRepository<>).MakeGenericType(modelType);
IocResolve.Resolve(repositoryType);
0 голосов
/ 25 января 2011

Посмотрите интерфейс IRegistrationConvention.

public class DomainRegistry : Registry
{
    public DomainRegistry()
        : base()
    {
        Scan(y =>
        {
            y.AssemblyContainingType<IRepository>();
            y.Assembly(Assembly.GetExecutingAssembly().FullName);
            y.With(new RepositoryTypeScanner());
        });

Где RepositoryTypeScanner:

public class RepositoryTypeScanner : IRegistrationConvention
{
    public void Process(Type type, Registry registry)
    {
      ...
...