Вопросы по использованию Ninject
Я прошел рекомендуемые шаги, чтобы добавить Ninject в мое приложение MVC. И я добавил DbContext
аргумент конструкторам моих контроллеров.
контроллер:
public class MyController : BaseController
{
public ArticlesController(MyDbContext context)
: base(context)
{ }
}
Базовый контроллер:
public class BaseController : Controller
{
protected DbContext MyDbContext;
public BaseController(MyDbContext context)
{
MyDbContext = context;
}
}
Кажется, это хорошо работает. Но оставляет меня с несколькими вопросами.
Ninject обеспечивает мой
DbContext
своевременно очищается и утилизируется?Я создал базовый класс для всех контроллеров моего приложения для обработки любой общей инициализации и т. Д. Базовый класс принимает экземпляр моего
DbContext
аргумент в конструкторе. Но для этого требуется, чтобы я также добавил этот аргумент для каждого контроллера в моем приложении. Есть ли способ не требовать этого?Я не уверен, насколько дорого создать экземпляр моего
DbContext
, Есть ли способ сделать оптимизацию, которая создается только в том случае, если запрос действительно требует от меня доступа к базе данных.
1 ответ
Обеспечивает ли Ninject своевременную очистку и удаление моего DbContext?
Согласно этому ответу:
В документации CLR говорится, что тот, кто создает объект Disposable, отвечает за вызов Dispose. В этом случае объект создается Ninject. Это означает, что вы не должны вызывать Dispose явно.
Ninject располагает каждый доступный объект, который имеет другую область видимости, кроме
InTransientScope
как только объект области видимости, к которому привязан созданный объект, собирается GC. Вот почему каждый объект Disposable должен быть привязан с областью, отличной от InTransientScope(). Например, вы можете использовать InParentScope() из расширения NamedScope, которое удалит объект, как только объект, в который он введен, будет собран мусором.
Я создал базовый класс для всех контроллеров моего приложения для обработки любой общей инициализации и т. Д. Базовый класс принимает экземпляр моего аргумента DbContext в конструкторе. Но для этого требуется, чтобы я также добавил этот аргумент для каждого контроллера в моем приложении. Есть ли способ не требовать этого?
Проще говоря, никогда не используйте общий базовый класс для контроллеров MVC. Наследование классов имеет тенденцию тесно связывать вашу логику, и со временем становится трудно поддерживать ее. Это также приводит к созданию божественного объекта, потому что создание нескольких уровней внедренных зависимостей будет означать еще больше необходимых зависимостей для каждого контроллера.
Если у вас есть общие проблемы, вы должны использовать глобально зарегистрированные фильтры. Вы можете создать отдельный фильтр для каждого элемента логики, который не нарушает принцип единой ответственности, как разделяемый базовый класс. И если вы регистрируете свои фильтры глобально, вы можете использовать инжектор конструктора, как в этом фильтре действий или в этом фильтре авторизации. Вы также можете создать свои собственные атрибуты ( без поведения), чтобы сделать их условными для контроллера и / или действия, если это необходимо.
Пример:
Поскольку вы прямо сказали, что хотите установить общий ViewBag
свойства, основанные на текущем пользователе, вот как это можно сделать с помощью фильтров.
CurrentUserProfileFilter
public class CurrentUserProfileFilter : IAuthorizationFilter
{
private readonly MyDbContext context;
public CurrentUserAuthorizationFilter(MyDbContext context)
{
this.context = context;
}
public void OnAuthorization(AuthorizationContext filterContext)
{
var currentUserName = filterContext.HttpContext.User.Identity.Name;
// Set the ViewBag for the request.
filterContext.Controller.ViewBag.UserName = currentUserName;
var userBirthdate =
from user as this.context.AspNetUsers
where user.UserName == currentUserName
select birthdate;
if (userBirthdate.Date == DateTime.Now.Date)
{
filterContext.Controller.ViewBag.Message = "Happy Birthday!";
}
}
}
GlobalFilterProvider
MVC имеет статический GlobalFiltersCollection
где вы должны регистрировать экземпляры фильтра глобально. Это не будет сделано для фильтров, которые имеют зависимости, у которых есть времена жизни, которыми управляет контейнер DI (такой как DbContext
).
Чтобы убедиться, что фильтры разрешены по требованию (по запросу), мы делаем IFilterProvider
который разрешает их через контейнер (при условии, что ваш контейнер Ninject зарегистрирован в MVC как DependencyResolver);
public class GlobalFilterProvider : IFilterProvider
{
private readonly IDependencyResolver dependencyResolver;
public GlobalFilterProvider(IDependencyResolver dependencyResolver)
{
this.dependencyResolver = dependencyResolver;
}
public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
foreach (var filter in this.dependencyResolver.GetServices<IActionFilter>())
{
yield return new Filter(filter, FilterScope.Global, order: null);
}
foreach (var filter in this.dependencyResolver.GetServices<IAuthorizationFilter>())
{
yield return new Filter(filter, FilterScope.Global, order: null);
}
foreach (var filter in this.dependencyResolver.GetServices<IExceptionFilter>())
{
yield return new Filter(filter, FilterScope.Global, order: null);
}
foreach (var filter in this.dependencyResolver.GetServices<IResultFilter>())
{
yield return new Filter(filter, FilterScope.Global, order: null);
}
// If MVC 5, add these as well...
//foreach (var filter in this.dependencyResolver.GetServices<System.Web.Mvc.Filters.IAuthenticationFilter>())
//{
// yield return new Filter(filter, FilterScope.Global, order: null);
//}
}
}
использование
В корне композиции Ninject зарегистрируйте экземпляр вашего фильтра с помощью kernel
для типа или типов интерфейсов фильтра, которые это реализует.
// Self-bind our filter, so dependencies can be injected.
kernel.Bind<IAuthorizationFilter>().To<CurrentUserProfileFilter>();
В FilterConfig
, зарегистрируйте своего поставщика фильтров.
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
// Register the filter provider with MVC.
FilterProviders.Providers.Insert(0, new GlobalFilterProvider(DependencyResolver.Current));
}
}
Теперь при каждом запросе ваши данные пользователя заполняются.
Но что более важно, ваш ArticlesController
не требует MyDbContext
как зависимость, а также не остальные ваши контроллеры.
Я не уверен, насколько дорого создать экземпляр моего DbContext. Есть ли способ сделать оптимизацию, которая создается только в том случае, если запрос действительно требует от меня доступа к базе данных.
Посмотрите на этот вопрос: один DbContext на веб-запрос... почему?