EF异常,如何首先在c#和实体框架代码中建模地址

时间:2015-07-15 15:02:17

标签: c# linq entity-framework

EF抛出以下异常:

  

System.InvalidOperationException:参照完整性约束违规。依赖角色具有多个具有不同值的主体。

我有两个实体,StateProvince和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; }
}

和两个使用它们的实体:

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; }
}

在我的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);
    }

最后,抛出异常的代码:

[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();
    }

我怀疑问题是我没有正确使用[ForeignKey]属性,或者我在OnModelCreating中做错了什么。如果我在Customer实体中只有一个StateProvince和一个Country实体,我可以使它工作,但是,一旦我有两个,就无法弄明白。有什么帮助吗?

更广泛地说,是否有人有关于如何使用c#EF代码建模地址的博客/帖子/文章/教程的链接?我遇到this one,它使用[ComplexType]属性,问题是我无法封装其中的StateRegion和Country类。我目前的想法是将各个地址字段烘焙到每个类中,即MailingStreetAddress,BillingStreetAddress等。我想看看其他人如何处理地址以更好地理解如何自己做。

修改

Per @Sam我是,我试图将属性更改为:

[ForeignKey("BillingStateProvinceId")]

每个属性。删除/重新创建数据库后,EF抛出了此错误:

  

Customer_BillingCountry_Source :: Multiplicity在关系'Customer_BillingCountry'中的角色'Customer_BillingCountry_Source'中无效。由于“从属角色”属性不是关键属性,因此从属角色的多重性的上限必须为“”。   Customer_BillingStateProvince_Source ::多重性在关系'Customer_BillingStateProvince'中的角色'Customer_BillingStateProvince_Source'中无效。由于Dependent Role属性不是关键属性,因此Dependent Role的多重性的上限必须为“”。   Customer_ShippingCountry_Source :: Multiplicity在关系'Customer_ShippingCountry'中的角色'Customer_ShippingCountry_Source'中无效。由于“从属角色”属性不是关键属性,因此从属角色的多重性的上限必须为“”。   Customer_ShippingStateProvince_Source ::多重性在关系'Customer_ShippingStateProvince'中的角色'Customer_ShippingStateProvince_Source'中无效。由于Dependent Role属性不是关键属性,因此Dependent Role的多重性的上限必须为“”。

编辑2

Per @Steve Green,更改了我的StateRegion,Country实体和OnModelCreating,当我删除重新创建db时,它现在抛出此错误:

  

指定的架构无效。错误:未加载“StackOverflow.Customer_ShippingCountry”关系,因为“StackOverflow.Country”类型不可用。

我编辑的代码:

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; }
}

我的OnModel创建镜像他的:

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);
    }

2 个答案:

答案 0 :(得分:1)

我相信你错误地使用了ForeignKeyAttribute。我通常这样做

[ForeignKey("BillingStateProvince")]
public long BillingStateProvinceId { get; set; }
public StateProvince BillingStateProvince { get; set; }

虽然我相信这也是有效的,

public long BillingStateProvinceId { get; set; }
[ForeignKey("BillingStateProvinceId")]
public StateProvince BillingStateProvince { get; set; }

'BillingStateProvinceId'是外键。当您执行[ForeignKey("Id")]时,您指的是Customer.Id,而不是StateProvince.Id

你也可能想用KeyAttribute

标记你的ID
[Key]
public long Id { get; set; }

答案 1 :(得分:1)

由于你已经有了一个流畅的配置,你可以使用这样的东西并放弃注释:

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);
    }

https://msdn.microsoft.com/en-us/data/jj591620.aspx#Model