小编典典

有关使用Ninject的问题

c#

我按照推荐的步骤操作,将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;
    }
}

这似乎运作良好。但是,我还有几个问题。

  1. Ninject是否确保我DbContext及时得到清理和处置?

  2. 我为所有应用程序的控制器创建了一个基类,以处理任何常见的初始化等。该基类DbContext在构造函数中接受我的参数的实例。但这要求我也将此参数添加到应用程序中的每个控制器。有什么方法可以不需要这个吗?

  3. 我不确定创建自己的实例的成本是多少DbContext。有什么方法可以使优化仅在请求实际上需要我访问数据库时才创建。


阅读 353

收藏
2020-05-19

共1个答案

小编典典

Ninject是否确保及时清理和处置我的DbContext?

按照这个答案

CLR文档指出,创建Disposable对象的任何人都应负责调用Dispose。在这种情况下,对象是由Ninject创建的。这意味着您不应显式调用Dispose。

Ninject处置具有另一个作用域的每个Disposable对象,而不是InTransientScope
在GC收集与创建的对象相关联的作用域对象后立即进行处理。这就是为什么每个Disposable对象都应该是Bindd且作用域不是InTransientScope()的原因。例如,您可以使用NamedScope
扩展中的
InParentScope(),一旦注入到对象中的对象被垃圾回收,它将立即处理该对象。


我为所有应用程序的控制器创建了一个基类,以处理任何常见的初始化等。该基类在构造函数中接受我的DbContext参数的实例。但这要求我也将此参数添加到应用程序中的每个控制器。有什么方法可以不需要这个吗?

简而言之, 切勿 对MVC
Controller使用通用的基类
。类继承倾向于紧密地耦合您的逻辑,并且随着时间的推移将变得难以维护。它还会导致您创建God对象,因为创建多个级别的注入依赖项将意味着每个Controller需要更多的依赖项。

如果您有横切关注点,则应改用全局注册的过滤器。您可以为每个逻辑块创建一个单独的过滤器,这不会像共享基类那样违反单一职责原则。而且,如果您在全局范围内注册过滤器,则可以在此操作过滤器此授权过滤器中使用构造函数注入。您还可以设置自己的属性(不带行为),以根据需要根据控制器和/或操作来设置它们的条件。

例:

由于您明确表示要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实例的成本是多少。有什么方法可以使优化仅在请求实际上需要我访问数据库时才创建。

看一下这个问题:每个Web请求一个DbContext
…为什么?

2020-05-19