Multiplicity violation broken by SQL Server 2008 - CodeFirst

I am working on a very tiring problem. I have a class called Nation and a class called NationAlly

public class Nation { public int ID {get; set;} public int name {get;set;} public List<NationAlly> NationAllies {get;set;} } public class NationAlly { public int ID {get; set;} public int level {get;set;} public Nation toNation {get;set;} } 

I use EF 4 and CodeFirst with a DbContext called NationsDB to manage my database on SQL Server 2008. If I create a new object of type Nation and I try to call countriesDB.SaveChanges, I got the following exception:

"Violation of plurality is limited. The role of the 'NationAlly_toNation_Target' relationship of 'CodeFirstNamespace.NationAlly_toNation' has a multiplicity of 1 or 0..1.

I tried to save Nation with the NationAllies null field, and this exception is not thrown, the country table in the database gets all the correct values.

In my database, the Nation table has 2 fields: ID (primary key), the name NationAlly table has 3 fields: ID (primary key), level, NationID Two tables are associated with the relationship, where NationAlly.NationID is the foreign key, and Nation. ID is the primary key.

Not weird? In my eyes, the NationAlly table should have a field called NationID1, and another NationID2 to create a “relationship” between the nation and a list of other countries.

What have I done wrong?

+8
sql-server-2008 entity-framework code-first
source share
2 answers

You may be the victim of EF Code-First mapping agreements that automatically create relationships between NationAllies and toNation that you do not want to have.

If I understand you correctly (but I'm not 100 percent sure if I do this), you really want to have two connections, and you have only one end of the relationship in each of the entities. Thus, NationAllies DOES NOT NationAllies to toNation , but to the "invisible" owner country in your NationAlly organization.

If so, you need to explicitly rewrite convention mappings. In Fluent API EF 4.1, it might look like this:

 public class MyContext : DbContext { public DbSet<Nation> Nations { get; set; } public DbSet<NationAlly> NationAllies { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Nation>() .HasMany(n => n.NationAllies) .WithRequired() .Map(conf => conf.MapKey("OwnerID")) .WillCascadeOnDelete(false); modelBuilder.Entity<NationAlly>() .HasRequired(a => a.toNation) .WithMany() .Map(conf => conf.MapKey("NationID")) .WillCascadeOnDelete(false); } } 

This mapping would create two foreign keys, OwnerID and NationID in the NationAllies table, pointing to the primary ID key in the Nations table.

Edit

Here is the application I tested:

  • Create a new console application in VS2010 / .NET 4.0, name it "NationsApp"
  • Add link to "EntityFramework.dll"
  • Clear the contents of "Program.cs" and paste the following in its place:

Program.cs Content:

 using System; using System.Collections.Generic; using System.Data.Entity; namespace NationsApp { public class Nation { public int ID { get; set; } public int name { get; set; } public List<NationAlly> NationAllies { get; set; } } public class NationAlly { public int ID { get; set; } public int level { get; set; } public Nation toNation { get; set; } } public class NationsContext : DbContext { public DbSet<Nation> Nations { get; set; } public DbSet<NationAlly> NationAllies { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Nation>() .HasMany(n => n.NationAllies) .WithRequired() .Map(conf => conf.MapKey("OwnerID")) .WillCascadeOnDelete(false); modelBuilder.Entity<NationAlly>() .HasRequired(a => a.toNation) .WithMany() .Map(conf => conf.MapKey("NationID")) .WillCascadeOnDelete(false); } } class Program { static void Main(string[] args) { using (var context = new NationsContext()) { try { // We have three Nations and two Allies Nation nation1 = new Nation() { NationAllies = new List<NationAlly>() }; Nation nation2 = new Nation() { NationAllies = new List<NationAlly>() }; Nation nation3 = new Nation() { NationAllies = new List<NationAlly>() }; NationAlly ally1 = new NationAlly(); NationAlly ally2 = new NationAlly(); // Nation1 has two Allies // (Nation1 is the "owner" of both Allies) nation1.NationAllies.Add(ally1); nation1.NationAllies.Add(ally2); // toNation of ally1 refers to Nation2 ally1.toNation = nation2; // toNation of ally2 refers to Nation3 ally2.toNation = nation3; context.Nations.Add(nation1); context.Nations.Add(nation2); context.Nations.Add(nation3); context.SaveChanges(); } catch (Exception e) { throw; } } } } } 

You can set a breakpoint on "throw" to see possible exceptions in e in the debugger.

This creates a database called NationsApp.NationsContext if you are using SQL Server Express and do not have any additional connection strings.

It gives two relations Nation_NationAllies (FK - "OwnerID") and NationAlly_toNation (FK - "NationID"). All columns are not NULL. The result in the database is as follows:

Nations and NationAllies

+11
source share

In case this helps someone get this error ... I received this message by executing queries, rather than storing it in the database. My data design:

 public class Base { public int Id {get; set;} } public class Child { [Key][ForeignKey("Base")] public int Id {get; set;} public virtual Base Base {get; set;} public Child() { Base = new Base(); } } 

The problem was in the constructor. Turns out EF4.1 doesn't like it when you initialize associations there! I removed this constructor and everything started working again.

+6
source share

All Articles