EF6 多对多不能删除

Posted

技术标签:

【中文标题】EF6 多对多不能删除【英文标题】:EF6 Many-to-many can not delete 【发布时间】:2022-01-08 22:44:33 【问题描述】:

我使用以下代码使用 EF6 核心创建了表。

问题是, UserA 、 UserB 创建并分配了不同的声明。完全没有问题。分配现有声明时(例如,为 UserB 分配了 UserA 的声明 --> [tying to do] 删除 UserB 的声明并将 UserB 的主题更改为 UserA 的主题)然后我将通过 subjectId 删除 UserB 的声明,这将创建主要UserUserClaims 表中的关键重复问题 bcos SubjectId 和 ClaimId 已经存在

public class User

    [Key]
    public Guid UserId  get; set; 
    
    [MaxLength(200)]
    public string Username  get; set; 

    [Required]
    public Guid Subject  get; set; 

    public ICollection<UserClaim> Claims  get; set;  = new List<UserClaim>();

    public List<UserClaimRelation> UserClaimRelations  get; set; 



public class UserClaim 


    [Key]
    public Guid UserClaimId  get; set;  

    [MaxLength(250)]
    [Required]
    public string Type get; set;  

    [Required]
    public Guid Subject  get; set; 

    [MaxLength(250)]
    [Required]
    public string Value  get; set; 

    public ICollection<User> Users  get; set; 

    public List<UserClaimRelation> UserClaimRelations  get; set; 


public class UserClaimRelation

        public Guid UserClaimId get; set; 
        public UserClaim Claim  get; set; 

        public Guid UserSubject get; set; 
        public User User  get; set; 



public class MyDbContext: DbContext

        public DbSet<User> Users  get; set; 

        public DbSet<UserClaim> UserClaims  get; set; 

    protected override void OnModelCreating(ModelBuilder modelBuilder)
        
            base.OnModelCreating(modelBuilder);
        
            modelBuilder.Entity<User>()
            .HasMany(p => p.Claims)
            .WithMany(p => p.Users)
            .UsingEntity<UserClaimRelation>(
                j => j
                    .HasOne(pt => pt.Claim)
                    .WithMany(t => t.UserClaimRelations)
                    .HasPrincipalKey(f => f.UserClaimId),
                j => j.HasOne(p => p.User)
                    .WithMany(t => t.UserClaimRelations)
                    .HasPrincipalKey( t => t.Subject),
                j => j.HasKey( t => new t.UserClaimId, t.UserSubject)
            );

我正在尝试按 SubjectId 删除声明

public async Task<bool> DeleteUserClaimsBySubjectAsync(Guid subject)

            if (subject == Guid.Empty)
            
                throw new ArgumentNullException(nameof(subject));
            
            
        var claims = await _context.UserClaims
                .Where(x => x.Subject == subject).ToListAsync();
            

            foreach (var claim in claims)
            
               _context.UserClaims.Remove(claim);    
            

            //_context.UserClaims.RemoveRange(claims);

            return (await _context.SaveChangesAsync() > 0);

DeleteUserClaimsBySubjectAsync 中删除声明时,我收到以下错误

SqlException:违反主键约束“PK_UserClaimRelation”。无法在对象“dbo.UserClaimRelation”中插入重复键。重复键值为 (13229d33-99e0-41b3-b18d-4f72127e3971, 8acbbd40-1608-41f2-de59-08d9b58b83d3)。

SQL Profiler 查询

exec sp_executesql N'SET NOCOUNT ON;
INSERT INTO [UserClaimRelation] ([UserClaimId], [UserSubject])
VALUES (@p0, @p1),
(@p2, @p3),
(@p4, @p5),
(@p6, @p7),
(@p8, @p9);
DELETE FROM [UserClaims]
WHERE [UserClaimId] = @p10 AND [ConcurrencyStamp] = @p11;
SELECT @@ROWCOUNT;

DELETE FROM [UserClaims]
WHERE [UserClaimId] = @p12 AND [ConcurrencyStamp] = @p13;
SELECT @@ROWCOUNT;

DELETE FROM [UserClaims]
WHERE [UserClaimId] = @p14 AND [ConcurrencyStamp] = @p15;
SELECT @@ROWCOUNT;

DELETE FROM [UserClaims]
WHERE [UserClaimId] = @p16 AND [ConcurrencyStamp] = @p17;
SELECT @@ROWCOUNT;

DELETE FROM [UserClaims]
WHERE [UserClaimId] = @p18 AND [ConcurrencyStamp] = @p19;
SELECT @@ROWCOUNT;

',N'@p0 uniqueidentifier,@p1 uniqueidentifier,@p2 uniqueidentifier,@p3 uniqueidentifier,@p4 uniqueidentifier,@p5 uniqueidentifier,@p6 uniqueidentifier,@p7 uniqueidentifier,@p8 uniqueidentifier,@p9 uniqueidentifier,@p10 uniqueidentifier,@p11 nvarchar(4000),@p12 uniqueidentifier,@p13 nvarchar(4000),@p14 uniqueidentifier,@p15 nvarchar(4000),@p16 uniqueidentifier,@p17 nvarchar(4000),@p18 uniqueidentifier,@p19 nvarchar(4000)',

