ASP.NET MVC & EF4 Entity Framework - Существуют ли какие-либо проблемы с производительностью при использовании сущностей по сравнению с извлечением только тех полей, которые мне нужны? - PullRequest
0 голосов
/ 12 января 2011

Допустим, у нас есть 3 таблицы: пользователи, продукты, покупки. Существует представление, которое должно отображать покупки, сделанные пользователем.

Я мог бы найти необходимые данные, выполнив:

from p in DBSet<Purchases>.Include("User").Include("Product") select p;

Тем не менее, я обеспокоен тем, что это может повлиять на производительность, поскольку он будет извлекать полные объекты. Кроме того, я мог бы выбрать только те поля, которые мне нужны:

from p in DBSet<Purchases>.Include("User").Include("Product") select new SimplePurchaseInfo() { UserName = p.User.name, Userid = p.User.Id, ProductName = p.Product.Name ... etc };

Итак, мой вопрос: Какова лучшая практика в этом?

== РЕДАКТИРОВАТЬ

Спасибо за все ответы.

[ВОПРОС 1]: Я хочу знать, должны ли все представления работать с плоскими моделями представления с очень конкретными данными для этого представления, или же они должны содержать объекты сущности.

Реальный пример: Отзывы пользователей Товары

var query = from dr in productRepository.FindAllReviews()
            where dr.User.UserId = 'userid'
            select dr;
string sql = ((ObjectQuery)query).ToTraceString();

SELECT [Extent1].[ProductId] AS [ProductId], 
       [Extent1].[Comment] AS [Comment], 
       [Extent1].[CreatedTime] AS [CreatedTime], 
       [Extent1].[Id] AS [Id], 
       [Extent1].[Rating] AS [Rating], 
       [Extent1].[UserId] AS [UserId], 
       [Extent3].[CreatedTime] AS [CreatedTime1], 
       [Extent3].[CreatorId] AS [CreatorId], 
       [Extent3].[Description] AS [Description], 
       [Extent3].[Id] AS [Id1], 
       [Extent3].[Name] AS [Name], 
       [Extent3].[Price] AS [Price], 
       [Extent3].[Rating] AS [Rating1], 
       [Extent3].[ShopId] AS [ShopId], 
       [Extent3].[Thumbnail] AS [Thumbnail], 
       [Extent3].[Creator_UserId] AS [Creator_UserId], 
       [Extent4].[Comment] AS [Comment1], 
       [Extent4].[DateCreated] AS [DateCreated], 
       [Extent4].[DateLastActivity] AS [DateLastActivity], 
       [Extent4].[DateLastLogin] AS [DateLastLogin], 
       [Extent4].[DateLastPasswordChange] AS [DateLastPasswordChange], 
       [Extent4].[Email] AS [Email], 
       [Extent4].[Enabled] AS [Enabled], 
       [Extent4].[PasswordHash] AS [PasswordHash], 
       [Extent4].[PasswordSalt] AS [PasswordSalt], 
       [Extent4].[ScreenName] AS [ScreenName], 
       [Extent4].[Thumbnail] AS [Thumbnail1], 
       [Extent4].[UserId] AS [UserId1], 
       [Extent4].[UserName] AS [UserName]
       FROM    [ProductReviews] AS [Extent1]
       INNER JOIN [Users] AS [Extent2] ON [Extent1].[UserId] = [Extent2].[UserId]
       LEFT OUTER JOIN [Products] AS [Extent3] ON [Extent1].[ProductId] = [Extent3].[Id]
       LEFT OUTER JOIN [Users] AS [Extent4] ON [Extent1].[UserId] = [Extent4].[UserId]
       WHERE N'615005822' = [Extent2].[UserId]

или

from d in productRepository.FindAllProducts()
from dr in d.ProductReviews
where dr.User.UserId == 'userid'
orderby dr.CreatedTime
select new ProductReviewInfo()
       {
           product = new SimpleProductInfo() { Id = d.Id, Name = d.Name, Thumbnail = d.Thumbnail, Rating = d.Rating },
           Rating = dr.Rating,
           Comment = dr.Comment,
           UserId = dr.UserId,
           UserScreenName = dr.User.ScreenName,
           UserThumbnail = dr.User.Thumbnail,
           CreateTime = dr.CreatedTime
       };

