如何在 ASP.net EFCodeFirst 中设置具有判别式的外键约束?

发布于 2024-10-29 00:21:02 字数 5601 浏览 1 评论 0原文

我有以下用于 EFCodeFirst 数据库创建的 C# 代码:

enum MemberOfGroupDiscriminator { MemberOfGroup, GroupMemberOfGroup, UserMemberOfGroup }
public class MemberOfGroup
{
    public int Member_ID { get; set; }
    public int Group_ID { get; set; }
}

public class GroupMemberOfGroup : MemberOfGroup { }
public class UserMemberOfGroup : MemberOfGroup { }

public class User : WSSCSDevModel, IUser
{
    public int LoginCount { get; set; }
    public DateTime LastLogin { get; set; }
    public virtual ICollection<MemberOfGroup> Memberships { get; set; }
}

public class Group : WSSCSDevModel,  IGroup
{
    public string Name { get; set; }
}

这在流畅的 API 中创建数据库:

        ModelBuilder.Entity<MemberOfGroup>()
            .HasKey(k => new { k.Member_ID, k.Group_ID })
            .Map<MemberOfGroup>(m => m.Requires("OfGroupType")
                .HasValue((byte)MemberOfGroupDiscriminator.MemberOfGroup))
            .Map<UserMemberOfGroup>(m => m.Requires("OfGroupType")
                .HasValue((byte)MemberOfGroupDiscriminator.UserMemberOfGroup))
            .Map<UserMemberOfGroup>(m => m.Properties(p => p.Member_ID = UserAddition.))
            .Map<GroupMemberOfGroup>(m => m.Requires("OfGroupType")
                .HasValue((byte)MemberOfGroupDiscriminator.GroupMemberOfGroup))
            .ToTable("MemberOfGroup");

我的想法是允许用户和组能够形成组。因此 B 组可能是 A 组加上另外 1 个人。我的问题是,当判别式要求这样做时,如何强制框架检查 Member_ID 是否是 User 的键,以及 Group 的键。

顺便说一句,用户和组实体有 int ID {get;放;在其父类中声明。

谢谢!

编辑:

重新审视这一点,我现在在向群组添加成员时遇到问题。这是我的代码:

public class WSSCSDevDbContext : DbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<Group> Groups { get; set; }

    protected override void OnModelCreating(DbModelBuilder ModelBuilder)
    {
        ModelBuilder.Entity<User>().ToTable("Users");
        ModelBuilder.Entity<Group>().ToTable("Groups");

        ModelBuilder.Entity<MemberOfGroup>()
            .HasKey(m => m.ID)
            .Map<GroupMemberOfGroup>(g => g.Requires("OfType")
                .HasValue((byte)MemberOfGroupDiscriminator.GroupMemberOfGroup))
            .Map<UserMemberOfGroup>(u => u.Requires("OfType")
                .HasValue((byte)MemberOfGroupDiscriminator.UserMemberOfGroup));

        ModelBuilder.Entity<User>()
            .HasMany(u => u.MemberOf)
            .WithRequired(m => m.MemberUser)
            .HasForeignKey(m => m.MemberUser_ID)
            .WillCascadeOnDelete(false);

        ModelBuilder.Entity<Group>()
            .HasMany(g => g.MemberOf)
            .WithRequired(m => m.MemberGroup)
            .HasForeignKey(m => m.MemberGroup_ID)
            .WillCascadeOnDelete(false);

        ModelBuilder.Entity<Group>()
            .HasMany(u => u.Members)
            .WithRequired(m => m.Group)
            .HasForeignKey(m => m.Group_ID);
    }
}

public class User : WSSCSDevModel, IUser
{
    public int LoginCount { get; set; }
    public DateTime LastLogin { get; set; }
    public virtual ICollection<UserMemberOfGroup> MemberOf { get; set; }
    public virtual ICollection<MemberOfGroup> Memberships { get; set; }
}

public class Group : WSSCSDevModel,  IGroup
{
    public string Name { get; set; }
    public virtual ICollection<GroupMemberOfGroup> MemberOf { get; set; }
    public virtual ICollection<MemberOfGroup> Members { get; set; }
}



public class MemberOfGroup
{
    public int? Group_ID { get; set; }
    virtual public Group Group { get; set; }
}

public class GroupMemberOfGroup : MemberOfGroup 
{
    public int? MemberGroup_ID { get; set; }
    virtual public Group MemberGroup { get; set; }
}

