45

我的項目中有多個類(包括TPT)。每個POCO都有一個BaseClass,其中有一個GUID(稱爲GlobalKey)作爲主鍵。如何在代碼中首先刪除下劃線中的外鍵字段

首先我使用DataAnnotations來創建正確的外鍵。但是,我遇到了將對應的GUID與對象本身同步的問題。

現在我想只有一個虛擬導航屬性,以便數據庫中的GUID字段由NamingConvention創建。但字段名稱總是添加一個下劃線,後面跟着GlobalKey(這是正確的)。當我想刪除下劃線,我不想去通過我的所有波蘇斯流利的API來做到這一點的:

// Remove underscore from Navigation-Field  
modelBuilder.Entity<Person>() 
      .HasOptional(x => x.Address) 
      .WithMany() 
      .Map(a => a.MapKey("AddressGlobalKey")); 

任何想法要做到這一點對所有POCOS由於覆蓋慣例?

在此先感謝。

安德烈亞斯

+0

你不能這樣做 - 但。有一個[可定製的代碼優先開發功能的約定](http://msdn.microsoft.com/en-us/data/jj819164.aspx)正在爲EF 6.0工作,但現在 - 你必須處理這個你自己,手動... –

+0

我實際上使用EF6 alpha 2.是否有可能以這種方式使用它? –

+0

[查看此博客文章](http://blog.3d-logic.com/2013/03/24/my-first-encounter-with-custom-conventions-in-entity-framework-6/) - this同事似乎在做一些類似於你想要的事情。 –

回答

63

我終於找到了一個答案,通過編寫一個自定義約定。該慣例適用於EF 6.0 RC1(上週的代碼),所以我認爲在EF 6.0發佈後它可能會繼續工作。

使用此方法,標準EF約定標識獨立關聯(IAs),然後爲外鍵字段創建EdmProperty。然後這個約定出現並重命名外鍵字段。

/// <summary> 
/// Provides a convention for fixing the independent association (IA) foreign key column names. 
/// </summary> 
public class ForeignKeyNamingConvention : IStoreModelConvention<AssociationType> 
{ 

    public void Apply(AssociationType association, DbModel model) 
    { 
     // Identify a ForeignKey properties (including IAs) 
     if (association.IsForeignKey) 
     { 
      // rename FK columns 
      var constraint = association.Constraint; 
      if (DoPropertiesHaveDefaultNames(constraint.FromProperties, constraint.ToRole.Name, constraint.ToProperties)) 
      { 
       NormalizeForeignKeyProperties(constraint.FromProperties); 
      } 
      if (DoPropertiesHaveDefaultNames(constraint.ToProperties, constraint.FromRole.Name, constraint.FromProperties)) 
      { 
       NormalizeForeignKeyProperties(constraint.ToProperties); 
      } 
     } 
    } 

    private bool DoPropertiesHaveDefaultNames(ReadOnlyMetadataCollection<EdmProperty> properties, string roleName, ReadOnlyMetadataCollection<EdmProperty> otherEndProperties) 
    { 
     if (properties.Count != otherEndProperties.Count) 
     { 
      return false; 
     } 

     for (int i = 0; i < properties.Count; ++i) 
     { 
      if (!properties[i].Name.EndsWith("_" + otherEndProperties[i].Name)) 
      { 
       return false; 
      } 
     } 
     return true; 
    } 

    private void NormalizeForeignKeyProperties(ReadOnlyMetadataCollection<EdmProperty> properties) 
    { 
     for (int i = 0; i < properties.Count; ++i) 
     { 
      string defaultPropertyName = properties[i].Name; 
      int ichUnderscore = defaultPropertyName.IndexOf('_'); 
      if (ichUnderscore <= 0) 
      { 
       continue; 
      } 
      string navigationPropertyName = defaultPropertyName.Substring(0, ichUnderscore); 
      string targetKey = defaultPropertyName.Substring(ichUnderscore + 1); 

      string newPropertyName; 
      if (targetKey.StartsWith(navigationPropertyName)) 
      { 
       newPropertyName = targetKey; 
      } 
      else 
      { 
       newPropertyName = navigationPropertyName + targetKey; 
      } 
      properties[i].Name = newPropertyName; 
     } 
    } 

} 

注意該公約被添加到您的DbContextDbContext.OnModelCreating覆蓋,使用:

modelBuilder.Conventions.Add(new ForeignKeyNamingConvention()); 
+7

而不是最後添加約定,您可以在ForeignKeyIndexConvention之前添加它,以將命名約定應用於外鍵索引名稱。 'modelBuilder.Conventions.AddBefore (new ForeignKeyNamingConvention( ));' –

+0

這段代碼對我來說不太合適,因爲這個測試總是爲false:if(!properties [i] .Name.EndsWith(「_」+ otherEndProperties [i] .Name))事實上,Name本身已經包含下劃線。所以我跳過了整個測試,總是調用NormalizeForeignKeyProperties。 –

+0

這是故意的 - 如果屬性Name有下劃線,則DoPropertiesHaveDefaultNames()應該返回true。這聽起來像你正在發生的事情? 檢查的原因是,它不會覆蓋其他約定修復的外鍵名稱,也不會覆蓋配置時指定的外鍵名稱。 – crimbo

5

你可以做兩件事情之一:

  1. 的外鍵,即命名按照EF約定,如果你有虛擬Address,定義關鍵屬性爲AddressId

  2. 明確告訴EF要使用什麼。一種方法是使用Fluent API,就像你現在正在做的那樣。你也可以用數據說明,雖然:

    [ForeignKey("Address")] 
    public int? AddressGlobalKey { get; set; } 
    
    public virtual Address Address { get; set; } 
    

那是你唯一的選擇。

+0

我以前做過這件事,但我遇到了同步問題。所以我正在尋找一種在EF6中做到這一點的方法。 –

+0

你得到了什麼錯誤?這是非常基本的功能,應該仍然可以在EF6中使用(除非某些東西壞了,這對於預發行軟件來說當然是可能的)。 –

+0

當試圖調用SaveChanges()時,我得到了PK約束錯誤。即,在這種情況下,當我嘗試保存包含對地址對象的引用的人物對象之前創建和保存的地址對象時。導航屬性是正確的,而相應的Guid-Property不是(null或Guid.Empty),這通常沒有問題。 (繼續...) –

0

我發現鍵列自定義沒有被由ForeignKeyNamingConvention抓獲。做出這個改變來抓住他們。

private bool DoPropertiesHaveDefaultNames(ReadOnlyMetadataCollection<EdmProperty> properties, string roleName, ReadOnlyMetadataCollection<EdmProperty> otherEndProperties) 
{ 
    if (properties.Count == otherEndProperties.Count) 
    { 
     for (int i = 0; i < properties.Count; ++i) 
     { 
      if (properties[i].Name.EndsWith("_" + otherEndProperties[i].Name)) 
      { 
       return true; 
      } 
      else 
      { 
       var preferredNameProperty = 
        otherEndProperties[i] 
         .MetadataProperties 
         .SingleOrDefault(x => x.Name.Equals("PreferredName")); 

       if (null != preferredNameProperty) 
       { 
        if (properties[i].Name.EndsWith("_" + preferredNameProperty.Value)) 
        { 
         return true; 
        } 
       } 
      } 
     } 
    } 
    return false; 
} 
+0

我認爲如果在屬性集合中有多個列,則您已經引入了一個錯誤,因爲您在第一次成功時返回true –

0

我在將它與id命名約定的EntityNameId組合時遇到了問題。

使用以下約定確保Customer表具有CustomerId而不是簡單Id時。

modelBuilder.Properties() 
         .Where(p => p.Name == "Id") 
         .Configure(p => p.IsKey().HasColumnName(p.ClrPropertyInfo.ReflectedType == null ? "Id" : p.ClrPropertyInfo.ReflectedType.Name +"Id")); 

外鍵命名約定需要更改爲以下內容。

/// <summary> 
    /// Provides a convention for fixing the independent association (IA) foreign key column names. 
    /// </summary> 
    public class ForeignKeyNamingConvention : IStoreModelConvention<AssociationType> 
    { 
     public void Apply(AssociationType association, DbModel model) 
     { 
      // Identify ForeignKey properties (including IAs) 
      if (!association.IsForeignKey) return; 

      // rename FK columns 
      var constraint = association.Constraint; 
      if (DoPropertiesHaveDefaultNames(constraint.FromProperties, constraint.ToProperties)) 
      { 
       NormalizeForeignKeyProperties(constraint.FromProperties); 
      } 

      if (DoPropertiesHaveDefaultNames(constraint.ToProperties, constraint.FromProperties)) 
      { 
       NormalizeForeignKeyProperties(constraint.ToProperties); 
      } 
     } 

     private static bool DoPropertiesHaveDefaultNames(IReadOnlyList<EdmProperty> properties, IReadOnlyList<EdmProperty> otherEndProperties) 
     { 
      if (properties.Count != otherEndProperties.Count) 
      { 
       return false; 
      } 

      for (var i = 0; i < properties.Count; ++i) 
      { 
       if (properties[i].Name.Replace("_", "") != otherEndProperties[i].Name) 
       { 
        return false; 
       } 
      } 

      return true; 
     } 

     private void NormalizeForeignKeyProperties(ReadOnlyMetadataCollection<EdmProperty> properties) 
     { 
      for (var i = 0; i < properties.Count; ++i) 
      { 
       var underscoreIndex = properties[i].Name.IndexOf('_'); 
       if (underscoreIndex > 0) 
       { 
        properties[i].Name = properties[i].Name.Remove(underscoreIndex, 1); 
       }     
      } 
     } 
    } 
5

我知道這是有點老了,但這裏是我通過我的流利的配置(OnModelCreating)指定映射列的示例:

modelBuilder.Entity<Application>() 
      .HasOptional(c => c.Account) 
       .WithMany() 
       .Map(c => c.MapKey("AccountId")); 

希望這有助於

+1

爲什麼這是低調的? – ProfK

+0

@ProfK我沒有投票,但我猜測是因爲OP想要一個將被全面應用的約定。 – Casey

1

我有當字段類型關閉時也會出現同樣的問題。請仔細檢查字段的類型:例如:

public string StateId {get;set;} 

指向int爲State.Id類型的域對象。確保你的類型相同。

0

這些答案中的大多數都與獨立關聯(其中定義了「MyOtherTable」導航屬性,而不是「MyOtherTableId」)而不是外鍵關聯(其中都定義了這些關係)有關。

這很好,因爲問題是關於IA(它使用MapKey),但是當我用FKA搜索解決同一個問題時遇到了這個問題。由於其他人可能出於同樣的原因來到這裏,所以我想我會分享使用ForeignKeyDiscoveryConvention的解決方案。

https://stackoverflow.com/a/43809004/799936

相關問題