I'm playing around with Entity Framework 6 Code First and have the problem that a many-to-many relationship is not correctly synchronized to my "internal" objects.
I got following classes:
public class ClassA
{
public String AId { get; set; }
public ICollection<ClassB> Bs { get; set; }
public ICollection<ClassC> Cs { get; set; }
}
public class ClassB
{
public int BId { get; set; }
ICollection<ClassA> As { get; set; }
}
public class ClassC
{
public int CId { get; set; }
public ClassA A { get; set; }
}
A and B have a many-to-many relationship where A and C have a one-to-many relationship. I use Fluent API to build the mappings:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ClassA>().ToTable("ClassA");
modelBuilder.Entity<ClassA>().HasKey(a => a.AId);
modelBuilder.Entity<ClassA>().HasManyClassB>(a => a.Bs).WithMany(b => b.As).Map(
a =>
{
a.MapLeftKey("AId");
a.MapRightKey("BId");
a.ToTable("ClassAClassB");
});
modelBuilder.Entity<ClassA>().Property(a => a.OrderId).IsVariableLength().HasColumnName("AId");
modelBuilder.Entity<ClassB>().ToTable("ClassB");
modelBuilder.Entity<ClassB>().HasKey(b => b.BId);
modelBuilder.Entity<ClassB>().Property(b => b.BId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
.HasColumnName("BId");
modelBuilder.Entity<ClassC>().ToTable("ClassC");
modelBuilder.Entity<ClassC>().HasKey(c => c.CId);
modelBuilder.Entity<ClassC>().HasRequired(c => c.A).WithMany(a => a.Bs).Map(m => m.MapKey("AId"));
modelBuilder.Entity<ClassC>().Property(c => c.CId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
.HasColumnName("CId");
}
If i create objects of class ClassA, ClassB and ClassC, add them to my database context and invoke SaveChanges(). In my opinion the values are stored in the database correctly:
MariaDB [Test]> select * from ClassA;
+----------+
| BId |
+----------+
| 2017-002 |
+----------+
MariaDB [Test]> select * from ClassB;
+--------+
| BId |
+--------+
| 2 |
+--------+
MariaDB [OMSData]> select * from ClassAClassB;
+----------+-----+
| AId | BId |
+----------+-----+
| 2017-002 | 2 |
+----------+-----+
MariaDB [Test]> select * from classC;
+----+-----------+
| CId | AId |
+-----+----------+
| 1 | 2017-002 |
+-----+----------+
However, if I execute my test program another time, the relationships are not correctly restored.
This means:
- The B's collection in ClassA does not contain any object (and vice versa).
- I can only access the object of ClassC through ClassA if I accessed this object beforehand through my database context.
Do I have to invoke the synchronization manually or is there an error in model definition?
Thanks in advance!
You are not building your classes right. Think logically, you need to store your many to many bindings somewhere, meaning you need a third entity class which stores the many-to-many relations.
Make your classes like below:
Create a code-first migration and entity framework will understand this and sketch out your database accordingly. Store just the IDs of A and B in the third entity AB.
For anyone facing the same problem than me, my issue was that I was missing the word
virtual
in the related entity. As far as I know,virtual
is needed when Lazy Loading is enabled, this helps Entity Framework to load the navigation property correctly. Hope it helps.