Соединение таблиц в приложении MVC очень медленное - PullRequest
1 голос
/ 30 марта 2012

Я немного растерялся из-за того, что одно из моих представлений ASP в моем приложении MVC работает так медленно.

Я выбираю некоторые данные с помощью linq в контроллере.Это выполняется быстро:

public ActionResult Progress(int ID)
        {


            var reviewitems = from ri in db.ReviewItems
                         where ri.Enrolment.Course.LearningArea.LearningAreaID == ID && ri.Review.ReviewSeries.StartDate < DateTime.Now && ri.Review.ReviewSeries.EndDate > DateTime.Now && ri.Progress < 2
                         select ri;


            return View("Progress", reviewitems);


        }

Затем я выполняю цикл foreach в представлении ASP, просматривая каждую строку данных типа 'reviewitem', которая была передана представлению.Опять же, это быстро:

<%foreach (var ri in Model)
  {  %>
<tr>
<td><%= ri.Progress %></td>
</tr>
<%} %>   

Мне нужно больше информации для отображения, поэтому мне нужно присоединиться к таблице «Обзор» (которая снова дает быстрые результаты), а затем к таблице «Студент».Вот где проблема, и она начинает занимать более 30 секунд:

 <%foreach (var ri in Model)
  {  %>
<tr>
<td><%= ri.Review.Student.Surname %></td>
</tr>
<%} %>   

Каждый элемент рецензирования связан с одним уникальным студентом, поэтому я не понимаю, почему это занимает так много времени.У кого-нибудь есть идеи, откуда мне начать искать, почему это так медленно?Предположительно, это как-то связано с таблицей 'Student' (которая на самом деле является представлением SQL Server), но я могу выбрать все строки из нее за секунду, используя SQL?

Ответы [ 4 ]

6 голосов
/ 30 марта 2012

Когда вы пишете запрос LINQ, запрос фактически не выполняется до тех пор, пока вам не понадобятся данные (см. Отложенное выполнение).Ваш первый вызов БД не будет выполнен до тех пор, пока вы не перейдете к ri.Progress, который является одним вызовом.

Когда вы затем запрашиваете ri.Review.Student, вы запрашиваете дополнительные данные.Итак, вы звоните в базу данных.Поскольку вы находитесь в цикле foreach, вы делаете один вызов в базу данных для каждого элемента в этом цикле.

Это проблема "Linq n + 1".Чтобы решить эту проблему, вы должны получить все данные в одном запросе.Вы можете либо заполнить класс модели и строго набрать представление, либо я думаю, что вы можете использовать метод Linq .Include, чтобы включить данные Student в выборку

. Вы можете посмотреть, какие вызовы выполняются.сделано путем запуска SQL Server Profiler и выполнения трассировки

2 голосов
/ 30 марта 2012

Когда вы запрашиваете данные об ученике в цикле foreach, вы делаете множество запросов к базе данных: по одному на каждый раз, когда цикл повторяется (ищите проблему N + 1, если вы заинтересованы).запрос с методом include и составление его списка (чтобы избежать отложенного выполнения) должны решить вашу проблему:

        var reviewitems = (
            from ri in db.ReviewItems
            where ri.Enrolment.Course.LearningArea.LearningAreaID == ID
                && ri.Review.ReviewSeries.StartDate < DateTime.Now
                && ri.Review.ReviewSeries.EndDate > DateTime.Now
                && ri.Progress < 2
            select ri
            ).Include("Review.Student").ToList();
0 голосов
/ 02 апреля 2012

После того, как вышеупомянутые ответы услужливо указали, что это была проблема n + 1, я немного погуглил.Метод include не работал для меня (мой тип var был IQueryable, а не ObjectQuery), но я нашел решение здесь: http://l2sprof.com/Learn/Alerts/SelectNPlusOne

Добавив этот код перед моим запросом linq, страница загружаетсяочень быстро:

var loadoptions = new DataLoadOptions();
        loadoptions.LoadWith<ReviewItem>(ri => ri.Review);
        loadoptions.LoadWith<Review>(r => r.Student);
        db.LoadOptions = loadoptions;
0 голосов
/ 30 марта 2012

Вы можете решить эту проблему, только создав процедуру хранилища на сервере sql (который возвращает оператор выбора со всеми столбцами вашего результата), а затем обновите вашу платформу Entity с новым сложным типом и свяжите этот новый сложный тип с типом возврата вашегоимпорт функции в выбранной процедуре хранения

Результат будет во много раз быстрее, чем раньше в linq Join

, это будет более полезно ... я думаю, что новая процедура => update EF => new ComplexType (то же имяи тип выбранных столбцов statemnet) => Добавить функцию импорта (щелкнуть правой кнопкой мыши по процедуре) => связать новый сложный тип (в типе возврата)

...