【问题标题】:Entity Framework - many to many with one primary实体框架 - 多对多,一个主要的
【发布时间】:2021-04-21 17:23:33
【问题描述】:

我有一个Channel 域模型:

public class Channel
{
   public string Id {get; set;}
   public List<ChannelUser> ChannelUsers {get; set;}
}

还有一个ChannelUser 类如下(我在表上有额外的元数据,为简洁起见,在此处删除,这就是为什么我使用特定类来表示多对多关系)。

public class ChannelUser
{
   public string ChannelId {get; set;}
   public string UserId {get; set;}
}

ChannelUser 的主键是 (ChannelId, UserId)。

这些 ChannelUser 中的一个(并且只有一个)可以是 Channel 的所有者。 理论上,一个用户可以是许多频道的所有者(但显然,一个 ChannelUser 只能是它所关联的频道的所有者)。

我想在数据库级别强制执行这一点,而不是在 ChannelUser 上使用“IsOwner”属性并使用业务逻辑来确保每个通道只设置一次标志。我还想强制 ChannelOwner 是 ChannelUser 之一,因此不希望直接来自 Channel &gt; User 的关系,而是将其保留为 Channel &gt; ChannelUser

因此我将Channel类更新如下:

public class Channel
{
   public string Id {get; set;}
   public List<ChannelUser> {get; set;}
   public ChannelUser Owner {get; set;}
}

并添加了以下流畅的API表达式:

            modelBuilder.Entity<ChannelUser>().HasKey(cu => new { cu.ChannelId, cu.UserId });

            modelBuilder.Entity<ChannelUser>().HasOne<Channel>(x => x.Channel).WithMany(x => x.ChannelUsers)
                .OnDelete(DeleteBehavior.NoAction);

            modelBuilder.Entity<ChannelUser>().HasOne<User>(x => x.User).WithMany(x => x.UserChannels)
                .OnDelete(DeleteBehavior.NoAction);

添加迁移时会导致:

Both relationships between 'ChannelUser.Channel' and 'Channel.ChannelUsers' and between 'ChannelUser' and 'Channel.Owner' could use {'ChannelId'} as the foreign key. To resolve this configure the foreign key properties explicitly on at least one of the relationships.

我理解,但我认为这是我们想要的。因为 ChannelUser.ChannelId 应该始终是两个关系的相同 Channel.Id。

请任何人都可以建议我如何构建这种关系或仍然强制执行表之间的many to manyone to one 关系的替代方法?

【问题讨论】:

  • 用简单的英语陈述思考。一个频道有多个用户。一个频道只有一个所有者。用户可以拥有多个频道,但可以是任何单个频道的所有者。因此,三个表 Channel、User 和 ChannelUser 之间的关系就像通道和用户之间的多对多以及所有者关系之间的一对一一样。在您的 Channel 类中,您将 Owner 引用为 ChannelUser 而不是 User 类,这有点循环引用。此外,您应该拥有 User 和 Channel 而不是 ChannelUser 的列表,因为它只是中间类,除非您有额外的元数据。
  • 感谢您的评论。我更新了我的问题以澄清:用户可以是许多频道的所有者(但 ChannelUser 只能是一个频道的所有者,由 ChannelId 确定)。我确实有关于这种关系的额外元数据,为了简洁起见,我只是省略了。谢谢,
  • 我将创建关系 Channel > User 以按照您的建议表达主要所有者。如果你想写作为答案,我会接受。
  • 深入研究,这是可能的,但您的数据结构可能需要稍作更改以适应约束。

标签: entity-framework entity-framework-core


【解决方案1】:

这不是理想的方法,但可以做到,看看这个 ER 图:

理想的方法是拥有适当的实体映射,例如:

用户类:

public class User
{
   public string Id {get; set;}
   public virtual List<Channel> Channels {get; set;}
   // Other properties....
}

频道类:

public class Channel
{
   public string Id {get; set;}
   public List<User> Users {get; set;}
   public User Owner {get; set;}
   public string? OwnerId { get; set; }
   public User Owner { get; set; }
}

ChannelUser 类加入两者:

public class ChannelUser
{
   public string Id {get; set;}
   public string ChannelId {get; set;}
   public string UserId {get; set;}
}

映射:

modelBuilder.Entity<User>()
            .HasMany<Channel>(user => user.channels)
            .WithMany(channel => channel.users)
            .Map(cu =>
                    {
                        cu.MapLeftKey("UserId");
                        cu.MapRightKey("ChannelId");
                        cu.ToTable("ChannelUser");
             });

    // configure one-to-many relationship for ownership
    modelBuilder.Entity<Channel>()
        .HasRequired<User>(c => c.User)
        .WithMany(u => u.ChannelId )
        .HasForeignKey<int>(c => c.UserId);

在规范化之后,在控制器代码中检查用户是否存在的责任应该相当容易,或者我们可以使用 DB 过程、触发器来创建约束来检查给定的所有者 ID 是否确实在给定频道的 ChannelUser 表中。

【讨论】:

    猜你喜欢
    • 2021-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-11
    • 2012-02-13
    • 1970-01-01
    相关资源
    最近更新 更多