【问题标题】:Entity Framework include Extension Returns a Ton of Data实体框架包括扩展返回大量数据
【发布时间】:2011-07-27 17:28:33
【问题描述】:

我有两个实体,用户和用户权限。 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 扩展基本上只是将两个表扁平化为一个,因此所有用户字段都针对每一个重复与用户关联的用户权限。返回的数据如下所示:

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 查询

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

谢谢

尼克

【问题讨论】:

  • 看起来我误解了你的问题 - 我认为这是关于优化,你的两种方法的结果都是一样的。
  • @BrokenGlass 所以你的意思是在一次数据库调用中返回一堆重复数据与在两次数据库调用中返回更少的数据是一样的?
  • 否 - 生成的 User 实体相同,性能可能不同。

标签: entity-framework include code-first


【解决方案1】:

这就是它的工作方式。 Include 的集合确实会导致父实体的列重复(参见此处以获取很好的示例和解释:How many Include I can use on ObjectSet in EntityFramework to retain performance?

并且您可以在没有一般规则的情况下进行权衡,哪种方式更好:与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;

【讨论】:

  • 你的第三段是我所想的例子,这种情况符合你所说的,一个有很多列和大量子实体的父级。感谢您的提示。
【解决方案2】:

只是想知道您是否可以执行一个查询来选择具有权限的新对象作为列表。

这是所有完全伪代码/未经测试。未编译(如果您尝试,请根据需要进行修改;))

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

第二个注意,这是测试,甚至在编辑器中编写以测试它是否可以编译。只是从臀部快速射击。

要考虑的想法?

【讨论】:

  • 很有趣,但是:我相信它会导致与Include 相同的数据重复,因为数据库只发送一个结果集,并且它始终具有正方形或矩形的列和行。 EF 将此数据矩阵“分派”到实体属性中,从而解决数据重复问题。它发生在客户端,而不是数据库服务器上。 (顺便说一句:您需要删除 ToList()。它在预计的集合中不起作用。)
  • 正如我所说,这是在黑暗中拍摄的。 :)
【解决方案3】:

我问过类似的question。有一些建议如何限制重复。但我想让 Entity Framework 生成这些查询会很困难。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-08
    • 2013-06-21
    • 1970-01-01
    • 2021-04-03
    相关资源
    最近更新 更多