MVC3 - Как правильно ввести зависимости с MVC3 и Ninject?

Я пытаюсь перепроектировать существующее приложение, используя внедрение зависимостей с помощью Ninject в MVC3. Вот часть унаследованного поведения, с которым у меня возникают трудности (и да, я знаю, что это плохо, поэтому я пытаюсь его реорганизовать):

protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
    base.OnActionExecuting(filterContext);

    MyUserSession userSession = filterContext.HttpContext.Session[SESSIONKEY_USER] as MyUserSession;

    // if session  empty, rebuild user information
    if (userSession == null)
    {
        string userName = HttpContext.User.Identity.Name;
        userSession = new MyUserSession();

        using (ADSearcher ad = new ADSearcher(ldapPath, excludeOUString.Split(',')))
        {
            // get basic user information from Active Directory
            ADUserInfo aduser = MyActiveDirectorySearcher.GetUserRecord(userName);

            // ... set several properties queries from AD... 
            userSession.propertyXYZ = aduser.propXYZ
        }   

        // if user can proxy as another indivudual, set property
        using (EDMContainer db = new EDMContainer())
        {
            if (db.Proxies.Any(p => p.ProxyLogin == userSession.userLogin))
                userSession.CanProxy == true;
        }

        // save new user object to session
        filterContext.HttpContext.Session[SESSIONKEY_USER] = userSession;

        if(userSession.canProxy)
            filterContext.Result = RedirectToAction("Proxy", "Home");

        return;
    }
}

Поэтому в настоящее время контроллер напрямую использует несколько объектов: Session, ActiveDirectorySearch, EF Database. Я понимаю, что было бы лучше создать класс, который предоставляет один метод "GetUser", маскирующий всю сложность, но я борюсь с тем, как внедрить зависимости.

Если я создаю класс SomeUserProvider, ему также потребуется доступ к сеансу для проверки существующей пользовательской информации, а затем ActiveDirectorySearcher и база данных для перестройки свойств пользователя, если сеанс был пустым.

Моя путаница связана с тем, что самому контроллеру потребуется доступ к ActiveDirectorySearcher в других методах действия, и тогда другие классы также будут использовать ту же базу данных. Ввести ли IActiveDirSearchrer в конструктор контроллера, а затем передать его в ISomeUserProvider? Как насчет IMyDatabase? Это также вводится в конструктор контроллера и передается вниз?

И последнее, но не аренда, ISessionWrapper? Я знаю, что сессия противоречива, но мне нужно отследить, кто является текущим пользователем и кто им проксируется во время каждого запроса (GET и POST). Итак, это также вводится?

Если ответ на каждый из них положительный, то плохо ли иметь 3+ введенных параметра contstuctor?

Я понимаю, что мой вопрос может быть расплывчатым, поэтому, пожалуйста, попросите разъяснений, где это необходимо. Я открыт для любых предложений и рекомендаций. Моя цель - научиться делать это правильно.

Благодарю.

1 ответ

Решение

Я не уверен, что это именно то, что вы ищете, но это должно помочь вам начать реорганизацию вашего приложения для DI

public class YourController : Controller
{
    private readonly ISessionWrapper _sessionWrapper;
    private readonly IActiveDirSearcher _adSearcher;
    private readonly IMyDatabase _database;

    public YourController(ISessionWrapper sessionWrapper,
        IActiveDirSearcher adSearcher, IMyDatabase database)
    {
        this._sessionWrapper = sessionWrapper;
        this._adSearcher = adSearcher;
        this._database = database;
    }

    // now all actions in this controller have a _sessionWrapper,
    // _adSearcher and _database
}

Тогда вы должны связать свои инъекции Ninject способом. Подкласс вашего приложения от NinjectHttpApplication и переопределить OnApplicationStarted а также CreateKernel

public class MvcApplication : NinjectHttpApplication
{
    // ...

    protected override void OnApplicationStarted()
    {
        base.OnApplicationStarted();

        AreaRegistration.RegisterAllAreas();
        RegisterGlobalFilters(GlobalFilters.Filters);
        RegisterRoutes(RouteTable.Routes);
    }

    protected override IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
        kernel.Bind<ISessionWrapper>().To<YourSessionWrapperImplementation>();
        kernel.Bind<IActiveDirSearcher>().To<YourADImplementation>();
        kernel.Bind<IMyDataBase>().To<YourEDMContainerIThink>();

        return kernel;
    }
}

Их реализация описана в вашем вопросе. Однако вы упомянули, что другие действия (и другие классы) зависят от этих реализаций. Хорошие новости - привязки в CreateKernel позаботится о любых отсутствующих зависимостях в другом месте вашего приложения. например

public class MyActiveDirImplementation : IActiveDirSearcher
{
    private readonly IMyDatabase _database;

    // injected automagically WOOHOO!
    public MyActiveDirImplementation(IMyDatabase database)
    {
        this._database = database;
    }

    public ADUserInfo GetUserRecord(string username)
    {
        return _database.GetSomeUserRecord(username);
    }
}

Вы могли бы, конечно, аналогичным образом реализовать свой ISessionWrapper или же IMyDatabase

Другие вопросы по тегам