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:
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.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
Make your classes like below:
public class ClassA
{
[Index(IsUnique = true)]
[MaxLength(200)]
public String AId { get; set; }
public virtual List<AB> AB { get; set; }
public List<ClassC> C { get; set; }
}
public class ClassB
{
public int BId { get; set; }
public virtual List<AB> AB { get; set; }
}
public class AB
{
public int Id { get; set; }
[Required]
public virtual A A { get; set; }
[Required]
public virtual B B { get; set; }
[MaxLength(200)]
public string AId { get; set; }
public int BId { get; set; }
}
public class ClassC
{
public int CId { get; set; }
public ClassA A { get; set; }
public int AId { get; set; }
}
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.