1

我有这个数据库架构:

User  <--  UserRole  -->  Role

Role 表有几列,但我只对主键感兴趣,我可以从 UserRole 的外键中获取它,所以我根本不需要加入 Role 表,但我不知道如何以避免使用 EF 加入该表。

我试过这段代码:

context.Users.Where(u => u.UserId == x).Single().Roles.Select(r => r.RoleId);

这会生成两个查询。一个在用户表上,一个在 UserRole 和 Role 的连接上。我当然可以使用 .Include("Roles") 或 SelectMany() 将其减少为一个查询,但该查询将连接三个表。有没有办法摆脱多余的加入?我希望我的 SQL 类似于:

SELECT u.*, ur.RoleId
FROM User u
LEFT OUTER JOIN UserRole ur on ur.UserId = u.UserId
WHERE ...

实际上,我使用的是 automapper,但我认为这个示例演示了同样的问题。Roles 是一个小表,所以这次我可以忍受性能损失,但它让我烦恼的是,我不能像手写 SQL 那样高效。有没有其他人在我之前遇到过这个问题并找到了解决方案?

4

2 回答 2

1

You can use the following (I don't use var here to make the types explicit):

IQueryable<IEnumerable<int>> query = context.Users
    .Where(u => u.UserId == x)
    .Select(u => u.Roles.Select(r => r.RoleId));

IEnumerable<int> result = query.Single();

This

  • throws an exception if the user with UserId == x doesn't exist. (I guess you want that because you are using Single in your example.)
  • returns a collection of RoleIds of user x. The collection can be empty (result.Count() == 0) if the user isn't in any role.
  • creates the following SQL for query (with x == 1) which only joins User and UserRoles table:

    SELECT 
    [Project1].[UserId] AS [UserId], 
    [Project1].[C1] AS [C1], 
    [Project1].[RoleId] AS [RoleId]
    FROM ( SELECT 
            [Extent1].[UserId] AS [UserId], 
            [Extent2].[RoleId] AS [RoleId], 
           CASE WHEN ([Extent2].[UserId] IS NULL) 
               THEN CAST(NULL AS int) 
               ELSE 1
           END AS [C1]
           FROM  [dbo].[Users] AS [Extent1]
           LEFT OUTER JOIN [dbo].[UserRoles] AS [Extent2]
               ON [Extent1].[UserId] = [Extent2].[UserId]
           WHERE 1 = [Extent1].[UserId]
         )  AS [Project1]
    ORDER BY [Project1].[UserId] ASC, [Project1].[C1] ASC
    

If you don't want to distinguish if the user doesn't exist or exists but has no roles you can get a much simpler SQL with this query:

IQueryable<int> query = context.Users
    .Where(u => u.UserId == x)
    .SelectMany(u => u.Roles.Select(r => r.RoleId));

IEnumerable<int> result = query.ToList();

This returns an empty collection if the user has no roles or if the user doesn't exist. But the SQL is very simple:

SELECT 
[Extent1].[RoleId] AS [RoleId]
FROM [dbo].[UserRoles] AS [Extent1]
WHERE 1 = [Extent1].[UserId]

So, here is no join between tables involved at all and the query only uses the UserRoles link table.

于 2011-12-30T17:27:57.703 回答
0

我更有效地使用角色。

用于将角色添加到用户表的签出标志。

标志的作用:

使用以下命令创建一种枚举:

Administrator = 1
Moderator = 2
SuperUser = 4
User = 8
Visitor = 16

您在 Usertable 中将属性 Role 添加为 Integer。

"User" = Administrator + Moderator --> Role =  3 ( 1 + 2 )
"User" = Moderator + SuperUser     --> Role =  6 ( 2 + 4 )
"User" = SuperUser + User          --> Role = 12 ( 4 + 8 )

不再有连接表并解决您的问题。

(尽管将逻辑添加到您的应用程序中需要额外的编码)

于 2011-12-30T15:58:24.537 回答