EF is throwing the following exception:
System.InvalidOperationException: Referential integrity constraint violation. A Dependent Role has multiple principals with different values.
I have two entities, StateProvince and Country:
public class StateProvince
{
public long Id { get; set; }
[StringLength(100)]
public string Name { get; set; }
public long CountryId { get; set; }
public Country Country { get; set; }
}
public class Country
{
public long Id { get; set; }
[StringLength(2)]
public string Code { get; set; }
public IEnumerable<StateProvince> StatesProvinces { get; set; }
}
and two entities that use them:
public class Customer
{
public long Id { get; set; }
public long BillingStateProvinceId { get; set; }
[ForeignKey("Id")]
public StateProvince BillingStateProvince { get; set; }
public long BillingCountryId { get; set; }
[ForeignKey("Id")]
public Country BillingCountry { get; set; }
public long ShippingStateProvinceId { get; set; }
[ForeignKey("Id")]
public StateProvince ShippingStateProvince { get; set; }
public long ShippingCountryId { get; set; }
[ForeignKey("Id")]
public Country ShippingCountry { get; set; }
}
public class Vendor
{
public long Id { get; set; }
public long StateProvinceId { get; set; }
public StateProvince StateProvince { get; set; }
public long CountryId { get; set; }
public Country Country { get; set; }
}
And in my OnModelCreating:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>()
.HasRequired(m => m.ShippingStateProvince)
.WithOptional().WillCascadeOnDelete(false);
modelBuilder.Entity<Customer>()
.HasRequired(m => m.ShippingCountry)
.WithOptional().WillCascadeOnDelete(false);
modelBuilder.Entity<Customer>()
.HasRequired(m => m.BillingStateProvince)
.WithOptional().WillCascadeOnDelete(false);
modelBuilder.Entity<Customer>()
.HasRequired(m => m.BillingCountry)
.WithOptional().WillCascadeOnDelete(false);
modelBuilder.Entity<Vendor>()
.HasRequired(m => m.StateProvince)
.WithOptional().WillCascadeOnDelete(false);
modelBuilder.Entity<Vendor>()
.HasRequired(m => m.Country)
.WithOptional().WillCascadeOnDelete(false);
base.OnModelCreating(modelBuilder);
}
Finally, the code that throws the exception:
[TestMethod]
public void TestMethod1()
{
Db db = new Db();
#region Set/reset items
Country us = db.Countries.FirstOrDefault();
if (us == null)
{
us = new Country { Code = "US" };
db.Countries.Add(us);
db.SaveChanges();
}
long usId = us.Id;
List<StateProvince> states = db.StateProvinces.ToList();
StateProvince mass = states.Where(m => m.Name == "MA").FirstOrDefault();
StateProvince ct = states.Where(m => m.Name == "CT").FirstOrDefault();
if (mass == null)
{
mass = new StateProvince { Name = "MA", CountryId = usId };
ct = new StateProvince { Name = "CT", CountryId = usId };
db.StateProvinces.Add(mass);
db.StateProvinces.Add(ct);
db.SaveChanges();
}
long massId = mass.Id;
long ctId = ct.Id;
List<Customer> customersToRemove = db.Customers.ToList();
db.Customers.RemoveRange(customersToRemove);
db.SaveChanges();
#endregion
Customer customerToAdd = new Customer {
BillingStateProvinceId = massId,
ShippingStateProvinceId = ctId,
ShippingCountryId = usId,
BillingCountryId = usId,
BillingStateProvince = mass,
ShippingStateProvince = ct,
BillingCountry = us,
ShippingCountry = us
};
db.Customers.Add(customerToAdd);
try
{
//exception thrown here
db.SaveChanges();
}
catch (Exception e)
{
throw;
}
db.Dispose();
Db dbCheck = new Db();
Customer customer = dbCheck.Customers.Include(m => m.BillingStateProvince).Include(m => m.ShippingStateProvince).FirstOrDefault();
dbCheck.Dispose();
}
I suspect the issue is either I'm not using the [ForeignKey] attribute correctly or I'm doing something wrong in OnModelCreating. I can make it work if I only have one StateProvince and one Country entity in the Customer entity, however, once I have two, can't figure it out. Any help?
On a broader note does anyone have any links to blogs/posts/articles/tutorials on how to model addresses with c# EF code first? I came across this one , which uses the [ComplexType] attribute, the problem there is that I can't encapsulate the StateRegion and Country classes within it. My current thinking is to just bake in the various address fields into each class, ie MailingStreetAddress, BillingStreetAddress, etc. I would like to see how others have handled addresses to gain a better understanding on how to do my own.
Per @Sam I am, I tried to change attribute to:
[ForeignKey("BillingStateProvinceId")]
for each property. Upon dropping/recreating the db, EF threw this error:
Customer_BillingCountry_Source: : Multiplicity is not valid in Role 'Customer_BillingCountry_Source' in relationship 'Customer_BillingCountry'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be ' '. Customer_BillingStateProvince_Source: : Multiplicity is not valid in Role 'Customer_BillingStateProvince_Source' in relationship 'Customer_BillingStateProvince'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be ' '. Customer_ShippingCountry_Source: : Multiplicity is not valid in Role 'Customer_ShippingCountry_Source' in relationship 'Customer_ShippingCountry'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be ' '. Customer_ShippingStateProvince_Source: : Multiplicity is not valid in Role 'Customer_ShippingStateProvince_Source' in relationship 'Customer_ShippingStateProvince'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be ' '.
Per @Steve Green, changed my StateRegion, Country entities, and OnModelCreating, which now throws this error when I drop recreate the db:
Schema specified is not valid. Errors: The relationship 'StackOverflow.Customer_ShippingCountry' was not loaded because the type 'StackOverflow.Country' is not available.
My edited code:
public class StateProvince
{
public long Id { get; set; }
[StringLength(100)]
public string Name { get; set; }
//new properties
public ICollection<Customer> Customers { get; set; }
public ICollection<Vendor> Vendors { get; set; }
public long CountryId { get; set; }
public Country Country { get; set; }
}
public class Country
{
public long Id { get; set; }
[StringLength(2)]
public string Code { get; set; }
//new properties
public ICollection<Customer> Customers { get; set; }
public ICollection<Vendor> Vendors { get; set; }
public IEnumerable<StateProvince> StatesProvinces { get; set; }
}
And my OnModelCreating to mirror his:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>()
.HasRequired(m => m.ShippingStateProvince)
.WithMany(m => m.Customers)
.HasForeignKey(m => m.ShippingStateProvinceId);
modelBuilder.Entity<Customer>()
.HasRequired(m => m.ShippingCountry)
.WithMany(m => m.Customers)
.HasForeignKey(m => m.ShippingCountryId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Customer>()
.HasRequired(m => m.BillingStateProvince)
.WithMany(m => m.Customers)
.HasForeignKey(m => m.BillingStateProvinceId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Customer>()
.HasRequired(m => m.BillingCountry)
.WithMany(m => m.Customers)
.HasForeignKey(m => m.BillingCountryId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Vendor>()
.HasRequired(m => m.StateProvince)
.WithMany(m => m.Vendors)
.HasForeignKey(m => m.StateProvinceId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Vendor>()
.HasRequired(m => m.Country)
.WithMany(m => m.Vendors)
.HasForeignKey(m => m.CountryId)
.WillCascadeOnDelete(false);
base.OnModelCreating(modelBuilder);
}
I believe that you're using the ForeignKeyAttribute incorrectly. I normally do it like this
[ForeignKey("BillingStateProvince")]
public long BillingStateProvinceId { get; set; }
public StateProvince BillingStateProvince { get; set; }
although I believe this is also valid,
public long BillingStateProvinceId { get; set; }
[ForeignKey("BillingStateProvinceId")]
public StateProvince BillingStateProvince { get; set; }
'BillingStateProvinceId' is the foreign key. when you do [ForeignKey("Id")]
, you're pointing it to Customer.Id
, not StateProvince.Id
You also probably want to mark you Ids with the KeyAttribute
[Key]
public long Id { get; set; }
Since you already have a fluent config, you could go with something like this and forgo the annotations:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>()
.HasRequired(m => m.StateProvince)
.WithMany()
.HasForeignKey(c => c.ShippingStateProvinceId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Customer>()
.HasRequired(m => m.Country)
.WithMany()
.HasForeignKey(c => c.ShippingCountryId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Customer>()
.HasRequired(m => m.StateProvince)
.WithMany()
.HasForeignKey(c => c.BillingStateProvinceId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Customer>()
.HasRequired(m => m.Country)
.WithMany()
.HasForeignKey(c => c.BillingCountryId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Vendor>()
.HasRequired(m => m.StateProvince)
.WithMany()
.HasForeignKey(c => c.StateProvinceId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Vendor>()
.HasRequired(m => m.StateProvince)
.WithMany()
.HasForeignKey(c => c.CountryId)
.WillCascadeOnDelete(false);
base.OnModelCreating(modelBuilder);
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.