2010-12-18 33 views
31

Estoy trabajando en una aplicación MVC de ASP.net y tengo una pregunta sobre el uso de constructores para mis controladores.ASP.net MVC Controller - Uso del constructor

Estoy usando Entity Framework y linq para entidades para todas mis transacciones de datos. Necesito acceder a mi modelo de entidad para casi todas mis acciones de controlador. Cuando comencé a escribir la aplicación, estaba creando un objeto de entidad al comienzo de cada método de Acción, realizando cualquier trabajo que necesitaba y luego devolviendo mi resultado.

Me di cuenta de que creaba el mismo objeto una y otra vez para cada método de acción, así que creé una variable miembro privada para el objeto Entity y comencé a crear una instancia en el constructor para cada controlador. Ahora, cada método solo hace referencia a esa variable miembro privada para hacer su trabajo.

Todavía me estoy preguntando de qué manera es la correcta. Me pregunto A.) ​​¿Qué método es el más apropiado? B.) en el método constructor, ¿cuánto tiempo viven esos objetos? C.) hay problemas de rendimiento/integridad con el método constructor?

Gracias

+0

¿Necesita este objeto solo para un controlador? –

+0

Puede ser que deba usar el patrón singleton con instancia privada static y la propiedad de carga lenta de la instancia. –

Respuesta

26

Usted está haciendo las preguntas correctas.

A. Definitivamente no es apropiado crear estas dependencias dentro de cada método de acción. Una de las principales características de MVC es la capacidad de separar las preocupaciones. Al cargar su controlador con estas dependencias, hará que el controlador sea grueso. Estos deben ser inyectados en el controlador. Hay varias opciones para la inyección de dependencia (DI). En general, estos tipos de objetos pueden ser inyectados en el constructor o en una propiedad. Mi preferencia es la inyección de constructor.

B. La vida útil de estos objetos estará determinada por el recolector de basura. GC no es determinista. Por lo tanto, si tiene objetos que tienen conexiones a servicios con recursos limitados (conexiones de bases de datos), entonces es posible que deba asegurarse de cerrar esas conexiones usted mismo (en lugar de confiar en deshacerse de ellas). Muchas veces las preocupaciones de 'vida' se separan en un contenedor de inversión de control (IOC). Hay muchos por ahí. Mi preferencia es Ninject.

C. Los costos de creación de instancias son probablemente mínimos. El costo de las transacciones de la base de datos es donde probablemente desee centrar su atención. Existe un concepto llamado "unidad de trabajo" en el que es posible que desee investigar. Esencialmente, una base de datos puede manejar transacciones de más de una operación de guardado/actualización. Aumentar el tamaño de la transacción puede conducir a un mejor rendimiento de db.

Espero que empieces.

Bob

+1

prueba NINJECT para Inyección de Dependencia! – Omkar

+1

"asegúrese de cerrar esas conexiones usted mismo (en lugar de confiar en deshacerse de ellas)" - Estoy totalmente en desacuerdo. Los controladores descartables son eliminados por un ControllerFactory, no por el GC, y su eliminación es determinista. Ver [esta pregunta] (http://stackoverflow.com/questions/1380019/asp-mvc-when-is-icontroller-dispose-llamado) para más detalles. – Alex

28

RCravens tiene unas excelentes perspectivas. Me gustaría mostrarle cómo puede implementar sus sugerencias.

Sería bueno empezar por definir una interfaz para la clase de acceso de datos para implementar:

public interface IPostRepository 
{ 
    IEnumerable<Post> GetMostRecentPosts(int blogId); 
} 

luego implementar una clase de datos. Los contextos de Entity Framework son baratos de construir, y puede obtener un comportamiento incoherente cuando no se deshace de ellos, por lo que me parece que generalmente es mejor extraer de la memoria los datos que desea y luego eliminar el contexto.

public class PostRepository : IPostRepository 
{ 
    public IEnumerable<Post> GetMostRecentPosts(int blogId) 
    { 
     // A using statement makes sure the context is disposed quickly. 
     using(var context = new BlogContext()) 
     { 
      return context.Posts 
       .Where(p => p.UserId == userId) 
       .OrderByDescending(p => p.TimeStamp) 
       .Take(10) 
       // ToList ensures the values are in memory before disposing the context 
       .ToList(); 
     } 
    } 
} 

Ahora su controlador puede aceptar uno de estos repositorios como un argumento del constructor:

public class BlogController : Controller 
{ 
    private IPostRepository _postRepository; 
    public BlogController(IPostRepository postRepository) 
    { 
     _postRepository = postRepository; 
    } 

    public ActionResult Index(int blogId) 
    { 
     var posts = _postRepository.GetMostRecentPosts(blogId); 
     var model = new PostsModel { Posts = posts }; 
     if(!posts.Any()) {model.Message = "This blog doesn't have any posts yet";} 
     return View("Posts", model); 
    } 

} 

MVC le permite utilizar su propia fábrica de controlador en lugar del defecto, por lo que puede especificar que el COI un marco como Ninject decide cómo se crean los Controladores. Puede configurar su marco de inyección para saber que cuando solicita un IPostRepository debe crear un objeto PostRepository.

Una gran ventaja de este enfoque es que hace que sus controladores sean comprobables por la unidad. Por ejemplo, si usted quiere asegurarse de que su modelo consigue un mensaje cuando no hay mensajes, se puede utilizar un marco de burla como Moq para establecer un escenario en el que sus declaraciones de repositorio no hay mensajes:

var repositoryMock = new Mock<IPostRepository>(); 
repositoryMock.Setup(r => r.GetMostRecentPosts(1)) 
    .Returns(Enumerable.Empty<Post>()); 
var controller = new BlogController(repositoryMock.Object); 
var result = (ViewResult)controller.Index(1); 
Assert.IsFalse(string.IsNullOrEmpty(result.Model.Message)); 

Esto hace es fácil probar el comportamiento específico que está esperando de las acciones de su controlador, sin necesidad de configurar su base de datos ni nada especial como ese. Las pruebas unitarias como esta son fáciles de escribir, deterministas (su estado de aprobación/falla se basa en el código, no en los contenidos de la base de datos), y rápido (a menudo puede ejecutar miles de estos en un segundo).

+0

Eso es genial. Gracias por la ayuda. He tenido un modelo similar (DI) en el fondo de mi mente por un tiempo, pero todavía estaba aprendiendo los pormenores de MVC. Creo que es hora de volver y construir controladores más sostenibles. Muchas gracias. – BZink

+2

¿Cómo enviarías un argumento al constructor? Lo hago de la misma manera, excepto que en el constructor solo tengo 'myRepository = new MyRepository()'. – Cody

+0

@DoctorOreo: Como dije en la publicación, MVC le permite especificar una fábrica de controladores. Puede crear una fábrica de controladores que sepa pasar en el repositorio cuando esté creando su controlador. O puede usar un marco de inyección de Dependencia existente, configurar los enlaces del repositorio, y hacer que MVC solicite al marco DI que cree el controlador. – StriplingWarrior

Cuestiones relacionadas