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

时间:2011-04-01 16:05:29

标签: entity-framework inheritance foreign-keys entity-framework-4.1 ef-code-first

我有以下用于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");

我的想法是允许用户和群组能够组建群组。所以GroupB可以是GroupA加上另外一个人。我的问题是,当判别式出现时,我如何强制框架检查Member_ID是否是User的关键,以及Group。

BTW,用户和组实体具有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();
        }

我知道这甚至都不是最好的方法,但它说明了我的问题。 BTW,存储库&lt;&gt;类共享DBContext。所以基本上,。保存在一个是。保存所有。这工作正常。

此代码采用以分号分隔的组ID字符串,并将User添加到它们中。但是,我得到了这个例外:

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?,所以它应该是可以为空的,我想。这里发生了什么?

1 个答案:

答案 0 :(得分:1)

您想要的是动态切换用户和组之间的外键引用。你能在单列数据库中做到这一点吗?你不能,所以你也不能在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);
    }
}