我完成了建议的步骤,将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
的实例有多昂贵。有没有办法进行优化,只有在请求实际要求我访问数据库时才会创建它。
答案 0 :(得分:10)
Ninject能否确保我的DbContext得到及时清理和处理?
根据this answer:
CLR文档指出创建Disposable对象的人负责调用Dispose。在这种情况下,对象由Ninject创建。这意味着你不应该明确地调用Dispose。
Ninject处理具有
InTransientScope
as soon as the scope object to which the created object is tied is collected by GC以外的其他范围的每个Disposable对象。这就是为什么每个Disposable对象应该使用非InTransientScope()的范围进行Bindd的原因。例如。你可以使用来自the NamedScope extension的InParentScope(),它会在注入对象时立即处理对象。
我为所有应用程序的控制器创建了一个基类来处理任何常见的初始化等。基类在构造函数中接受我的DbContext参数的实例。但这需要我也将此参数添加到我的应用程序中的每个控制器。有没有办法不要求这个?
简单地说,never use a common base class for MVC Controllers。类继承倾向于紧密耦合您的逻辑,并且随着时间的推移变得难以维护。它还会导致您创建god object,因为创建多个注入依赖项级别意味着每个Controller都需要更多依赖项。
如果您有cross-cutting concerns,则应使用globally registered filters。您可以为每个逻辑单独创建一个过滤器,它不会像共享基类那样违反单一责任原则。如果您在全局注册过滤器,则可以使用this action filter或this authorization filter中的构造函数注入。如有必要,您还可以创建自己的属性(without behavior),使其成为每个控制器和/或操作的条件。
由于您明确表示要根据当前用户设置常见的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
)的过滤器,这不会做。
为了确保按需解析过滤器(按请求),我们创建一个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
作为依赖项,其他控制器也不需要。{/ p>
我不确定创建DbContext的实例有多昂贵。有没有办法进行优化,只有在请求实际要求我访问数据库时才会创建它。