Reputation: 93
I got a wierd problem, i already know about why normaly this exception happen and how to solve it by Disablaing WillCascadeOnDelete to False in one of the properties.
But my problem seems to be other kind, Here is my code :
Memeber :
public class Memeber
{
public int MemberID { get; set; }
public virtual ICollection<Message> SentMessages { get; set; }
public virtual ICollection<Message> RecievedMessages { get; set; }
}
public class Message
{
public long MessageID { get; set; }
public int SenderMemberId{ get; set; }
public virtual ICollection<Member> RecipientsMembers { get; set; }
[ForeignKey("SenderMemberId")]
public virtual Member SenderMember { get; set; }
}
And here is the mapping : in Message configuration:
this.HasRequired(c => c.SenderMember)
.WithMany(c => c.SentMessages)
.HasForeignKey(c => c.SenderMemberId)
.WillCascadeOnDelete(false);
and in MemberConfiguration :
this.HasMany(c => c.RecievedMessages)
.WithMany(c => c.RecipientsMembers)
.Map(cm =>
{
cm.ToTable("MessageJoinMemeber");
cm.MapLeftKey("MessageId");
cm.MapRightKey("MemberId");
});
As you see a message hase a sender with ForeignKey SenderID, one to many bidirrectional. and Member has 2 list of messages one as reciever and one list as sender. I tried to disableing cascading on delete in one of those relation(first one) but i still get the same problem from SQL engine. Also i tried to define the relation from Message part and not from member part with :
this.HasMany(c => c.RecipientsMembers)
.WithMany(c=> c.RecievedMessages)
.Map ( cm =>
{
cm.ToTable("MessageJoinMemeber");
cm.MapLeftKey("MessageId");
cm.MapRightKey("MemberId");
});
But Allways i get this Error :
System.Data.SqlClient.SqlException was unhandled by user code
HResult=-2146232060
Message=Introducing FOREIGN KEY constraint 'FK_dbo.MessageJoinMemeber_dbo.Messages_MemberId' on table 'MessageJoinMemeber' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint. See previous errors.
Source=.Net SqlClient Data Provider
ErrorCode=-2146232060
Class=16
LineNumber=1
Number=1785
Procedure=""
Server=KINGPC
State=0
StackTrace:
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
Upvotes: 2
Views: 4806
Reputation: 14302
This should work fine (it's the same as what you have)
public class Member
{
public int MemberID { get; set; }
public string Name { get; set; }
public virtual ICollection<Message> SentMessages { get; set; }
public virtual ICollection<Message> RecievedMessages { get; set; }
}
public class Message
{
public long MessageID { get; set; }
public int SenderMemberId { get; set; }
public virtual ICollection<Member> RecipientsMembers { get; set; }
[ForeignKey("SenderMemberId")]
public virtual Member SenderMember { get; set; }
}
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Message>()
.HasRequired(c => c.SenderMember)
.WithMany(c => c.SentMessages)
.HasForeignKey(c => c.SenderMemberId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Member>()
.HasMany(c => c.RecievedMessages)
.WithMany(c => c.RecipientsMembers)
.Map(cm =>
{
cm.ToTable("MessageJoinMemeber");
cm.MapLeftKey("MessageId");
cm.MapRightKey("MemberId");
});
...
var member = new Member { Name = "sender1" };
db.Messages.Add(new Message
{
SenderMember = new Member { Name = "sender1" },
RecipientsMembers = new List<Member>
{
new Member { Name = "receiver1" },
new Member { Name = "receiver2" },
new Member { Name = "receiver3" },
}
});
db.SaveChanges();
...but if you still have problems you can make the join table manually...
// public virtual ICollection<Message> RecievedMessages { get; set; }
public virtual ICollection<MessageJoinMember> Recieved { get; set; }
...
// public virtual ICollection<Member> RecipientsMembers { get; set; }
public virtual ICollection<MessageJoinMember> Recipients { get; set; }
...
public class MessageJoinMember
{
public long MessageID { get; set; }
public Message Message { get; set; }
public int MemberID { get; set; }
public Member Member { get; set; }
}
...
modelBuilder.Entity<Message>()
.HasRequired(c => c.SenderMember)
.WithMany(c => c.SentMessages)
.HasForeignKey(c => c.SenderMemberId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<MessageJoinMember>()
.HasKey(x => new { x.MessageID, x.MemberID });
modelBuilder.Entity<MessageJoinMember>()
.HasRequired(x => x.Message)
.WithMany(x => x.Recipients)
.HasForeignKey(x => x.MessageID)
.WillCascadeOnDelete(false);
modelBuilder.Entity<MessageJoinMember>()
.HasRequired(x => x.Member)
.WithMany(x => x.Recieved)
.HasForeignKey(x => x.MemberID)
.WillCascadeOnDelete(false);
// remove the implicit many-to-many here
...
var message = new Message { SenderMember = new Member { Name = "sender1" }, };
db.MessageJoinMembers.Add(new MessageJoinMember { Message = message, Member = new Member { Name = "receiver1" } });
db.MessageJoinMembers.Add(new MessageJoinMember { Message = message, Member = new Member { Name = "receiver2" } });
db.MessageJoinMembers.Add(new MessageJoinMember { Message = message, Member = new Member { Name = "receiver3" } });
db.Messages.Add(message);
db.SaveChanges();
Upvotes: 3