public class UserMemberOfGroup : MemberOfGroup 
{
    public int? MemberUser_ID { get; set; }
    virtual public User MemberUser { get; set; }
}

同样,Group 和 User 父类定义 int ID {get;set;}

这是我的控制器中用于将用户添加到组的代码:

        if(model.MemberOf_IDs != null)
        {
            Repository<User> UserRepo = new Repository<User>();
            User ToEdit = UserRepo.GetById(model.User_ID);
            foreach (UserMemberOfGroup Membership in ToEdit.MemberOf)
            {
                Membership.Group.Members.Remove(Membership);
            }
            ToEdit.MemberOf = new List<UserMemberOfGroup>();
            Repository<Group> GroupRepo = new Repository<Group>();
            string[] Delims = new string[1];
            Delims[0] = ";";
            List<int> IDs = model.MemberOf_IDs.Split(Delims, 
                StringSplitOptions.RemoveEmptyEntries).Select(id => Convert.ToInt32(id)).ToList();
            foreach(int ID in IDs)
            {
                Group ToAddTo = GroupRepo.GetById(ID);
                ToEdit.MemberOf.Add(ToAddTo.AddMember(ToEdit));
            }

            GroupRepo.Save();
        }

我知道这甚至不是最好的方法,但它说明了我的想法问题。顺便说一句,存储库<>类共享 DBContext。因此,从本质上讲,对其中一个进行保存就是对所有这些进行保存。这工作正常。

此代码采用分号分隔的组 ID 字符串,并将用户添加到其中。但是,我得到了这个异常:

A first chance exception of type 'System.Data.SqlClient.SqlException' occurred in     System.Data.dll

Additional information: Cannot insert the value NULL into column 'Group_ID', table 'CSProSuiteIndia.dbo.MemberOfGroups'; column does not allow nulls. UPDATE fails.
The statement has been terminated.

我不明白为什么它想将 Group_ID 更改为 NULL。我将 Group_ID 声明为 int?,所以我认为它应该可以为空。这里发生了什么事?

I have the following C# code for EFCodeFirst database creation:

enum MemberOfGroupDiscriminator { MemberOfGroup, GroupMemberOfGroup, UserMemberOfGroup }
public class MemberOfGroup
{
    public int Member_ID { get; set; }
    public int Group_ID { get; set; }
}

public class GroupMemberOfGroup : MemberOfGroup { }
public class UserMemberOfGroup : MemberOfGroup { }

public class User : WSSCSDevModel, IUser
{
    public int LoginCount { get; set; }
    public DateTime LastLogin { get; set; }
    public virtual ICollection<MemberOfGroup> Memberships { get; set; }
}

public class Group : WSSCSDevModel,  IGroup
{
    public string Name { get; set; }
}

And this in the the fluent API to create the database:

        ModelBuilder.Entity<MemberOfGroup>()
            .HasKey(k => new { k.Member_ID, k.Group_ID })
            .Map<MemberOfGroup>(m => m.Requires("OfGroupType")
                .HasValue((byte)MemberOfGroupDiscriminator.MemberOfGroup))
            .Map<UserMemberOfGroup>(m => m.Requires("OfGroupType")
                .HasValue((byte)MemberOfGroupDiscriminator.UserMemberOfGroup))
            .Map<UserMemberOfGroup>(m => m.Properties(p => p.Member_ID = UserAddition.))
            .Map<GroupMemberOfGroup>(m => m.Requires("OfGroupType")
                .HasValue((byte)MemberOfGroupDiscriminator.GroupMemberOfGroup))
            .ToTable("MemberOfGroup");

My idea here is to allow User and Groups to be able to form Groups. So GroupB could be GroupA plus 1 other person. My question is this, how can I force the framework to check that Member_ID is a key of User when the discriminant says to do so, as well as with Group.

BTW, the User and Group entities have int ID {get; set; } declared in their parent class.

Thanks!

EDIT:

Revisiting this, and I'm having issues now adding members to the group. Here's my code:

