Динамическое использование Ninject для подключения к различным базам данных - PullRequest
1 голос
/ 11 октября 2011

У меня есть приложение MVC, использующее Ninject для подключения к одной базе данных. Теперь мне нужно поддерживать несколько баз данных. В настоящее время мой файл global.asax.cs имеет следующее определение для ninject:

    protected void Application_Start() 
    { 
        AreaRegistration.RegisterAllAreas(); 
        RegisterRoutes(RouteTable.Routes); 

        //Using DI for controllers - use the Ninject custom controller factor 
        ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory()); // Repository config is defined in ninject controller 
    }

А вот как выглядит мой класс контроллера Ninject:

public class NinjectControllerFactory : DefaultControllerFactory 
{ 
    private IKernel kernel = new StandardKernel(new EriskServices()); 

    protected override IController GetControllerInstance(RequestContext context, Type controllerType) 
    { 
        if (controllerType == null) 
            return null; 
        return (IController)kernel.Get(controllerType); 
    } 

    private class EriskServices : NinjectModule 
    { 
        public override void Load() 
        { 
            Bind<IRisksRepository>().To<MySql_RisksRepository>() 
                .WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["mydb1"].ConnectionString); 
        } 
    } 
}

У меня также есть страница входа, которая обрабатывает аутентификацию пользователя. Это делается через LDAP и не требует подключения к базе данных.

Мой вопрос таков: могу ли я связать целую строку соединения после страницы авторизации пользователя? У пользователя будет раскрывающийся список для базы данных, к которой он хочет подключиться, например, «mydb1» или «mydb2» или «mydb3». Каждая строка подключения будет определена в файле web.config.

Пожалуйста, помогите! Спасибо!

1 Ответ

5 голосов
/ 23 октября 2011

Нет, вы не можете связать «после» - во-первых, веб-приложения не имеют состояния и не контролируют порядок событий, но, что более важно, модули Ninject определяют ваш контейнер IoC, и эта конфигурация происходит почти до что-нибудь еще в жизненном цикле приложения или запроса.

Если вы говорите, что пользователь выберет это из раскрывающегося списка, то выбор хранилища является частью вашей логики приложения , а не частью вашей конфигурации IoC. Способ справиться с этим - создать фабричный интерфейс. Реализация может быть тонкой оболочкой вокруг самого ядра Ninject.

public interface IRisksRepositoryFactory()
{
    IRisksRepository GetRepository(string name);
    // Optional: add a GetRepositoryNames() method for populating dropdowns, etc.
}

public class NinjectRisksRepositoryFactory
{
    private readonly IKernel kernel;

    public NinjectRisksRepositoryFactory(IKernel kernel)
    {
        if (kernel == null)
            throw new ArgumentNullException("kernel");
        this.kernel = kernel;
    }

    public IRisksRepository GetRepository(string name)
    {
        return kernel.Get<IRisksRepository>(name);
    }
}

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

Bind<IRisksRepository>()
    .To<MySqlRisksRepository>()
    .InRequestScope()
    .Named("mysql")
    .WithConstructorArgument("connectionString", GetConnectionString("mydb1"));
Bind<IRisksRepository>()
    .To<OracleRisksRepository>()
    .InRequestScope()
    .Named("oracle")
    .WithConstructorArgument("connectionString", GetConnectionString("ordb1"));
Bind<IRisksRepositoryFactory>()
    .To<NinjectRisksRepositoryFactory>();

Также возможно сделать это без явного создания каждой привязки, особенно если все цели имеют одинаковый тип (т. Е. У вас есть только MySqlRisksRepository), передав строку подключения или связанный символ в качестве параметра Get вызов и привязка к контекстному методу, а не к типу - но я бы рекомендовал против этого, так как он действительно плавает против течения, насколько вообще DI идет.

Еще одна вещь: не беспокойтесь, что это напоминает антишаблон "локатор служб", потому что это все, что есть - поверхностное сходство. Когда объекты должны иметь возможность создавать зависимости на лету, рекомендуется создавать фабрики специального назначения вокруг контейнера IoC, поскольку это сводит к минимуму воздействие ядра на один класс, который можно легко заменить, если вы переключитесь на другую среду.

...