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