Preguntas sobre el uso de Ninject

Pasé por los pasos recomendados para agregar Ninject a mi aplicación MVC. Y agregué un argumento de DbContext a los constructores de mis controladores.

Controlador:

 public class MyController : BaseController { public ArticlesController(MyDbContext context) : base(context) { } } 

Controlador base:

 public class BaseController : Controller { protected DbContext MyDbContext; public BaseController(MyDbContext context) { MyDbContext = context; } } 

Esto parece funcionar bien. Pero me deja con algunas preguntas.

  1. ¿Ninject garantiza que mi DbContext se limpia y desecha de manera oportuna?

  2. DbContext una clase base para todos los controladores de mi aplicación para manejar cualquier inicialización común, etc. La clase base acepta una instancia de mi argumento DbContext en el constructor. Pero esto también me obliga a agregar este argumento a cada controlador de mi aplicación. ¿Hay alguna forma de no requerir esto?

  3. No estoy seguro de lo caro que es crear una instancia de mi DbContext . ¿Hay alguna manera de hacer la optimización que solo se crea si la solicitud realmente me exige acceder a la base de datos?

¿Ninject garantiza que mi DbContext se limpia y desecha de manera oportuna?

Según esta respuesta :

La documentación de CLR establece que quien crea un objeto desechable es responsable de llamar a Dispose. En este caso, el objeto es creado por Ninject. Eso significa que no debe llamar a Dispose explícitamente.

Ninject dispone cada objeto desechable que tiene otro scope que no sea InTransientScope tan pronto como el objeto de scope al cual está vinculado el objeto creado es recogido por GC . Es por eso que cada objeto desechable debe vincularse con un ámbito que no sea InTransientScope (). Por ejemplo, puede usar InParentScope () desde la extensión de NamedScope que desechará el objeto tan pronto como el objeto en el que se inyecta sea basura.


Creé una clase base para todos los controladores de mi aplicación para manejar cualquier inicialización común, etc. La clase base acepta una instancia de mi argumento DbContext en el constructor. Pero esto también me obliga a agregar este argumento a cada controlador de mi aplicación. ¿Hay alguna forma de no requerir esto?

En pocas palabras, nunca use una clase base común para Controladores MVC . La herencia de clase tiende a unir estrechamente su lógica y se vuelve difícil mantenerla a lo largo del tiempo. También tiende a llevarlo a crear un objeto de dios , porque crear múltiples niveles de dependencias inyectadas significaría aún más dependencias requeridas para cada controlador.

Si tiene inquietudes transversales , debería usar filtros registrados globalmente . Puede hacer un filtro separado para cada parte de la lógica, lo que no viola el Principio de Responsabilidad Individual como lo haría una clase base compartida. Y si registra sus filtros globalmente, puede usar inyección de constructor como en este filtro de acción o este filtro de autorización . También puede crear sus propios atributos ( sin comportamiento ) para que sean condicionales por controlador y / o acción, si es necesario.

Ejemplo:

Dado que usted dijo explícitamente que quería establecer las propiedades comunes de ViewBag basadas en el usuario actual, así es cómo se puede hacer con los filtros.

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 tiene una GlobalFiltersCollection estática donde se supone que debe registrar instancias de filtro globalmente. Esto no DbContext para los filtros que tienen dependencias que tienen vidas controladas por el contenedor DI (como DbContext ).

Para garantizar que los filtros se resuelven a petición (por solicitud), creamos un IFilterProvider que los resuelve a través del contenedor (suponiendo que su contenedor Ninject está registrado en MVC como DependencyResolver);

 public class GlobalFilterProvider : IFilterProvider { private readonly IDependencyResolver dependencyResolver; public GlobalFilterProvider(IDependencyResolver dependencyResolver) { this.dependencyResolver = dependencyResolver; } public IEnumerable GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) { foreach (var filter in this.dependencyResolver.GetServices()) { yield return new Filter(filter, FilterScope.Global, order: null); } foreach (var filter in this.dependencyResolver.GetServices()) { yield return new Filter(filter, FilterScope.Global, order: null); } foreach (var filter in this.dependencyResolver.GetServices()) { yield return new Filter(filter, FilterScope.Global, order: null); } foreach (var filter in this.dependencyResolver.GetServices()) { yield return new Filter(filter, FilterScope.Global, order: null); } // If MVC 5, add these as well... //foreach (var filter in this.dependencyResolver.GetServices()) //{ // yield return new Filter(filter, FilterScope.Global, order: null); //} } } 

Uso

En su raíz de composición de Ninject, registre la instancia de su filtro con el kernel para el tipo o tipos de interfaces de filtro que implementa.

 // Self-bind our filter, so dependencies can be injected. kernel.Bind().To(); 

En FilterConfig , registre su proveedor de filtros.

 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)); } } 

Ahora en cada solicitud, sus detalles de usuario están llenos.

Pero más importante aún, su ArticlesController no requiere MyDbContext como una dependencia, ni el rest de sus controladores.

No estoy seguro de lo caro que es crear una instancia de mi DbContext. ¿Hay alguna manera de hacer la optimización que solo se crea si la solicitud realmente me exige acceder a la base de datos?

Eche un vistazo a esta pregunta: un DbContext por solicitud web … ¿por qué?