Вопрос по asp.net-mvc, using, entity-framework, idisposable, c# – Вопросы о жизненном контексте контекста Entity Framework

20

У меня есть несколько вопросов о желаемом времени жизни контекста Entity Framework в приложении ASP.NET MVC. Не лучше ли сохранить контекст живым в кратчайшие сроки?

Рассмотрим следующее действие контроллера:

public ActionResult Index()
{
    IEnumerable<MyTable> model;

    using (var context = new MyEntities())
    {
        model = context.MyTable;
    }

    return View(model);
}

Приведенный выше код не будет работать, потому что контекст Entity Framework вышел за рамки видимости, пока представление отображает страницу. Как другие будут структурировать код выше?

model = context.MyTable.ToList() - ToList() выполнит ваш запрос. В вашем случае IQueryable действительно не будет работать вне контекста. Andrei

Ваш Ответ

4   ответа
6

мы обычно делаем это, связывая контекст .InRequestScope с помощью Ninject, который действительно хорошо работает, это:

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

Также очень полезно перечислять множество как можно ближе к запросу, т.е.

public ActionResult Index()
{
    IEnumerable<MyTable> model;

    using (var context = new MyEntities())
    {
        model = (from mt in context.MyTable
                select mt).ToArray();
    }
    return View(model);
}

это поможет вам избежать непреднамеренного увеличения запроса с вашей точки зрения.

1

.ToList() метод расширения как таковой:

public ActionResult Index()
{
    IEnumerable<MyTable> model;

    using (var context = new MyEntities())
    {
        model = (from mt in context.MyTable
                select mt).ToList();
    }
    return View(model);
}
Да, я могу, но это не решает потенциальные проблемы с производительностью из-за повторного создания контекста в течение одного HTTP-запроса Вот почему я ищу примеры того, как это делают другие. Jonathan Wood
Вы измерили влияние производительности? Это соответствует вашим ожиданиям / требованиям?
Я не измерял производительность. Я просто пытаюсь выяснить, как другие имеют дело с этим конкретным аспектом EF. У меня проблемы с поиском множества примеров. Jonathan Wood
46

Let's get controversial!

Я не согласен с общим консенсусом MVC + EF о том, что поддержание контекста в течение всего запроса полезно по ряду причин:

Low performance increase Вы знаете, как дорого стоит создание нового контекста базы данных? Ну ...A DataContext is lightweight and is not expensive to create& Quot; это изMSDN

Get the IoC wrong and it'll seem fine.. until you go live Если вы настроили свой IoC-контейнер для того, чтобы избавиться от вашего контекста, и вы ошиблись, вы действительно поняли его неправильно. Я & APOS; веtwice теперь наблюдаются огромные утечки памяти, созданные из контейнера IoC, который не всегда корректно удаляет контекст. Вы не поймете, что настроили неправильно, пока ваши серверы не рухнут при нормальном уровне одновременных пользователей. Это не произойдет в процессе разработки, поэтому проведите несколько нагрузочных тестов!

Accidental lazy loading Вы возвращаете IQueryable из ваших последних статей, чтобы вы могли перечислить их на своей домашней странице. Однажды кого-то еще просят показать количество комментариев рядом с соответствующей статьей. Таким образом, они добавляют простой кусок кодаto the View чтобы показать количество комментариев, как это ...

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

Выглядит отлично, отлично работает. Но на самом деле вы не включили комментарии в свои возвращенные данные, так что теперь это сделает новый вызов базы данных для каждой статьи в цикле. ВЫБЕРИТЕ N + 1 выпуск. 10 статей = 11 вызовов базы данных. Хорошо, так что код неправильный, но сделать ошибку легко, так что это случится.

Вы можете предотвратить это, закрыв свой контекст на уровне данных. Но не сломается ли код с исключением NullReferenceException для article.Comments.Count ()? Да, так и будет заставлять вас редактироватьData слой, чтобы получить данные, необходимые для слоя просмотра. Вот как это должно быть.

Code smell Что-то не так с попаданием в базу данных из вашего View. Вы знаете, что IQueryable на самом деле еще не попал в базу данных, так что забудьте об этом объекте. Убедитесь, что ваша база данных поражена, прежде чем она покинет слой данных.

So the answer

Ваш код должен быть (на мой взгляд) таким

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

контроллер:

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

Как только вы это сделаете и поймете это, возможно, вы сможете начать экспериментировать с контекстом дескриптора контейнера IoC, но определенно не раньше. Возглавьте мое предупреждение - я видел два крупных сбоя :)

Но если честно, делай, что тебе нравится, программирование - это весело и должно быть вопросом предпочтений. Я просто говорю тебе свое. Но что бы вы ни делали, не начинайте использовать контекст IoC для контроллера или для запроса только потому, что «все крутые дети делают это». Сделайте это, потому что вы действительно заботитесь о его преимуществах и понимаете, как это делается правильно.

@BritishDeveloper: Вопрос был о DbContext из пространства имен System.Data.Entity (класс сущности dbcontext). Я не нашел ничего "легковесности" в описании этого контекста вMSDN, У вас есть другие доказательства?
Я думаю, что стоит процитировать весь абзац из MSDN: "В общем случае экземпляр DataContext рассчитан на одну единицу работы". однако ваше заявление определяет этот термин. DataContext легок и не дорог в создании. Типичное приложение LINQ to SQL создает экземпляры DataContext в области действия метода или в качестве члена недолговечных классов, представляющих логический набор связанных операций с базой данных. & Quot;
После чуть более года борьбы с нашими собственными уровнями данных мы начинаем создавать их ПРОСТО, как вы описали здесь ... именно по этим причинам. Вы на месте. Что касается кэширования, у нас есть целый объект, который кэширует DATA (не контекст или объекты), который обрабатывается на уровне бизнес-логики (или контроллера), а не на уровне данных.
Рассматривая это (годы спустя), я бы добавил еще одну мысль: проблема не ограничивается толькоcreating контекст базы данных. В больших проектах я часто вижу, что на одни и те же данные ссылаются из разных мест. Если каждый раз создается новый контекст базы данных, то данные должны извлекаться каждый раз, а не кэшироваться в контексте, открытом для длины запроса. Jonathan Wood
Ах, это было 2 года назад, cba, чтобы найти больше доказательств для вас. Тем не менее, у меня есть профессиональный опыт использования этого метода в течение последних нескольких лет на больших сайтах без проблем и проблем с производительностью.
2

вы должны рассмотреть возможность предоставления доступа к вашей базе данных отдельным классам.

Во-вторых, мое любимое решение - использовать «один контекст на запрос». (если вы используете MVC, я полагаю, что это один контекст на контроллер).

Requested edit:

Посмотрите на этот ответ, возможно, он вам тоже поможет. Обратите внимание, что я использую веб-формы, поэтому в настоящее время не могу проверить это в MVC, но это может быть полезным для вас или, по крайней мере, дать вам некоторые подсказки.https://stackoverflow.com/a/10153406/1289283

Некоторые примеры использования этого dbcontext:

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

Тогда вы можете сделать что-то вроде этого:

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

Просто, правда? Вам больше не нужно беспокоиться об удалении своего контекста, вы пишете только тот код, который вам действительно нужен.

Некоторые люди предпочитают еще немного оживить, добавив шаблон UnitOfWork или, возможно, контейнеры IoC ... Но мне больше нравится этот подход из-за его простоты.

Спасибо, но я пытаюсь выяснить, как люди будут структурировать код, чтобы в запросе был только один контекст? Где я могу его создать, и как я могу обеспечить его своевременное удаление после выполнения запроса? Jonathan Wood

Похожие вопросы