51

プロジェクトに複数のクラス (TPT を含む) があります。各 POCO には がありBaseClass、主キーとしてGUID(と呼ばれる) があります。GlobalKey

最初にDataAnnotations、正しい外部キーを作成していました。しかし、その後、対応する GUID をオブジェクト自体と同期する際に問題が発生しました。

ここで、データベースの GUID フィールドが によって作成されるように、仮想ナビゲーション プロパティを 1 つだけ使用したいと考えていますNamingConvention。ただし、フィールド名には常にアンダースコアが追加され、その後に単語が続きますGlobalKey(これは正しいことです)。アンダースコアを削除したい場合、これを行うために流暢な API ですべての POCO を通過したくありません。

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

慣例を上書きして、すべてのPOCOSに対してこれを行うアイデアはありますか?

前もって感謝します。

アンドレアス

4

7 に答える 7

67

カスタム規則を作成することで、最終的にこれに対する答えを見つけました。この規則は 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以下を使用して、DbContext.OnModelCreatingオーバーライドでコンベンションが追加されることに注意してください。

modelBuilder.Conventions.Add(new ForeignKeyNamingConvention());
于 2013-08-15T01:59:32.263 に答える
6

次の 2 つのいずれかを行うことができます。

  1. 外部キーの命名では EF 規則に従います。つまり、 virtual がある場合はAddress、キー プロパティを次のように定義します。AddressId

  2. 何を使用するかを EF に明示的に伝えます。これを行う 1 つの方法は、現在行っているように Fluent API を使用することです。ただし、データ注釈を使用することもできます。

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

それがあなたの唯一の選択肢です。

于 2013-03-28T17:15:10.127 に答える
5

これは少し古いことは知っていますが、流暢な構成 (OnModelCreating) を使用してマッピング列を指定する方法のサンプルを次に示します。

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

お役に立てれば、

于 2015-03-10T13:29:58.783 に答える
1

フィールドのタイプがオフの場合にも同じ問題が発生しました。フィールドのタイプを再確認します例:

public string StateId {get;set;}

State.Id 型として int を持つドメイン オブジェクトを指します。タイプが同じであることを確認してください。

于 2015-05-05T03:58:53.510 に答える
0

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); 
                }                 
            } 
        } 
    }
于 2015-01-23T10:53:03.400 に答える