如何在Entity Framework 6中强制执行内连接?

huangapple go评论69阅读模式
英文:

How to force inner join in Entity Framework 6?

问题

Entity Framework 6 automatically translates the Include with a where condition to a LEFT JOIN because it aims to include related entities even if there are no matches in the INNER JOIN. This behavior is by design to ensure that you retrieve all user groups and their related users, even if there are no matching users.

If you want to change this behavior and use an INNER JOIN instead of a LEFT JOIN, you can try the following:

  1. Use a projection to select only the entities you need, excluding those with null related entities:
var userGroups = await context.UserGroups
    .Select(userGroup => new
    {
        UserGroup = userGroup,
        Users = userGroup.UserToUserGroups
            .Where(ugtu => ugtu.IsActive && UserIds.Contains(ugtu.UserId))
            .Select(ugtu => ugtu.User)
            .ToList()
    })
    .Where(item => item.Users.Any()) // Filter out user groups with no matching users
    .ToListAsync();

This approach uses a projection to select user groups along with their matching users. It filters out user groups with no matching users using the .Where(item => item.Users.Any()) clause.

This way, you can achieve an INNER JOIN-like behavior, but keep in mind that this may result in different SQL queries depending on your specific database provider and query optimizer.

Please test this approach thoroughly to ensure it meets your requirements and performance expectations with your specific database system.

英文:

I would like to ask why Entity Framework 6 automatically translate Include (with where condition) to LEFT JOIN instead of INNER JOIN, and is there any option to change it?

public async Task<IEnumerable<UserGroup>> GetUserGroupWithUser()
{
    var UserIds = new List<int>{ 1};

    var userGroups = await context.UserGroups.Include(userGroup => 
     userGroup.UserToUserGroups.Where(ugtu => ugtu.IsActive && 
     UserIds.Contains(ugtu.UserId)))
     .ThenInclude(ugtu => ugtu.User)
     .Where(userGroup => userGroup.UserToUserGroups.Any())
     //Removed some projection
     .ToListAsync();

     return userGroups;
}

Query:

SELECT [u].[Id], [u].[IsActive], [u].[Name], [t].[Id], [t].[IsActive], [t].[UserGroupId], [t].[UserId], [t].[Id0], [t].[IsActive0], [t].[Name]
FROM [UserGroups] AS [u]
LEFT JOIN (
    SELECT [u1].[Id], [u1].[IsActive], [u1].[UserGroupId], [u1].[UserId], [u2].[Id] AS [Id0], [u2].[IsActive] AS [IsActive0], [u2].[Name]
    FROM [UserToUserGroups] AS [u1]
    INNER JOIN [Users] AS [u2] ON [u1].[UserId] = [u2].[Id]
    WHERE ([u1].[IsActive] = CAST(1 AS bit)) AND ([u1].[UserId] = 1)
) AS [t] ON [u].[Id] = [t].[UserGroupId]
WHERE EXISTS (
    SELECT 1
    FROM [UserToUserGroups] AS [u0]
    WHERE [u].[Id] = [u0].[UserGroupId])
ORDER BY [u].[Id], [t].[Id]

Entities:

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public bool IsActive { get; set; }
    public virtual ICollection<UserToUserGroup> UserToUserGroups { get; set;}
}

 public class UserToUserGroup
 {
    public int Id { get; set; }
    public int UserId { get; set; }
    public int UserGroupId { get; set; }
    public bool IsActive { get; set; }
    public virtual UserGroup UserGroup { get; set; }
    public virtual User User { get; set; }
 }

public class UserGroup
{
    public int Id { get; set; }
    public string Name { get; set; }
    public bool IsActive { get; set; }
    public virtual ICollection<UserToUserGroup> UserToUserGroups { get; set; }
}

Configuration:

public class UserConfiguration : IEntityTypeConfiguration<User>
{
    public void Configure(EntityTypeBuilder<User> builder)
    {
        builder.ToTable("Users");
        builder.HasKey(user => user.Id);
        builder.Property(user => user.Id)
            .IsRequired()
            .ValueGeneratedOnAdd();
    }
}

public class UserGroupConfiguration : IEntityTypeConfiguration<UserGroup>
{
    public void Configure(EntityTypeBuilder<UserGroup> builder)
    {
        builder.ToTable("UserGroups");
        builder.HasKey(userGroup => userGroup.Id);
        builder.Property(userGroup => userGroup.Id)
            .IsRequired()
            .ValueGeneratedOnAdd();
    }
}

