2011-05-09 70 views
6

我有一個客戶實體引用一組地址。這裏的複雜之處在於我希望能夠識別特定地址作爲默認地址。EF Code First 4.1 - 如何配置默認的一對多關係

如果可能,我想在客戶表中保存默認地址的FK。這似乎比在地址表中使用列來標識默認值更爲優雅。

我在定義這種關係方面遇到了流暢的API問題。當我運行下面的代碼時,我得到一個異常,它說: 「保存不會爲其關係公開外鍵屬性的實體時發生錯誤。EntityEntries屬性將返回null,因爲無法將單個實體標識爲例外情況:通過在實體類型中公開外鍵屬性,可以更輕鬆地處理保存時的異常,詳情請參閱InnerException。 「無法確定依賴操作的有效排序。由於外鍵約束,模型要求或存儲生成的值,可能存在依賴關係。」

我創建了一個控制檯應用程序來顯示確切的問題。在這個測試應用程序中,我有一個Customer實體,一個Address和api api配置。

任何幫助將非常感激:

using System; 
using System.Collections.Generic; 
using System.Data.Entity.ModelConfiguration; 
using System.ComponentModel.DataAnnotations; 
using System.Data.Entity; 

namespace OneToManyWithDefault 
{ 

    public class Customer 
    { 
     private ICollection<Address> m_Addresses; 

     public Customer() 
     { 
      Addresses = new List<Address>(); 
     } 

     public int Id { get; set; } 
     public string CompanyName { get; set; } 
     public virtual ICollection<Address> Addresses 
     { 
      get 
      { 
       if (m_Addresses == null) 
       { 
        m_Addresses = new List<Address>(); 
       } 
       return m_Addresses; 
      } 
      set 
      { 
       m_Addresses = value; 
      } 
     } 
     public Address DefaultAddress { get; set; } 
     public int DefaultAddressId { get; set; } 

    } 

    public class Address 
    { 
     public int Id { get; set; } 
     public string Town { get; set; } 
     public Customer Customer { get; set; } 
    } 

    public class MyContext 
     : DbContext 
    { 
     public DbSet<Customer> Customers { get; set; } 

     public MyContext(string connectionString) 
      : base(connectionString) 
     { 

     } 

     protected override void OnModelCreating(DbModelBuilder modelBuilder) 
     { 
      modelBuilder.Configurations.Add(new CustomerConfiguration()); 
      modelBuilder.Configurations.Add(new AddressConfiguration()); 
      base.OnModelCreating(modelBuilder); 
     } 
    } 

    public class CustomerConfiguration 
     : EntityTypeConfiguration<Customer> 
    { 
     public CustomerConfiguration() 
      : base() 
     { 
      HasKey(p => p.Id); 
      Property(p => p.Id) 
       .HasColumnName("Id") 
       .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity) 
       .IsRequired(); 
      Property(p => p.CompanyName) 
       .HasColumnName("Name") 
       .IsRequired(); 

      // Configure the mapping for the Default Address (this is likely to be wrong!): 
      HasRequired(p => p.DefaultAddress).WithMany() 
       .Map(x => x.MapKey("DefaultAddressId")) 
       .WillCascadeOnDelete(false); 
      HasRequired(p => p.DefaultAddress) 
       .WithMany() 
       .HasForeignKey(x => x.DefaultAddressId); 

      ToTable("Customers"); 
     } 
    } 

    public class AddressConfiguration 
     : EntityTypeConfiguration<Address> 
    { 
     public AddressConfiguration() 
      : base() 
     { 
      HasKey(p => p.Id); 
      Property(p => p.Id) 
       .HasColumnName("Id") 
       .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity) 
       .IsRequired(); 
      Property(p => p.Town) 
       .HasColumnName("Town") 
       .IsRequired(); 

      HasRequired(p => p.Customer) 
       .WithMany(c => c.Addresses) 
       .Map(x => x.MapKey("CustomerId")); 

      ToTable("Addresses"); 
     } 
    } 

    class Program 
    { 
     private const string ConnectionString = 
      @"Server=.\sql2005;Database=OneToManyWithDefault;integrated security=SSPI;"; 

     static void Main(string[] args) 
     { 
      Customer headOffice = new Customer(); 
      headOffice.CompanyName = "C1"; 

      Address address = new Address(); 
      address.Town = "Colchester"; 
      headOffice.Addresses.Add(address); 

      address = new Address(); 
      address.Town = "Norwich"; 
      headOffice.Addresses.Add(address); 
      headOffice.DefaultAddress = address; 

      MyContext context = new MyContext(ConnectionString); 
      context.Customers.Add(headOffice); 
      context.SaveChanges(); 

      Console.WriteLine("Done."); 
      Console.ReadLine(); 
     } 
    } 
} 

