Контроллер инъекции Castle Windsor с двумя экземплярами одного и того же интерфейса

У меня есть мой контроллер, как это

public class MyController : Controller
{
    private IEntityRepository accountsRepo;
    private IEntityRepository dataRepo;

    public MyController(IEntityRepository accs, IEntityRepository data)
    {
        accountsRepo = accs;
        dataRepo = data;
    }
.....
}

И я установил контейнер таким образом:

public class RepositoriesInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(
            Component.For<IEntityRepository>()
                .ImplementedBy<AccountsRepository>()
                .Named("accs")
                .LifestyleTransient(),
            Component.For<IEntityRepository>()
                .ImplementedBy<DataRepository>()
                .Named("data")
                .LifestyleTransient());

    }
}

Также у меня есть настроенные объекты:

public class PersistenceFacility : AbstractFacility
{
    protected override void Init()
    {
        Kernel.Register(
            Component.For<DbContext>()
                .ImplementedBy<AccountsContext>()
                .LifestylePerWebRequest(),
            Component.For<DbContext>()
                .ImplementedBy<DataContext>()
                .LifestylePerWebRequest());
    }
}

}

... и установлено:

public class PersistenceInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.AddFacility<PersistenceFacility>();
    }
}

Поэтому, когда я использую свой контроллер, оба параметра вводятся в экземпляр AccountsRepository (который был зарегистрирован первым). Конечно, я хочу, чтобы "данные" были соответственно хранилищем данных. Пожалуйста, объясните мне, как правильно бороться с такого рода инъекциями.

РЕДАКТИРОВАТЬ

Как предложил @roman, я внедрил универсальные репозитории:

public interface IRepository : IDisposable
{
    void SaveChanges();

    void ExecuteProcedure(String procedureCommand, params SqlParameter[] sqlParams);
}

public interface IEntityRepository<T> : IRepository
{
    T Context { get; set; }
    DbSet<TEntity> Set<TEntity>() where TEntity : class;
}

public class AccountsRepository : IEntityRepository<AccountsContext>
{
    public AccountsContext Context { get; set; }

    public AccountsRepository(AccountsContext c)
    {
        Context = c;
    }

    public DbSet<TEntity> Set<TEntity>() where TEntity : class
    {
        return Context.Set<TEntity>();
    }

    public virtual void ExecuteProcedure(String procedureCommand, params SqlParameter[] sqlParams)
    {
        Context.Database.ExecuteSqlCommand(procedureCommand, sqlParams);
    }

    public virtual void SaveChanges()
    {
        Context.SaveChanges();
    }

    public void Dispose()
    {
        if (Context != null)
            Context.Dispose();
    }
}

DataRepository выглядит так же, но в какой-то момент я решу иметь только один конкретный класс EntityRepository, но он не имеет отношения к полученным исключениям. Так что после косметических изменений интерфейса мой контроллер стал:

public class HomeController : Controller
{
    private IEntityRepository<AccountsContext> accountsRepo;
    private IEntityRepository<DataContext> dataRepo;

    public HomeController(IEntityRepository<AccountsContext> accs, IEntityRepository<DataContext> data)
    {
        accountsRepo = accs;
        dataRepo = data;
    }
    ....
}

Также я изменил код установщика:

        container.Register(
            Component.For<IEntityRepository<AccountsContext>>()
                .ImplementedBy<AccountsRepository>()
                .LifestyleTransient(),
            Component.For<IEntityRepository<DataContext>>()
                .ImplementedBy<DataRepository>()
                .LifestyleTransient());

А теперь во время процесса разрешения контроллера

        return (IController) kernel.Resolve(controllerType);

Я ловил

Не удается создать компонент "MyMVCProj.DAL.AccountsRepository", так как он имеет зависимости, которые должны быть удовлетворены.

'MyMVCProj.DAL.AccountsRepository' is waiting for the following dependencies:
- Service 'MyMVCProj.DAL.AccountsContext' which was not registered.

