实体框架正在删除所有行,然后添加新行

问题描述 投票:0回答:1

我的代码中有三个主要类:

public class AppRole : ITenantEntity<Guid>
{
    [Required]
    public Guid Id { get; set; }

    [Required]
    public string Name { get; set; }

    [Required]
    public Guid TenantId { get; set; }

    public virtual ICollection<AppUserRole> AppUserRoles { get; } = new HashSet<AppUserRole>();
}

public class AppUser
{
    [Required]
    public string UserId { get; set; }

    [Required]
    public Guid TenantId { get; set; }

    public virtual User User { get; set; }
}

public class AppUserRole
{
    [Required]
    public string UserId { get; set; }

    [Required]
    public Guid RoleId { get; set; }

    public virtual User User { get; set; }

    public virtual AppRole Role { get; set; }
}

以及这些的映射:

builder
    .Entity<AppRole>()
    .HasMany(s => s.AppUserRoles)
    .WithOne(s => s.Role)
    .HasForeignKey(s => s.RoleId)
    .OnDelete(DeleteBehavior.Cascade);

builder
    .Entity<AppUser>().HasKey(x => new { x.UserId, x.TenantId });

builder
    .Entity<AppUserRole>().HasKey(x => new { x.UserId, x.RoleId });

我的应用程序中的用例如下所示:

  • 应用程序中有很多角色 -> AppRole
  • 应用程序中有很多用户 -> AppUser
  • 用户可以附加到n个应用程序

所以在数据库中我得到了这样的数据:

AppRole - ID:1,名称:“测试”,... AppRole - ID:2,名称:“test2”,...

AppUser - 用户 ID:“myuser”,...

AppUserRole - 用户 ID:“myuser”,角色 ID:1 AppUserRole - 用户 ID:“myuser”,角色 ID:2

当我尝试将新的 AppRole 添加到表中时,实体框架的第一步是删除 AppRoles 表中的每一行,执行查询:

DELETE FROM [AppRoles]
WHERE [Id] = @p0 AND [TenantId] = @p1;
SELECT @@ROWCOUNT;

下一步是执行插入:

INSERT INTO [AppRoles] ([Id], [TenantId], [Name])
VALUES (@p22, @p23, @p24),
(@p25, @p26, @p27),
...
(@p55, @p56, @p57);

此机制会影响 AppUserRole 表中的所有行都被删除。 这才是真正的问题。 AppUserRole 表中消失的行

为了更好地理解问题,让我们看另一个例子:

想象一下班上有20名学生。每个人都有不同科目的成绩。一年中,一名学生加入。这是否会导致清除剩余学生的所有成绩数据? 我不这么认为。 那就是问题所在。当新项目添加到 AppRole 时,不应删除 AppUserRoles 中的数据。

有人看到这里有什么错误吗? 为什么 ef 先删除再添加?

我必须在这里问这个问题,因为我自己尝试解决这个问题并不能解决问题。

我忘了 - 这是执行更新的代码:

public virtual async Task<T> Update(T item)
{
    var originalItem = await this.DbSet.SingleAsync(...).ConfigureAwait(false);
    this.Mapper.Map(item, originalItem);
    await this.Context.SaveChangesAsync().ConfigureAwait(false);
    return this.Mapper.Map<T>(originalItem);
}
entity-framework entity-framework-core mapping cascading-deletes
1个回答
0
投票

使用集合时,您需要注意您的代码或映射器不会最终替换集合实例。此行也可能会在读取数据时导致问题:

public virtual ICollection<AppUserRole> AppUserRoles { get; } = new HashSet<AppUserRole>();

将此更新为:

public virtual ICollection<AppUserRole> AppUserRoles { get; protected set;} = new HashSet<AppUserRole>();

然后,当更新您想要对子集合进行更改的父集合时,请使用

Include()
:

立即加载它
var originalItem = await this.DbSet
    .Include(x => x.AppUserRoles)
    .SingleAsync(...)
    .ConfigureAwait(false);

this.Mapper.Map(item, originalItem);

如果您希望添加 AppUserRole,您可能希望让映射器忽略任何集合或引用并手动处理它们。您不希望映射器尝试用新集合覆盖集合(

protected
setter 应该有助于避免这种情况),而且对于单一引用,您通常不希望更改现有实体值,而是加载新实体(例如) 来引用并替换父级中的引用。

© www.soinside.com 2019 - 2024. All rights reserved.