work unit pattern patron mvc genericrepository generic framework c# asp.net-mvc entity-framework using idisposable

unit - repository pattern c# mvc



Preguntas sobre Entity Framework Context Lifetime (4)

¿Puede utilizar el método de extensión .ToList() LINQ como tal:

public ActionResult Index() { IEnumerable<MyTable> model; using (var context = new MyEntities()) { model = (from mt in context.MyTable select mt).ToList(); } return View(model); }

Tengo algunas preguntas sobre la vida útil deseada de un contexto de Entity Framework en una aplicación MVC de ASP.NET. ¿No es mejor mantener vivo el contexto durante el menor tiempo posible?

Considere la siguiente acción del controlador:

public ActionResult Index() { IEnumerable<MyTable> model; using (var context = new MyEntities()) { model = context.MyTable; } return View(model); }

El código anterior no funcionará porque el contexto de Entity Framework ha salido de su alcance mientras que la vista muestra la página. ¿Cómo estructurarían los demás el código anterior?


En primer lugar, debe considerar poner su base de datos de acceso a clases separadas.

En segundo lugar, mi solución favorita es usar "un contexto por solicitud" (si está utilizando MVC, creo que es un contexto por controlador).

Edición solicitada:

Echa un vistazo a esta respuesta, quizás también te ayude. Tenga en cuenta que estoy usando formularios web, por lo que no puedo verificarlo en MVC en este momento, pero puede ser útil para usted o al menos darle algunos consejos. https://.com/a/10153406/1289283

Algunos ejemplos de uso de este dbcontext:

public class SomeDataAccessClass { public static IQueryable<Product> GetAllProducts() { var products = from o in ContextPerRequest.Current.Products select o; return products; } }

Entonces puedes hacer algo como esto:

public ActionResult Index() { var products = SomeDataAccessClass.GetProducts(); return View(products); }

Simple, ¿verdad? Ya no tiene que preocuparse por deshacerse de su contexto, solo escribe el código que realmente necesita.

A algunas personas les gusta animar un poco más las cosas agregando el patrón UnitOfWork, o quizás los contenedores IoC ... Pero me gusta más este enfoque debido a su simplicidad.


Estoy de acuerdo con un contexto por solicitud, normalmente lo hacemos al vincular el contexto .InRequestScope usando Ninject, que funciona realmente bien, es:

Bind<MyContext>().ToSelf().InRequestScope();

También es una buena práctica enumerar el conjunto lo más cerca posible de la consulta, es decir:

public ActionResult Index() { IEnumerable<MyTable> model; using (var context = new MyEntities()) { model = (from mt in context.MyTable select mt).ToArray(); } return View(model); }

esto le ayudará a evitar aumentar la consulta involuntariamente desde su vista.


¡Seamos controvertidos!

No estoy de acuerdo con el consenso general de MVC + EF de que mantener un contexto vivo durante toda la solicitud es algo bueno por varias razones:

Aumento del rendimiento bajo ¿Sabe lo costoso que es crear un nuevo contexto de base de datos? Bueno ... " Un DataContext es liviano y no es costoso de crear " eso es de MSDN

Entienda mal el IoC y parecerá que está bien ... hasta que se ponga en marcha. Si configura su contenedor IoC para deshacerse de su contexto y se equivoca, realmente se equivoca. Ahora he visto dos veces enormes pérdidas de memoria creadas desde un contenedor IoC que no siempre eliminan un contexto correctamente. No se dará cuenta de que lo configuró mal hasta que sus servidores comiencen a desmoronarse durante los niveles normales de usuarios concurrentes. ¡No sucederá en el desarrollo, así que haga algunas pruebas de carga!

Carga diferida accidental. Devuelve un IQueryable de sus artículos más recientes para que pueda incluirlos en su página de inicio. Un día se le pide a otra persona que muestre la cantidad de comentarios al lado del artículo respectivo. Así que agregan un simple bit de código a la Vista para mostrar el recuento de comentarios así ...

@foreach(var article in Model.Articles) { <div> <b>@article.Title</b> <span>@article.Comments.Count() comments</span> </div> }

Se ve bien, funciona bien. Pero en realidad no incluyó los comentarios en sus datos devueltos, por lo que ahora esto hará una nueva llamada a la base de datos para cada artículo del ciclo. SELECCIONAR N + 1 tema. 10 artículo = 11 llamadas a bases de datos. Está bien, el código es incorrecto, pero es un error fácil de cometer, por lo que sucederá.

Puede evitar esto cerrando su contexto en su capa de datos. ¿Pero no se romperá el código con una NullReferenceException en el artículo.Comments.Count ()? Sí, así lo forzará a editar la capa de datos para obtener los datos necesarios para la capa de vista. Así es como debe ser.

Olor de código Hay algo malo en golpear la base de datos desde su Vista. Sabes que un IQueryable aún no ha llegado a la base de datos, así que olvida ese objeto. Asegúrate de que tu base de datos esté en contacto antes de abandonar tu capa de datos

Entonces la respuesta

Tu código debería ser (en mi opinión) así

DataLayer:

public List<Article> GetArticles() { List<Article> model; using (var context = new MyEntities()) { //for an example I''ve assumed your "MyTable" is a table of news articles model = (from mt in context.Articles select mt).ToList(); //data in a List<T> so the database has been hit now and data is final } return model; }

Controlador:

public ActionResult Index() { var model = new HomeViewModel(); //class with the bits needed for you view model.Articles = _dataservice.GetArticles(); //irrelevant how _dataService was intialised return View(model); }

Una vez que haya hecho esto y lo haya entendido, quizás pueda comenzar a experimentar con tener un contexto de manejo de contenedores IoC, pero definitivamente no antes. Dirija mi advertencia - He visto dos fallas a gran escala :)

Pero honestamente, haga lo que quiera, la programación es divertida y debería ser una cuestión de preferencia. Solo te estoy diciendo la mía. Pero hagas lo que hagas, no comiences a usar el contexto IoC por controlador o por solicitud solo porque "todos los niños geniales lo están haciendo". Hágalo porque realmente se preocupa por sus beneficios y entienda cómo se hace correctamente.