Entity Framework включает в себя расширение возвращает тонну данных - PullRequest
2 голосов
/ 27 июля 2011

У меня есть две сущности, User и UserPermission. Сущность User содержит все ваши обычные поля, Id, Username, Email и т. Д., А сущность UserPermission имеет два значения: UserId и PermissionId. Я написал метод репозитория GetUserWithPermissions, который первоначально использовал расширение Include и сделал что-то вроде этого:

return dbContext.Users.Include(u => u.UserPermission).Where(u => u.Username.Equals(username)).FirstOrDefault();

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

Id      Username      Email      ...      PermissionId
1       johndoe       john@email.com      1
1       johndoe       john@email.com      2
1       johndoe       john@email.com      3
1       johndoe       john@email.com      4
1       johndoe       john@email.com      5
1       johndoe       john@email.com      6
1       johndoe       john@email.com      7

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

User user = dbContext.Users.Where(u => u.Username.Equals(username)).FirstOrDefault();
if (user != null)
    user.UserPermissions.ToList();
return user;

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

Какой метод лучше? Возвращать много повторяющихся данных или совершать две поездки в базу данных?

Вот SQL-запрос, сгенерированный Entity Framework

SELECT 
[Project2].[Id] AS [Id], 
[Project2].[Username] AS [Username], 
[Project2].[LoweredUsername] AS [LoweredUsername], 
[Project2].[CompanyId] AS [CompanyId], 
[Project2].[FirstName] AS [FirstName], 
[Project2].[LastName] AS [LastName], 
[Project2].[Email] AS [Email], 
[Project2].[C1] AS [C1], 
[Project2].[UserId] AS [UserId], 
[Project2].[PermissionValue] AS [PermissionValue]
FROM ( SELECT 
    [Limit1].[Id] AS [Id], 
    [Limit1].[Username] AS [Username], 
    [Limit1].[LoweredUsername] AS [LoweredUsername],    
    [Limit1].[CompanyId] AS [CompanyId], 
    [Limit1].[FirstName] AS [FirstName], 
    [Limit1].[LastName] AS [LastName], 
    [Limit1].[Email] AS [Email], 
    [Extent2].[UserId] AS [UserId], 
    [Extent2].[PermissionValue] AS [PermissionValue], 
    CASE WHEN ([Extent2].[PermissionValue] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
    FROM   (SELECT TOP (1) 
        [Extent1].[Id] AS [Id], 
        [Extent1].[Username] AS [Username], 
        [Extent1].[LoweredUsername] AS [LoweredUsername],       
        [Extent1].[CompanyId] AS [CompanyId], 
        [Extent1].[FirstName] AS [FirstName], 
        [Extent1].[LastName] AS [LastName], 
        [Extent1].[Email] AS [Email]
        FROM [dbo].[Users] AS [Extent1]
        WHERE [Extent1].[LoweredUsername] = (LOWER(LTRIM(RTRIM(@p__linq__0)))) ) AS [Limit1]
    LEFT OUTER JOIN [dbo].[UserPermissions] AS [Extent2] ON [Limit1].[Id] = [Extent2].[UserId]
)  AS [Project2]
ORDER BY [Project2].[Id] ASC, [Project2].[C1] ASC

Спасибо

Ник

Ответы [ 3 ]

4 голосов
/ 27 июля 2011

Это способ, как это работает. Include коллекций действительно приводит к дублированию столбцов родительской сущности (отличный пример и объяснение см. Здесь: Сколько Включить я могу использовать для ObjectSet в EntityFramework для сохранения производительности? )

И у вас есть компромисс без общего правила, какой путь лучше: одна поездка туда и обратно с Include, но дублированные данные или две поездки туда и обратно без дублированных данных. Что лучше / лучше? Я думаю, что вы должны измерить это в каждом конкретном случае, если вы хотите получить точный ответ.

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

Если вы не хотите загружать с помощью Include, вы можете либо полагаться на отложенную загрузку, либо использовать явную загрузку:

User user = dbContext.Users.Where(u => u.Username.Equals(username))
    .FirstOrDefault();
if (user != null)
    dbContext.Entry(user).Collection(u => u.UserPermissions).Load();
return user;
0 голосов
/ 07 августа 2011

Я задавал похожий вопрос .Есть несколько предложений, как ограничить дублирование.Но я думаю, что было бы сложно заставить Entity Framework генерировать эти запросы.

0 голосов
/ 27 июля 2011

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

это все ПОЛНОСТЬЮ код psuedo / untested.not скомпилировано (поэтому измените, если вам нужно, если вы попробуете;))

var userinfo = from u in dbContext.Users
    Where(u => u.Username.Equals(username))
    Select new { User = u, Permissions = u.UserPermissions.ToList() };

второе примечание: не проверено или даже записано в редакторе для проверки его компиляции. Просто быстрый выстрел из бедра.

идея для рассмотрения?

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