Castle.MicroKernel.Handlers.HandlerException: Can't create component     'MyMVCProj.DAL.AccountsRepository' as it has dependencies to be satisfied.

'MyMVCProj.DAL.AccountsRepository' is waiting for the following dependencies:
- Service 'MyMVCProj.DAL.AccountsContext' which was not registered.

Но я установил AccountsContext в логике объекта.

РЕДАКТИРОВАТЬ ++ В соответствии с предложением @Roman я настроил свой объект следующим образом:

public class PersistenceFacility : AbstractFacility
{
    protected override void Init()
    {
        Kernel.Register(
            Component.For<DbContext>()
                .ImplementedBy<AccountsContext>()
                .Named("accctx")
                .LifestylePerWebRequest(),
            Component.For<DbContext>()
                .ImplementedBy<DataContext>()
                .Named("datactx")
                .LifestylePerWebRequest());
    }
}

а также установщик репозиториев:

public class RepositoriesInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(
            Component.For<IEntityRepository<AccountsContext>>()
                .ImplementedBy<AccountsRepository>()
                .Named("accs‌​")
                .LifestyleTransient()
                .DependsOn(Dependency.OnComponent(typeof (DbContext), "accctx")),
            Component.For<IEntityRepository<DataContext>>()
                .ImplementedBy<DataRepository>()
                .Named("data")
                .LifestyleTransient()
                .DependsOn(Dependency.OnComponent(typeof (DbContext), "datactx")));
    }
}

Это исключение, которое я получаю сейчас:

Can't create component 'accs‌​' as it has dependencies to be satisfied.
'accs‌​' is waiting for the following dependencies:
- Service 'MyMVCProj.DAL.AccountsContext' which was not registered.

Но пытаясь решить эту грубую форсировку кода, я закончил с рабочим решением, просто установив конкретные реализации DBContext:

public class PersistenceFacility : AbstractFacility
{
    protected override void Init()
    {
        Kernel.Register(
            Component.For<AccountsContext>().LifestylePerWebRequest(),
            Component.For<DataContext>().LifestylePerWebRequest());
    }
}

И компоненты ядра сейчас:

    AccountsContext PerWebRequest   
    AccountsRepository / IEntityRepository<AccountsContext> Transient
    DataContext PerWebRequest   
    DataRepository / IEntityRepository<DataContext> Transient

И прежде чем они были:

    AccountsContext / DbContext PerWebRequest   
    AccountsRepository / IEntityRepository<AccountsContext> Transient
    DataContext / DbContext PerWebRequest   
    DataRepository / IEntityRepository<DataContext> Transient

Итак, новые вопросы:

Я сделал все вещи идиоматически? Почему такое поведение - там уже был AccountContext с небольшим упоминанием его зависимостей.

1 ответ

Тот факт, что вы ожидаете два экземпляра одного и того же интерфейса, но вам требуется другое поведение для них (путем введения их к двум разным параметрам), подразумевает - на мой взгляд - что они не должны быть одним и тем же интерфейсом, потому что у них разные роли, или обязанности. Для меня было бы больше смысла, если бы IEntityRepository был универсальным классом, и тогда в MyController вам потребовалось бы два разных универсальных типа интерфейса:

public class MyController(IEntityRepository<Account> acc, IEntityRepository<Data> data)

Тем не менее, если вы все еще хотите делать подобные вещи, я предлагаю вам использовать CollectionResolver, который позволит классу MyController получить IEnumerable. Таким образом, вы получите оба экземпляра, но вам нужно выбрать подходящий для использования в зависимости от ваших потребностей, что я еще раз подчеркну, я думаю, что это неправильный подход для этого.

Чтобы использовать CollectionResolver, вам нужно зарегистрировать его в контейнере Windsor следующим образом:

var container = new WindsorContainer();
container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel));

И тогда MyController будет выглядеть так:

 public class MyController(IEnumerable<IEntityRepository> repositories)
 {
    accountsRepo = repositories.Where(...);
    dataRepo = repositories.Where(...);
 }
Другие вопросы по тегам