c# asp.net asp.net-mvc dependency-injection ninject

c# - Preguntas sobre el uso de Ninject



asp.net asp.net-mvc (1)

¿Ninject se asegura de que mi DbContext se limpie y elimine 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 desecha todos los objetos desechables que tienen otro alcance que no sea InTransientScope tan pronto como GC recolecta el objeto de alcance al que está vinculado el objeto creado . Es por eso que cada objeto desechable debe vincularse con un ámbito que no sea InTransientScope (). Por ejemplo, puede usar InParentScope () de la extensión NamedScope que desechará el objeto tan pronto como el objeto en el que se inyecta se recolecte basura.

He creado 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 requiere que yo también agregue este argumento a cada controlador en mi aplicación. ¿Hay alguna manera de no requerir esto?

En pocas palabras, nunca use una clase base común para controladores MVC . La herencia de clases tiende a acoplar estrechamente su lógica y se hace difícil mantenerla con el tiempo. También tiende a llevarte a crear un objeto dios , porque crear múltiples niveles de dependencias inyectadas significaría incluso más dependencias requeridas para cada controlador.

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

Ejemplo:

Dado que usted dijo explícitamente que desea establecer propiedades comunes de ViewBag basadas en el usuario actual, así es como 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 a nivel mundial. Esto no va a hacer para los filtros que tienen dependencias que tienen vidas administradas por el contenedor DI (como DbContext ).

Para garantizar que los filtros se resuelvan a pedido (por solicitud), creamos un IFilterProvider que los resuelve a través del contenedor (suponiendo que su contenedor Ninject esté registrado con MVC como 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); //} } }

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<IAuthorizationFilter>().To<CurrentUserProfileFilter>();

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, se rellenan sus datos de usuario.

Pero lo más importante, su ArticlesController no requiere MyDbContext como dependencia, ni el resto 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 requiere que acceda a la base de datos?

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

Seguí los pasos recomendados para agregar Ninject a mi aplicación MVC. Y agregué un argumento 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 se asegura de que mi DbContext se limpie y DbContext manera oportuna?

  2. He creado 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 requiere que yo también agregue este argumento a cada controlador en mi aplicación. ¿Hay alguna manera 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 requiere que acceda a la base de datos?