public class UserToUserGroupConfiguration : IEntityTypeConfiguration<UserToUserGroup>
{
    public void Configure(EntityTypeBuilder<UserToUserGroup> builder)
    {
        builder.ToTable("UserToUserGroups");
        builder.HasKey(ugtu => ugtu.Id);
        builder.Property(ugtu => ugtu.Id)
            .IsRequired()
            .ValueGeneratedOnAdd();

        builder.HasIndex(ugtu => new
        {
            ugtu.UserId,
            ugtu.UserGroupId
        }).HasFilter("IsActive = 'true'").IsUnique();

        builder
            .HasOne(utug => utug.User)
            .WithMany(user => user.UserToUserGroups)
            .HasForeignKey(ugtu => ugtu.UserId)
            .OnDelete(DeleteBehavior.Cascade);
        builder.HasOne(utug => utug.UserGroup)
            .WithMany(user => user.UserToUserGroups)
            .HasForeignKey(ugtu => ugtu.UserGroupId)
            .OnDelete(DeleteBehavior.Cascade);
        
    }
}

Problem:
如何在Entity Framework 6中强制执行内连接?

Checked

  1. I tried with IsRequired() inside the configuration.

Thanks in advance for all responses.

答案1

得分: 2

抱歉,Entity Framework基于数据库设计来进行连接操作,所以在这种情况下它强制使用了LEFT JOIN。建议手动进行连接操作,如下所示:

public async Task<IEnumerable<UserGroup>> GetUserGroupWithUser()
{
    var UserIds = new List<int>{ 1};

    var userGroups = await
        (from ug in context.UserGroups
         join ugtu in UserToUserGroups on ug.UserId equals ugtu.UserId
         where ugtu.IsActive
         where UserIds.Contains(ug.UserId)
         select whatever
        )
        .ToListAsync();

     return userGroups;
}
英文:

Unfortunately, Entity Framework bases its joins on the design of the database, so in this case it's forcing a LEFT JOIN.

Instead do the joins manually

public async Task&lt;IEnumerable&lt;UserGroup&gt;&gt; GetUserGroupWithUser()
{
    var UserIds = new List&lt;int&gt;{ 1};

    var userGroups = await
        (from ug in context.UserGroups
         join ugtu in UserToUserGroups on ug.UserId equals ugtu.UserId
         where ugtu.IsActive
         where UserIds.Contains(ug.UserId)
         select whatever
        )
        .ToListAsync();

     return userGroups;
}

答案2

得分: 1

你还可以使用 join 命令来执行此操作linq-joining-operator-join

我放置了生成的查询:

var UserIds = new List<int> { 1 };

var query = context.UserToUserGroup
.Join(
    context.UserGroup,
    UserToUserGroup => UserToUserGroup.UserGroupId,
    UserGroup => UserGroup.Id,
    (UserToUserGroup, UserGroup) => new { UserToUserGroup, UserGroup }
)
.Join(
    context.Users,
    UserToUserGroup =>
    UserToUserGroup.UserToUserGroup.UserId,
    Users => Users.Id,
    (UserToUserGroup, Users) =>
    new { UserToUserGroup, Users }
).Where(p=>p.UserToUserGroup.UserToUserGroup.IsActive && UserIds.Contains(p.UserToUserGroup.UserToUserGroup.UserId)) .ToListAsync();

查询:

SELECT [u].[Id], [u].[IsActive], [u].[UserGroupId], [u].[UserId], [u0].[Id], [u0].[IsActive], [u0].[Name], [u1].[Id], [u1].[IsActive], [u1].[Name]
FROM [UserToUserGroups] AS [u]
INNER JOIN [UserGroups] AS [u0] ON [u].[UserGroupId] = [u0].[Id]
INNER JOIN [Users] AS [u1] ON [u].[UserId] = [u1].[Id]
WHERE [u].[IsActive] = CAST(1 AS bit) AND [u].[UserId] = 1
英文:

You can also do it with join command linq-joining-operator-join .

I put the generated query

  var UserIds = new List&lt;int&gt; { 1 };

        var query = context.UserToUserGroup
        .Join(
            context.UserGroup,
            UserToUserGroup =&gt; UserToUserGroup.UserGroupId,
            UserGroup =&gt; UserGroup.Id,
            (UserToUserGroup, UserGroup) =&gt; new { UserToUserGroup, UserGroup }
        )
        .Join(
            context.Users,
            UserToUserGroup =&gt;
            UserToUserGroup.UserToUserGroup.UserId,
            Users =&gt; Users.Id,
            (UserToUserGroup, Users) =&gt;
            new { UserToUserGroup, Users }
        ).Where(p=&gt;p.UserToUserGroup.UserToUserGroup.IsActive &amp;&amp; UserIds.Contains(p.UserToUserGroup.UserToUserGroup.UserId)) .ToListAsync();

Query:

SELECT [u].[Id], [u].[IsActive], [u].[UserGroupId], [u].[UserId], [u0].[Id], [u0].[IsActive], [u0].[Name], [u1].[Id], [u1].[IsActive], [u1].[Name]
FROM [UserToUserGroups] AS [u]
INNER JOIN [UserGroups] AS [u0] ON [u].[UserGroupId] = [u0].[Id]
INNER JOIN [Users] AS [u1] ON [u].[UserId] = [u1].[Id]
WHERE [u].[IsActive] = CAST(1 AS bit) AND [u].[UserId] = 1

huangapple
  • 本文由 发表于 2023年5月21日 20:03:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/76299818.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定