我按照推荐的步骤操作,将Ninject添加到我的MVC应用程序中。DbContext我在控制器的构造函数中添加了一个参数。
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。有什么方法可以使优化仅在请求实际上需要我访问数据库时才创建。
Ninject是否确保及时清理和处置我的DbContext?
按照这个答案:
CLR文档指出,创建Disposable对象的任何人都应负责调用Dispose。在这种情况下,对象是由Ninject创建的。这意味着您不应显式调用Dispose。 Ninject处置具有另一个作用域的每个Disposable对象,而不是InTransientScope 在GC收集与创建的对象相关联的作用域对象后立即进行处理。这就是为什么每个Disposable对象都应该是Bindd且作用域不是InTransientScope()的原因。例如,您可以使用NamedScope 扩展中的 InParentScope(),一旦注入到对象中的对象被垃圾回收,它将立即处理该对象。
CLR文档指出,创建Disposable对象的任何人都应负责调用Dispose。在这种情况下,对象是由Ninject创建的。这意味着您不应显式调用Dispose。
Ninject处置具有另一个作用域的每个Disposable对象,而不是InTransientScope 在GC收集与创建的对象相关联的作用域对象后立即进行处理。这就是为什么每个Disposable对象都应该是Bindd且作用域不是InTransientScope()的原因。例如,您可以使用NamedScope 扩展中的 InParentScope(),一旦注入到对象中的对象被垃圾回收,它将立即处理该对象。
InTransientScope
我为所有应用程序的控制器创建了一个基类,以处理任何常见的初始化等。该基类在构造函数中接受我的DbContext参数的实例。但这要求我也将此参数添加到应用程序中的每个控制器。有什么方法可以不需要这个吗?
简而言之, 切勿 对MVC Controller使用通用的基类。类继承倾向于紧密地耦合您的逻辑,并且随着时间的推移将变得难以维护。它还会导致您创建God对象,因为创建多个级别的注入依赖项将意味着每个Controller需要更多的依赖项。
如果您有横切关注点,则应改用全局注册的过滤器。您可以为每个逻辑块创建一个单独的过滤器,这不会像共享基类那样违反单一职责原则。而且,如果您在全局范围内注册过滤器,则可以在此操作过滤器或此授权过滤器中使用构造函数注入。您还可以设置自己的属性(不带行为),以根据需要根据控制器和/或操作来设置它们的条件。
由于您明确表示要ViewBag基于当前用户设置通用属性,因此可以使用过滤器执行以下操作。
ViewBag
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!"; } } }
MVC具有静态功能GlobalFiltersCollection,您应该在其中静态注册全局过滤器实例。对于具有依赖项且生命周期由DI容器管理的依赖关系的过滤器(例如DbContext),将不会执行此操作。
GlobalFiltersCollection
为了确保按需解析过滤器(按请求),我们IFilterProvider将通过容器进行解析(假设您的Ninject容器已在MVC中注册为DependencyResolver);
IFilterProvider
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实现的过滤器接口的类型注册过滤器的实例。
kernel
// Self-bind our filter, so dependencies can be injected. kernel.Bind<IAuthorizationFilter>().To<CurrentUserProfileFilter>();
在中FilterConfig,注册您的过滤器提供商。
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作为依赖项,其他控制器也不需要。
ArticlesController
MyDbContext
我不确定创建DbContext实例的成本是多少。有什么方法可以使优化仅在请求实际上需要我访问数据库时才创建。
看一下这个问题:每个Web请求一个DbContext …为什么?