プロジェクトに複数のクラス(TPTを含む)があります。各POCOにはBaseClass
があり、GUID
(GlobalKey
と呼ばれる)が主キーとしてあります。
最初にDataAnnotations
を使用して、正しい外部キーを作成しました。しかし、対応するGUID=オブジェクト自体との同期に問題があります。
ここで、データベース内のGUIDフィールドがNamingConvention
によって作成されるように、1つの仮想ナビゲーションプロパティのみが必要です。ただし、フィールド名には常にアンダースコアとその後に続くWord GlobalKey
(右)が追加されます。 。アンダースコアを削除したいときは、流fluentなAPIのすべてのPOCOを介してこれを実行したくありません。
// Remove underscore from Navigation-Field
modelBuilder.Entity<Person>()
.HasOptional(x => x.Address)
.WithMany()
.Map(a => a.MapKey("AddressGlobalKey"));
規則を上書きしてすべてのPOCOSに対してこれを行うためのアイデアはありますか?
前もって感謝します。
アンドレアス
私は最終的に、カスタムコンベンションを作成することで、これに対する答えを見つけました。この規則はEF 6.0 RC1(先週のコード)で機能するため、EF 6.0がリリースされた後も機能し続けると思います。
このアプローチでは、標準のEF規則は独立したアソシエーション(IA)を識別してから、外部キーフィールドの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;
}
}
}
以下を使用して、DbContext.OnModelCreating
オーバーライドのDbContext
にコンベンションが追加されることに注意してください。
modelBuilder.Conventions.Add(new ForeignKeyNamingConvention());
次の2つのいずれかを実行できます。
外部キーの命名ではEF規則に従います。つまり、仮想Address
がある場合は、キープロパティをAddressId
として定義します。
EFに何を使用するかを明示的に伝えます。これを行う1つの方法は、現在行っているFluent APIを使用することです。ただし、データ注釈を使用することもできます。
[ForeignKey("Address")]
public int? AddressGlobalKey { get; set; }
public virtual Address Address { get; set; }
それはあなたの唯一の選択肢です。
私はこれが少し古いことを知っていますが、ここに流throughな設定(OnModelCreating)を通してマッピング列を指定する方法のサンプルがあります:
modelBuilder.Entity<Application>()
.HasOptional(c => c.Account)
.WithMany()
.Map(c => c.MapKey("AccountId"));
お役に立てれば、
フィールドのタイプがオフの場合にも同じ問題が発生しました。フィールドのタイプを再確認します例:
public string StateId {get;set;}
state.Id型としてintを持つドメインオブジェクトを指します。タイプが同じであることを確認してください。
これらの回答のほとんどは、外部キーアソシエーション(両方が定義されている)ではなく、独立したアソシエーション(「MyOtherTable」ナビゲーションプロパティが定義されているが、「int MyOtherTableId」ではない)に関係しています。
質問はIA(MapKeyを使用)に関するものなので問題ありませんが、FKAで同じ問題の解決策を探すときにこの質問に出くわしました。他の人が同じ理由でここに来るかもしれないので、ForeignKeyDiscoveryConventionを使用するソリューションを共有すると思いました。
キー列のカスタマイズが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;
}
EntityNameIdのid命名規則と組み合わせると問題が発生しました。
次の規則を使用して、Customerテーブルが単にIdではなくCustomerIdを持つようにします。
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);
}
}
}
}