Enity Framework M zu N oder “Many-To-Many”:
<p>Eine simple M zu N Beziehung mittels Entity Framework Code First zu definieren ist ziemlich trivial:</p>
1: public class DemoContext : DbContext
<p>
2: {
<p>
3: public DbSet<User> Users { get; set; }
<p>
4: public DbSet<Group> Groups { get; set; }
<p>
5: }
<p>
6:
<p>
7: public class User
<p>
8: {
<p>
9: public Guid Id { get; set; }
<p>
10: public string DisplayName { get; set; }
<p>
11: public virtual List<Group> MemberOf { get; set; }
<p>
12: }
<p>
13:
<p>
14: public class Group
<p>
15: {
<p>
16: public Guid Id { get; set; }
<p>
17: public string DisplayName { get; set; }
<p>
18: public virtual List<User> Members { get; set; }
<p>
19: }
<p>
20: }
<p>
<p>Alles was man dazu braucht ist jeweils eine Liste des jeweiligen anderen Typs, welche mit “virtual” gekennzeichnet ist.</p>
Problem: “Introducing FOREIGN KEY constraint… may cause cycles or multiple cascade” – “Multitenant-Szenario”
<p>In meinem Beispiel gab es aber noch die Entität des “Tenants” – jeder Tenant hat X-User und X-Groups. Das hat das normale Mapping etwas aus dem Konzept gebracht.</p>
<p>Folgender Code:</p>
1: public class DemoContext : DbContext
<p>
2: {
<p>
3:
<p>
4: public DbSet<User> Users { get; set; }
<p>
5: public DbSet<Tenant> Tenants { get; set; }
<p>
6: public DbSet<Group> Groups { get; set; }
<p>
7:
<p>
8: }
<p>
9:
<p>
10: public class User
<p>
11: {
<p>
12: public Guid Id { get; set; }
<p>
13: public string DisplayName { get; set; }
<p>
14: public Tenant Tenant { get; set; }
<p>
15: [ForeignKey("Tenant")]
<p>
16: public Guid TenantId { get; set; }
<p>
17: public virtual List<Group> MemberOf { get; set; }
<p>
18:
<p>
19: }
<p>
20:
<p>
21: public class Group
<p>
22: {
<p>
23: public Guid Id { get; set; }
<p>
24: public string DisplayName { get; set; }
<p>
25: public Tenant Tenant { get; set; }
<p>
26: [ForeignKey("Tenant")]
<p>
27: public Guid TenantId { get; set; }
<p>
28: public virtual List<User> Members { get; set; }
<p>
29: }
<p>
30:
<p>
31: public class Tenant
<p>
32: {
<p>
33: public Guid Id { get; set; }
<p>
34: public string Name { get; set; }
<p>
35: public List<User> Users { get; set; }
<p>
36: public List<Group> Groups { get; set; }
<p>
37:
<p>
38: }
<p>
39: }
<p>
<p>Dieser Code wird bei der Erzeugung der Db diesen Fehler werfen: </p>
<p>An unhandled exception of type ‘System.Data.SqlClient.SqlException’ occurred in EntityFramework.dll</p>
<p>Additional information: Introducing FOREIGN KEY constraint ‘FK_dbo.UserGroups_dbo.Groups_Group_Id’ on table ‘UserGroups’ may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.</p>
<p>Could not create constraint. See previous errors.</p>
Lösung des Problems: OnModelCreation & die Fluent-API
<p>Durch die Verlinkung des Tenants kann das Entity Framework nicht mehr automatisch die entsprechenden Constraints ermiteln. Es hat bei mir eine Weile gedauert und <a href="http://www.britishdeveloper.co.uk/2013/05/how-to-solve-introducing-foreign-key.html">ich bin erst durch diesen Blogpost</a> auf das Ergebnis gekommen, aber dies ist des Rätsels Lösung:</p>
1: public class DemoContext : DbContext
<p>
2: {
<p>
3: protected override void OnModelCreating(DbModelBuilder modelBuilder)
<p>
4: {
<p>
5:
<p>
6: modelBuilder.Entity<User>()
<p>
7: .HasRequired(u => u.Tenant)
<p>
8: .WithMany(t => t.Users)
<p>
9: .HasForeignKey(x => x.TenantId)
<p>
10: .WillCascadeOnDelete(false);
<p>
11:
<p>
12: modelBuilder.Entity<Group>()
<p>
13: .HasRequired(g => g.Tenant)
<p>
14: .WithMany(t => t.Groups)
<p>
15: .HasForeignKey(x => x.TenantId)
<p>
16: .WillCascadeOnDelete(false);
<p>
17:
<p>
18: base.OnModelCreating(modelBuilder);
<p>
19: }
<p>
20: public DbSet<User> Users { get; set; }
<p>
21: public DbSet<Tenant> Tenants { get; set; }
<p>
22: public DbSet<Group> Groups { get; set; }
<p>
23:
<p>
24: }
<p>
<p>Mit diesem Code sollte die “gewünschte” Datenbank erstellt werden:</p>
<p><a href="http://code-inside.de/blog/wp-content/uploads/image1974.png"></a></p>
<p>Dem gesamten Code gibt es <a href="https://github.com/Code-Inside/Samples/tree/master/2013/EfNToM">natürlich auch auf GitHub</a>. </p>
<p>Eine simple M zu N Beziehung mittels Entity Framework Code First zu definieren ist ziemlich trivial:</p>
1: public class DemoContext : DbContext
<p>
2: {
<p>
3: public DbSet<User> Users { get; set; }
<p>
4: public DbSet<Group> Groups { get; set; }
<p>
5: }
<p>
6:
<p>
7: public class User
<p>
8: {
<p>
9: public Guid Id { get; set; }
<p>
10: public string DisplayName { get; set; }
<p>
11: public virtual List<Group> MemberOf { get; set; }
<p>
12: }
<p>
13:
<p>
14: public class Group
<p>
15: {
<p>
16: public Guid Id { get; set; }
<p>
17: public string DisplayName { get; set; }
<p>
18: public virtual List<User> Members { get; set; }
<p>
19: }
<p>
20: }
<p>
<p>Alles was man dazu braucht ist jeweils eine Liste des jeweiligen anderen Typs, welche mit “virtual” gekennzeichnet ist.</p>
Problem: “Introducing FOREIGN KEY constraint… may cause cycles or multiple cascade” – “Multitenant-Szenario”
<p>In meinem Beispiel gab es aber noch die Entität des “Tenants” – jeder Tenant hat X-User und X-Groups. Das hat das normale Mapping etwas aus dem Konzept gebracht.</p>
<p>Folgender Code:</p>
1: public class DemoContext : DbContext
<p>
2: {
<p>
3:
<p>
4: public DbSet<User> Users { get; set; }
<p>
5: public DbSet<Tenant> Tenants { get; set; }
<p>
6: public DbSet<Group> Groups { get; set; }
<p>
7:
<p>
8: }
<p>
9:
<p>
10: public class User
<p>
11: {
<p>
12: public Guid Id { get; set; }
<p>
13: public string DisplayName { get; set; }
<p>
14: public Tenant Tenant { get; set; }
<p>
15: [ForeignKey("Tenant")]
<p>
16: public Guid TenantId { get; set; }
<p>
17: public virtual List<Group> MemberOf { get; set; }
<p>
18:
<p>
19: }
<p>
20:
<p>
21: public class Group
<p>
22: {
<p>
23: public Guid Id { get; set; }
<p>
24: public string DisplayName { get; set; }
<p>
25: public Tenant Tenant { get; set; }
<p>
26: [ForeignKey("Tenant")]
<p>
27: public Guid TenantId { get; set; }
<p>
28: public virtual List<User> Members { get; set; }
<p>
29: }
<p>
30:
<p>
31: public class Tenant
<p>
32: {
<p>
33: public Guid Id { get; set; }
<p>
34: public string Name { get; set; }
<p>
35: public List<User> Users { get; set; }
<p>
36: public List<Group> Groups { get; set; }
<p>
37:
<p>
38: }
<p>
39: }
<p>
<p>Dieser Code wird bei der Erzeugung der Db diesen Fehler werfen: </p>
<p>An unhandled exception of type ‘System.Data.SqlClient.SqlException’ occurred in EntityFramework.dll</p>
<p>Additional information: Introducing FOREIGN KEY constraint ‘FK_dbo.UserGroups_dbo.Groups_Group_Id’ on table ‘UserGroups’ may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.</p>
<p>Could not create constraint. See previous errors.</p>
Lösung des Problems: OnModelCreation & die Fluent-API
<p>Durch die Verlinkung des Tenants kann das Entity Framework nicht mehr automatisch die entsprechenden Constraints ermiteln. Es hat bei mir eine Weile gedauert und <a href="http://www.britishdeveloper.co.uk/2013/05/how-to-solve-introducing-foreign-key.html">ich bin erst durch diesen Blogpost</a> auf das Ergebnis gekommen, aber dies ist des Rätsels Lösung:</p>
1: public class DemoContext : DbContext
<p>
2: {
<p>
3: protected override void OnModelCreating(DbModelBuilder modelBuilder)
<p>
4: {
<p>
5:
<p>
6: modelBuilder.Entity<User>()
<p>
7: .HasRequired(u => u.Tenant)
<p>
8: .WithMany(t => t.Users)
<p>
9: .HasForeignKey(x => x.TenantId)
<p>
10: .WillCascadeOnDelete(false);
<p>
11:
<p>
12: modelBuilder.Entity<Group>()
<p>
13: .HasRequired(g => g.Tenant)
<p>
14: .WithMany(t => t.Groups)
<p>
15: .HasForeignKey(x => x.TenantId)
<p>
16: .WillCascadeOnDelete(false);
<p>
17:
<p>
18: base.OnModelCreating(modelBuilder);
<p>
19: }
<p>
20: public DbSet<User> Users { get; set; }
<p>
21: public DbSet<Tenant> Tenants { get; set; }
<p>
22: public DbSet<Group> Groups { get; set; }
<p>
23:
<p>
24: }
<p>
<p>Mit diesem Code sollte die “gewünschte” Datenbank erstellt werden:</p>
<p><a href="http://code-inside.de/blog/wp-content/uploads/image1974.png"></a></p>
<p>Dem gesamten Code gibt es <a href="https://github.com/Code-Inside/Samples/tree/master/2013/EfNToM">natürlich auch auf GitHub</a>. </p>
4.017 mal gelesen