public class WSSCSDevDbContext : DbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<Group> Groups { get; set; }

    protected override void OnModelCreating(DbModelBuilder ModelBuilder)
    {
        ModelBuilder.Entity<User>().ToTable("Users");
        ModelBuilder.Entity<Group>().ToTable("Groups");

        ModelBuilder.Entity<MemberOfGroup>()
            .HasKey(m => m.ID)
            .Map<GroupMemberOfGroup>(g => g.Requires("OfType")
                .HasValue((byte)MemberOfGroupDiscriminator.GroupMemberOfGroup))
            .Map<UserMemberOfGroup>(u => u.Requires("OfType")
                .HasValue((byte)MemberOfGroupDiscriminator.UserMemberOfGroup));

        ModelBuilder.Entity<User>()
            .HasMany(u => u.MemberOf)
            .WithRequired(m => m.MemberUser)
            .HasForeignKey(m => m.MemberUser_ID)
            .WillCascadeOnDelete(false);

        ModelBuilder.Entity<Group>()
            .HasMany(g => g.MemberOf)
            .WithRequired(m => m.MemberGroup)
            .HasForeignKey(m => m.MemberGroup_ID)
            .WillCascadeOnDelete(false);

        ModelBuilder.Entity<Group>()
            .HasMany(u => u.Members)
            .WithRequired(m => m.Group)
            .HasForeignKey(m => m.Group_ID);
    }
}

public class User : WSSCSDevModel, IUser
{
    public int LoginCount { get; set; }
    public DateTime LastLogin { get; set; }
    public virtual ICollection<UserMemberOfGroup> MemberOf { get; set; }
    public virtual ICollection<MemberOfGroup> Memberships { get; set; }
}

public class Group : WSSCSDevModel,  IGroup
{
    public string Name { get; set; }
    public virtual ICollection<GroupMemberOfGroup> MemberOf { get; set; }
    public virtual ICollection<MemberOfGroup> Members { get; set; }
}



public class MemberOfGroup
{
    public int? Group_ID { get; set; }
    virtual public Group Group { get; set; }
}

public class GroupMemberOfGroup : MemberOfGroup 
{
    public int? MemberGroup_ID { get; set; }
    virtual public Group MemberGroup { get; set; }
}

public class UserMemberOfGroup : MemberOfGroup 
{
    public int? MemberUser_ID { get; set; }
    virtual public User MemberUser { get; set; }
}

Again, the Group and User parent classes define int ID {get;set;}

Here's code in my controller to Add Users to Groups:

        if(model.MemberOf_IDs != null)
        {
            Repository<User> UserRepo = new Repository<User>();
            User ToEdit = UserRepo.GetById(model.User_ID);
            foreach (UserMemberOfGroup Membership in ToEdit.MemberOf)
            {
                Membership.Group.Members.Remove(Membership);
            }
            ToEdit.MemberOf = new List<UserMemberOfGroup>();
            Repository<Group> GroupRepo = new Repository<Group>();
            string[] Delims = new string[1];
            Delims[0] = ";";
            List<int> IDs = model.MemberOf_IDs.Split(Delims, 
                StringSplitOptions.RemoveEmptyEntries).Select(id => Convert.ToInt32(id)).ToList();
            foreach(int ID in IDs)
            {
                Group ToAddTo = GroupRepo.GetById(ID);
                ToEdit.MemberOf.Add(ToAddTo.AddMember(ToEdit));
            }

            GroupRepo.Save();
        }

I know that's not even close to the best way to do it, but it illustrates my problem. BTW, the Repository<> classes share the the DBContext. So essentially, .Save on one is .Save on them all. This works properly.

This code takes a semi-colon delimited string of Group IDs, and adds the User to them. However, I get this exception:

A first chance exception of type 'System.Data.SqlClient.SqlException' occurred in     System.Data.dll

Additional information: Cannot insert the value NULL into column 'Group_ID', table 'CSProSuiteIndia.dbo.MemberOfGroups'; column does not allow nulls. UPDATE fails.
The statement has been terminated.

I don't understand WHY it wants to change Group_ID to NULL. I declared Group_ID as int?, so it should be nullable, I would think. What's happening here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

妖妓 2024-11-05 00:21:02

您想要的是在用户和组之间动态切换外键引用。您可以在数据库中的单列上执行此操作吗?你不能,所以你也不能在 ORM 中做到这一点。
如果您想强制执行完整性,则必须将 Member_ID 从父级移动到子级,并为其映射单独的关系。它将在 db 中创建两列,每列都有自己的 FK:

public abstract class Principal
{
    public int Id { get; set; }
}