非常感謝,

保羅。

回答

7

我不明白什麼EF在那裏談論異常中的「未暴露的外鍵」。我會考慮內部例外作爲重要部分:

無法確定依賴操作的有效排序 。由於外鍵 約束,模型要求或 存儲生成的值,可能存在依存關係 。

我想在你的模型問題是,你有CustomerAddress之間的相互依存:一個地址需要一個客戶(你已經將其標記爲在映射代碼需要)和另一隻手客戶需要一個地址(默認地址是要求都是由於不可空的外鍵和由於您的映射代碼)。因此,EF不知道您的示例代碼中首先要保存哪個實體 - 默認地址還是客戶?兩個實體都需要使用有效的FK限制保存另一個實體的主鍵。

我能看到的最簡單的方法是使默認地址在模型可選,然後保存兩次(我忽略其工作按照約定的映射反正):

public class Customer 
{ 
    private ICollection<Address> m_Addresses; 

    public Customer() { Addresses = new List<Address>(); } 

    public int Id { get; set; } 
    public string CompanyName { get; set; } 
    public virtual ICollection<Address> Addresses { get { ... } set { ... } } 
    public Address DefaultAddress { get; set; } 
    public int? DefaultAddressId { get; set; } // FK for optional relationship 
} 

public class Address 
{ 
    public int Id { get; set; } 
    public string Town { get; set; } 
    public Customer Customer { get; set; } 
} 

// ... 

public class CustomerConfiguration : EntityTypeConfiguration<Customer> 
{ 
    public CustomerConfiguration() : base() 
    { 
     Property(p => p.CompanyName) 
      .HasColumnName("Name") 
      .IsRequired(); 

     HasMany(c => c.Addresses) 
      .WithRequired(a => a.Customer) 
      .Map(x => x.MapKey("CustomerId")); 
    } 
} 

public class AddressConfiguration : EntityTypeConfiguration<Address> 
{ 
    public AddressConfiguration() : base() 
    { 
     Property(p => p.Town) 
      .HasColumnName("Town") 
      .IsRequired(); 
    } 
} 

然後你的程序看起來像這樣的:

static void Main(string[] args) 
{ 
    Customer headOffice = new Customer(); 
    headOffice.CompanyName = "C1"; 

    Address address = new Address(); 
    address.Town = "Colchester"; 
    headOffice.Addresses.Add(address); 

    address = new Address(); 
    address.Town = "Norwich"; 
    headOffice.Addresses.Add(address); 

    //headOffice.DefaultAddress = address; 
    //We don't set the default address here as SaveChanges would throw an 
    //exception. But because it is optional now we are allowed to leave it null. 

    MyContext context = new MyContext(ConnectionString); 
    context.Customers.Add(headOffice); 
    context.SaveChanges(); 

    headOffice.DefaultAddress = address; // headoffice and address have now PKs 
    context.SaveChanges(); // Updates headoffice in the DB with default address 
} 

這雙SaveChanges是醜陋的,但我沒有看到另一種方式。

+0

謝謝你,這是有道理的,你在這裏說。我會試試這個,並在這裏更新我如何繼續。 – P2l 2011-05-10 09:32:48

+0

我現在已經測試過了,它適用於我們。正如你所說,它很醜,但我看不到另一種方法。謝謝你的幫助。 – P2l 2011-05-12 12:55:49

相關問題