SELECT 
[Extent1].[Id] AS [Id], 
[Extent1].[Name] AS [Name], 
[Extent1].[Thumbnail] AS [Thumbnail], 
[Extent1].[Rating] AS [Rating], 
[Extent2].[Rating] AS [Rating1], 
[Extent2].[Comment] AS [Comment], 
[Extent2].[UserId] AS [UserId], 
[Extent4].[ScreenName] AS [ScreenName], 
[Extent4].[Thumbnail] AS [Thumbnail1], 
[Extent2].[CreatedTime] AS [CreatedTime]
FROM    [Products] AS [Extent1]
INNER JOIN [ProductReviews] AS [Extent2] ON [Extent1].[Id] = [Extent2].[ProductId]
INNER JOIN [Users] AS [Extent3] ON [Extent2].[UserId] = [Extent3].[UserId]
LEFT OUTER JOIN [Users] AS [Extent4] ON [Extent2].[UserId] = [Extent4].[UserId]
WHERE N'userid' = [Extent3].[UserId]
ORDER BY [Extent2].[CreatedTime] ASC

[ВОПРОС 2]: Что за уродливые внешние соединения?

Ответы [ 4 ]

1 голос
/ 12 января 2011

Жесткая загрузка с .Include не очень хорошо играет, когда вы хотите фильтровать (или упорядочить).

Первый запрос в основном такой:

select p.*, u.*, p2.*
from products p
left outer join users u on p.userid = u.userid
left outer join purchases p2 on p.productid = p2.productid
where u.userid == @p1

Это действительно то, что вы хотите?

Существует представление, которое должно отображать покупки, сделанные пользователем.

Ну тогда почему вы включаете "Продукт"?

Разве это не должно быть просто:

from p in DBSet<Purchases>.Include("User") select p;

Ваш второй запрос будет ошибкой. Вы должны проецировать объект на модель или анонимный тип - не случайный класс / DTO.

Если честно, самый простой и наиболее эффективный вариант *1023* в вашем текущем сценарии - это запрос самого FK:

var purchasesForUser = DBSet<Purchases>.Where(x => x.UserId == userId);

Что должно дать:

select p.*
from products p
where p.UserId == @p1

Разумеется, в приведенном выше запросе необходимо включить внешние ключи в модель.

Если у вас нет FK в вашей модели, вам понадобится дополнительная хитрость LINQ-Entities в форме проекции анонимного типа.

В целом, не выходите в поисках оптимизации . Создавайте запросы, которые соответствуют сценарию / бизнес-требованиям, затем оптимизируйте, если необходимо, или ищите альтернативы LINQ-Entities, такие как хранимые процедуры, представления или скомпилированные запросы.

Помните: преждевременная оптимизация - корень всего зла.

* РЕДАКТИРОВАТЬ - В ответ на обновление вопроса *

[ВОПРОС 1]: Я хочу знать, должны ли все представления работать с плоскими моделями представления с очень конкретными данными для этого представления, или же они должны содержать объекты-сущности.

Да - ViewModel должен содержать только то, что требуется для этого View. В противном случае, почему есть ViewModel? С таким же успехом вы можете привязаться к модели EF. Итак, настройте ViewModel, для которого нужны только те поля, которые необходимы для представления.

[ВОПРОС 2]: Что за уродливые внешние соединения?

Это поведение по умолчанию для .Include. .Include всегда создает левое внешнее соединение.

1 голос
/ 12 января 2011

В общем, извлекайте только то, что вам нужно, но имейте в виду, что извлекаете достаточно информации, чтобы ваше приложение не было слишком болтливым, поэтому, если вы можете объединить несколько вещей вместе, сделайте это, в противном случае вы заплатите стоимость сетевого трафика.каждый раз, когда вам нужно вернуться в базу данных и получить еще кое-что.

В этом случае, если вам понадобится только эта информация, я бы выбрал второй подход (если это то, что вам действительно нужно).

1 голос
/ 12 января 2011

Я думаю, что второй запрос вызовет исключение, потому что вы не можете отобразить результат на неотображенный тип .NET в Linq-to-entity. Вы должны вернуть анонимный тип и сопоставить его с вашим объектом в Linq-to-objects или использовать некоторые расширенные концепции для проекций - QueryView (проекции в ESQL) или DefiningQuery (пользовательский запрос SQL, сопоставленный с новой сущностью только для чтения).

Как правило, это больше о дизайне ваших сущностей. Если вы выберете одну маленькую сущность, не будет большой разницей загружать все это вместо проекции. Если вы выбираете список объектов, вы должны учитывать прогнозы - особенно если таблицы содержат столбцы, такие как nvarchar (max) или varbinar (max), которые не нужны в вашем результате!

0 голосов
/ 12 января 2011

Оба создают почти один и тот же запрос: выберите из одной таблицы с двумя внутренними объединениями.Единственное, что меняется с точки зрения базы данных, это количество возвращаемых полей, но это не должно иметь большого значения.

Я думаю, что здесь DRY выигрывает от снижения производительности (если оно вообще существует): так что мойВызов идет для первого варианта.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...