@p0='6203BBC3-BD52-4E32-3DDD-08D9B86AB745',@p1='D860EFCA-22D9-47FD-8249-791BA61B07C7',
@p2='80B958C5-FF44-400F-3DDE-08D9B86AB745',@p3='D860EFCA-22D9-47FD-8249-791BA61B07C7',
@p4='A844CB59-5A00-488A-3DDF-08D9B86AB745',@p5='D860EFCA-22D9-47FD-8249-791BA61B07C7',@p6='794AB806-3215-45AF-3DE0-08D9B86AB745',@p7='D860EFCA-22D9-47FD-8249-791BA61B07C7',@p8='80EFF0DF-AE56-419A-3DE1-08D9B86AB745',@p9='D860EFCA-22D9-47FD-8249-791BA61B07C7',@p10='1A48CF6E-E1CD-4042-3DE4-08D9B86AB745',@p11=N'742674d8-3c59-4be4-ac09-38c6804acb66',@p12='258ABD95-6B2A-45CC-3DE6-08D9B86AB745',@p13=N'd206316a-71af-46b7-92e4-bddca669ad87',@p14='4FB1D2E3-18BE-4AFC-3DE3-08D9B86AB745',@p15=N'3eb17fb7-998b-47e1-a97f-3640cbd82b7a',@p16='6C2B9A18-5C8F-4068-3DE5-08D9B86AB745',@p17=N'415b2cc1-b50a-4ec8-872d-13b6342dcd33',@p18='951CEEA6-D057-4588-3DE2-08D9B86AB745',@p19=N'3dfd76dd-d515-4daa-893c-f1ae97aa063a'

【问题讨论】:

您是如何填充这些表格的?看起来 EF 想要在您删除的过程中填充连接表 - 您是否以某种方式手动填充它?如果是这样,请尝试使用 EF 分配声明,加载用户并添加声明。然后保存后,尝试删除。 在源代码中,您可以看到用于填充关系的 UserClaimRelationMyDbContext 代码。从 UserClaims 中删除时,我不明白为什么要插入 UserClaimRelation,对此有什么想法吗? 不要加入 .HasPrincipalKey( t =&gt; t.Subject) 而是加入 .HasPrincipalKey( t => t.UserClaimId) - 这应该会有所帮助。您仍然可以通过搜索适当的主题来删除。 对不起,我没明白你的意思 【参考方案1】:

好的,因此您需要在这里建立多对多关系 - 多个用户可以有多个不同的声明,并且可以将多个声明分配给多个用户。

所以首先 - Users:

public class User

    [Key]
    public Guid UserId  get; set; 
    
    [MaxLength(200)]
    public string Username  get; set; 

    [Required]
    public Guid Subject  get; set; 

    public ICollection<UserClaim> Claims  get; set;  = new List<UserClaim>();


那么,UserClaims

public class UserClaim 


    [Key]
    public Guid UserClaimId  get; set;  

    [MaxLength(250)]
    [Required]
    public string Type get; set;  

    [Required]
    public Guid Subject  get; set; 

    [MaxLength(250)]
    [Required]
    public string Value  get; set; 

    public ICollection<User> Users  get; set; 

请注意,以上任何内容都没有指向UserClaimRelation 的导航属性。事实上,我们将这个UserClaimRelation 完全删除为一个单独的实体模型——这个例子不需要它。如果你真的想要,你可以映射它,但让我们专注于完成这项工作。

现在,关系设置:

protected override void OnModelCreating(ModelBuilder modelBuilder)

    modelBuilder.Entity<User>()
        .HasMany(x => x.Claims)
        .WithMany(x => x.Users)
        .UsingEntity<Dictionary<string, object>>(
            "UserClaimRelation",
            x => x.HasOne<User>().WithMany(),
            x => x.HasOne<UserClaim>().WithMany(),
            x => x.ToTable("UserClaimRelation"));

现在,在更新/重新创建/重新应用迁移到数据库之后(取决于您的处理方式),您应该能够按主题删除声明,而无需修改您的 DeleteUserClaimsBySubjectAsync 方法。

这应该设置所有内容并创建中间表。验证这段代码,我是从内存中输入的

【讨论】:

请检查我下面的会话作为答案。您的解决方案不起作用。请注意,我正在用 UserB 更新 userA Subject,所以 userA 和 UserB 都有 same subject 好的,但这应该不是问题,因为主题不是任何地方的键或外键,对吧?因此它可以被复制。如果您将主题用作外键,则会导致问题,这在我的示例中不应该发生。学科,至少在理论上只是正常的必填字段。此外,它与加入表 (UserClaimRelation) 没有任何关系 - 按照惯例,它不在那里使用,只有 ID 存储在那里。可能是您没有应用迁移或类似的东西吗?数据库表已经存在,代码的变化没有反映在数据库中?【参考方案2】:

我应用了代码。创建的表格如下图所示。但是当我尝试用 userB 的主题更新用户 A 的主题(用户 B 的相同声明)时,我收到以下错误

更新

SqlException:违反主键约束“PK_UserClaimRelation”。无法在对象“dbo.UserClaimRelation”中插入重复键。重复键值为 (bd7eb87f-2a5f-4921-758e-08d9b97d3121, 13229d33-99e0-41b3-b18d-4f72127e3971)。因为它试图插入 UserClaimRelation,与我最初给出的探查器查询相同。

【讨论】:

以上是关于EF6 多对多不能删除的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 EF6 更新多对多表

csharp EF6 - 多对多第二种方式

csharp EF6 - 多对多第一种方式

创建实体模型多对多 CodeFirst Ef6

实体框架6:多对多关系问题[关闭]

同一张表之间多对多的级联删除