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?
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);
}
}
精彩评论