Fix: Entity Framework Many-To-Many SqlException mit “FOREIGN KEY constraint…”

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • 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&lt;User&gt; Users { get; set; }
    <p>
    4: public DbSet&lt;Group&gt; Groups { get; set; }
    <p>
    5: }
    <p>
    6:&nbsp;
    <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&lt;Group&gt; MemberOf { get; set; }
    <p>
    12: }
    <p>
    13:&nbsp;
    <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&lt;User&gt; 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&lt;User&gt; Users { get; set; }
    <p>
    5: public DbSet&lt;Tenant&gt; Tenants { get; set; }
    <p>
    6: public DbSet&lt;Group&gt; Groups { get; set; }
    <p>
    7:&nbsp;
    <p>
    8: }
    <p>
    9:&nbsp;
    <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&lt;Group&gt; MemberOf { get; set; }
    <p>
    18:&nbsp;
    <p>
    19: }
    <p>
    20:&nbsp;
    <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&lt;User&gt; Members { get; set; }
    <p>
    29: }
    <p>
    30:&nbsp;
    <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&lt;User&gt; Users { get; set; }
    <p>
    36: public List&lt;Group&gt; Groups { get; set; }
    <p>
    37:&nbsp;
    <p>
    38: }
    <p>
    39: }
    <p>

    <p>Dieser Code wird bei der Erzeugung der Db diesen Fehler werfen: </p>


    <p>An unhandled exception of type &#8216;System.Data.SqlClient.SqlException&#8217; occurred in EntityFramework.dll</p>


    <p>Additional information: Introducing FOREIGN KEY constraint &#8216;FK_dbo.UserGroups_dbo.Groups_Group_Id&#8217; on table &#8216;UserGroups&#8217; 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 &amp; 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&lt;User&gt;()
    <p>
    7: .HasRequired(u =&gt; u.Tenant)
    <p>
    8: .WithMany(t =&gt; t.Users)
    <p>
    9: .HasForeignKey(x =&gt; x.TenantId)
    <p>
    10: .WillCascadeOnDelete(false);
    <p>
    11:&nbsp;
    <p>
    12: modelBuilder.Entity&lt;Group&gt;()
    <p>
    13: .HasRequired(g =&gt; g.Tenant)
    <p>
    14: .WithMany(t =&gt; t.Groups)
    <p>
    15: .HasForeignKey(x =&gt; x.TenantId)
    <p>
    16: .WillCascadeOnDelete(false);
    <p>
    17:&nbsp;
    <p>
    18: base.OnModelCreating(modelBuilder);
    <p>
    19: }
    <p>
    20: public DbSet&lt;User&gt; Users { get; set; }
    <p>
    21: public DbSet&lt;Tenant&gt; Tenants { get; set; }
    <p>
    22: public DbSet&lt;Group&gt; Groups { get; set; }
    <p>
    23:&nbsp;
    <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