public class User : Principal
{
    public string Login { get; set; }
    public virtual ICollection<UserMemberOfGroup> MemberOf { get; set; }
}

public class Group : Principal
{
    public string Name { get; set; }
    public virtual ICollection<GroupMemberOfGroup> MemberOf { get; set; }
    public virtual ICollection<MemberOfGroup> Members { get; set; }
}

public enum MemberOfGroupDiscriminator
{
    MemberOfGroup, 
    GroupMemberOfGroup, 
    UserMemberOfGroup
}

public abstract class MemberOfGroup
{
    public int Id { get; set; }
    public int GroupId { get; set; }
    public Group Group { get; set; }
}

public class GroupMemberOfGroup : MemberOfGroup
{
    public int MemberGroupId { get; set; }
    public Group MemberGroup { get; set; }
}

public class UserMemberOfGroup : MemberOfGroup
{
    public int UserId { get; set; }
    public User User { get; set; }
}

public class Context : DbContext
{
    public DbSet<Principal> Principals { get; set; }
    public DbSet<MemberOfGroup> MembersOfGroups { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Principal>()
            .HasKey(p => p.Id)
            .Map<User>(u => u.Requires("Type").HasValue("U"))
            .Map<Group>(p => p.Requires("Type").HasValue("G"));

        modelBuilder.Entity<MemberOfGroup>()
            .HasKey(m => m.Id)
            .Map<GroupMemberOfGroup>(g => g.Requires("Type").HasValue("G"))
            .Map<UserMemberOfGroup>(u => u.Requires("Type").HasValue("U"));

        modelBuilder.Entity<User>()
            .HasMany(u => u.MemberOf)
            .WithRequired(m => m.User)
            .HasForeignKey(m => m.UserId)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Group>()
            .HasMany(u => u.MemberOf)
            .WithRequired(m => m.MemberGroup)
            .HasForeignKey(m => m.MemberGroupId)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Group>()
            .HasMany(u => u.Members)
            .WithRequired(m => m.Group)
            .HasForeignKey(m => m.GroupId);
    }
}

What you want is dynamically switching foreign key reference between users and groups. Can you do that in database on single column? You can't and so you can't do it in ORM as well.
If you want to enforce integrity you must move Member_ID from parent to childs and map separate relations for it. It will create two columns in db and each will have its own FK:

public abstract class Principal
{
    public int Id { get; set; }
}

public class User : Principal
{
    public string Login { get; set; }
    public virtual ICollection<UserMemberOfGroup> MemberOf { get; set; }
}

public class Group : Principal
{
    public string Name { get; set; }
    public virtual ICollection<GroupMemberOfGroup> MemberOf { get; set; }
    public virtual ICollection<MemberOfGroup> Members { get; set; }
}

public enum MemberOfGroupDiscriminator
{
    MemberOfGroup, 
    GroupMemberOfGroup, 
    UserMemberOfGroup
}

public abstract class MemberOfGroup
{
    public int Id { get; set; }
    public int GroupId { get; set; }
    public Group Group { get; set; }
}

public class GroupMemberOfGroup : MemberOfGroup
{
    public int MemberGroupId { get; set; }
    public Group MemberGroup { get; set; }
}

public class UserMemberOfGroup : MemberOfGroup
{
    public int UserId { get; set; }
    public User User { get; set; }
}

public class Context : DbContext
{
    public DbSet<Principal> Principals { get; set; }
    public DbSet<MemberOfGroup> MembersOfGroups { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Principal>()
            .HasKey(p => p.Id)
            .Map<User>(u => u.Requires("Type").HasValue("U"))
            .Map<Group>(p => p.Requires("Type").HasValue("G"));

        modelBuilder.Entity<MemberOfGroup>()
            .HasKey(m => m.Id)
            .Map<GroupMemberOfGroup>(g => g.Requires("Type").HasValue("G"))
            .Map<UserMemberOfGroup>(u => u.Requires("Type").HasValue("U"));

        modelBuilder.Entity<User>()
            .HasMany(u => u.MemberOf)
            .WithRequired(m => m.User)
            .HasForeignKey(m => m.UserId)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Group>()
            .HasMany(u => u.MemberOf)
            .WithRequired(m => m.MemberGroup)
            .HasForeignKey(m => m.MemberGroupId)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Group>()
            .HasMany(u => u.Members)
            .WithRequired(m => m.Group)
            .HasForeignKey(m => m.GroupId);
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文