web-dev-qa-db-ja.com

データベースをリバースエンジニアリングするときのナビゲーションプロパティ名を改善する

Entity Framework 5とVisual Studio with Entity Framework Power Tools Beta 2を使用して、適度なサイズのデータ​​ベース(約100テーブル)をリバースエンジニアリングしています。

残念ながら、ナビゲーションプロパティには意味のある名前がありません。たとえば、2つのテーブルがある場合:

CREATE TABLE Contacts (
    ContactID INT IDENTITY (1, 1) NOT NULL,
    ...
    CONSTRAINT PK_Contacts PRIMARY KEY CLUSTERED (ContactID ASC)
}

CREATE TABLE Projects (
    ProjectID INT IDENTITY (1, 1) NOT NULL,
    TechnicalContactID INT NOT NULL,
    SalesContactID INT NOT NULL,
    ...
    CONSTRAINT PK_Projects PRIMARY KEY CLUSTERED (ProjectID ASC),
    CONSTRAINT FK_Projects_TechnicalContact FOREIGN KEY (TechnicalContactID)
        REFERENCES Contacts (ContactID),
    CONSTRAINT FK_Projects_SalesContact FOREIGN KEY (SalesContactID)
        REFERENCES Contacts (ContactID),
    ...
}

これにより、次のようなクラスが生成されます。

public class Contact
{
     public Contact()
     {
          this.Projects = new List<Project>();
          this.Projects1 = new List<Project>();
     }
     public int ContactID { get; set; }
     // ...
     public virtual ICollection<Project> Projects { get; set; }
     public virtual ICollection<Project> Projects1 { get; set; }
}

public class Project
{
     public Project()
     {

     }
     public int ProjectID { get; set; }
     public int TechnicalContactID { get; set; }
     public int SalesContactID { get; set; }
     // ...
     public virtual Contact Contact { get; set; }
     public virtual Contact Contact1 { get; set; }
}

私はすべてこれよりも優れているいくつかの変種を見ます:

  • 外部キーの名を使用します:たとえば、最後のアンダースコア(FK_Projects_TechnicalContact-> TechnicalContact)以降。これはおそらく最も制御の行き届いたソリューションですが、既存のテンプレートと統合するのはより困難な場合があります。
  • 外部キー列に対応するプロパティ名を使用します。接尾辞IDTechnicalContactID-> TechnicalContact)を取り除きます
  • プロパティ名と既存のソリューションの連結を使用します:例TechnicalContactIDProjects(コレクション)とTechnicalContactIDContact

幸い、 テンプレートをプロジェクトに含めることでテンプレートを変更できます

Entity.ttおよびMapping.ttに変更を加える必要があります。これらの変更を行うためのインテリセンスとデバッグの可能性がないため、難しいと思います。


プロパティ名を連結する(上記のリストの3番目)は、おそらく実装する最も簡単なソリューションです。

Entity.ttおよびMapping.ttのナビゲーションプロパティの作成を変更して、次の結果を得る方法

public class Contact
{
     public Contact()
     {
          this.TechnicalContactIDProjects = new List<Project>();
          this.SalesContactIDProjects = new List<Project>();
     }
     public int ContactID { get; set; }
     // ...
     public virtual ICollection<Project> TechnicalContactIDProjects { get; set; }
     public virtual ICollection<Project> SalesContactIDProjects { get; set; }
}

public class Project
{
     public Project()
     {

     }
     public int ProjectID { get; set; }
     public int TechnicalContactID { get; set; }
     public int SalesContactID { get; set; }
     // ...
     public virtual Contact TechnicalContactIDContact { get; set; }
     public virtual Contact SalesContactIDContact { get; set; }
}
53
marapet

.ttファイル内で変更が必要なことがいくつかあります。私はあなたが提案した3番目のソリューションを使用することを選択しましたが、これはFK_CollectionName_RelationNameのようにフォーマットする必要があります。 '_'で分割し、配列の最後の文字列を使用します。 RelationNameをToEndMemberプロパティと共に使用して、プロパティ名を作成します。 FK_Projects_TechnicalContactは、

//Plularized because of EF. 
public virtual Contacts TechnicalContactContacts { get; set; }

そしてあなたのプロジェクトはこのようになります。

public virtual ICollection<Projects> SalesContactProjects { get;  set; }
public virtual ICollection<Projects> TechnicalContactProjects { get;  set; }

今、あなたが尋ねるかもしれないコード。 T4ファイルのCodeStringGeneratorクラスに2つの関数を追加しました。 NavigationPropertyを受け取ってpropertyNameを構築するもの。もう1つは、NavigationPropertyとプロパティの名前を受け取るプロパティのコードを生成します。

//CodeStringGenerator class
public string GetPropertyNameForNavigationProperty(NavigationProperty navigationProperty)
{
    var ForeignKeyName = navigationProperty.RelationshipType.Name.Split('_');
    var propertyName = ForeignKeyName[ForeignKeyName.Length-1] + navigationProperty.ToEndMember.Name;
    return propertyName;
}

public string NavigationProperty(NavigationProperty navigationProperty, string name)
{
    var endType = _typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType());
    return string.Format(
        CultureInfo.InvariantCulture,
        "{0} {1} {2} {{ {3}get; {4}set; }}",
        AccessibilityAndVirtual(Accessibility.ForProperty(navigationProperty)),
        navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? ("ICollection<" + endType + ">") : endType,
        name,
        _code.SpaceAfter(Accessibility.ForGetter(navigationProperty)),
        _code.SpaceAfter(Accessibility.ForSetter(navigationProperty)));
}

上記のコードをクラスに配置した場合でも、2つの部分を変更する必要があります。コンストラクター部分とナビゲーションプロパティ部分がエンティティーを構築している場所を見つける必要があります。コンストラクタ部分(60行目付近)では、メソッドGetPropertyNameForNavigationPropertyを呼び出してこれをエスケープメソッドに渡すことにより、既存のコードを置き換える必要があります。

      var propName = codeStringGenerator.GetPropertyNameForNavigationProperty(navigationProperty);
#>
      this.<#=code.Escape(propName)#> = new HashSet<<#=typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType())#>>();
<#

また、NavigationPropertiesパーツ(100行目付近)では、コードを次のコードに置き換える必要があります。

    var propName = codeStringGenerator.GetPropertyNameForNavigationProperty(navigationProperty);
#>
    <#=codeStringGenerator.NavigationProperty(navigationProperty, propName)#>
<#

これがお役に立てば幸いです。いつでもGetPropertyNameForNavigationProperty関数をデバッグして、プロパティの名前を少し変えてみてください。

47

BikeMrownの答えに基づいて、MSSQLで設定されているRelationshipNameを使用して、Intellisenseをプロパティに追加できます。

MSSQL relationships

VSプロジェクトのmodel.ttを編集し、これを変更します。

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
<#
            }
#>
    <#=codeStringGenerator.NavigationProperty(navigationProperty)#>
<#
        }
    }

これに:

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
<#
            }
#>
    /// <summary>
    /// RelationshipName: <#=code.Escape(navigationProperty.RelationshipType.Name)#>
    /// </summary>
    <#=codeStringGenerator.NavigationProperty(navigationProperty)#>
<#
        }
    }

プロパティ名の入力を開始すると、次のようなツールチップが表示されます。 Intellisense tooltip

EFはそれぞれのDBフィールド名のアルファベット順に基づいてナビゲーションプロパティ名を生成するため、DBモデルを変更した場合、プロパティが別のDBフィールドをポイントする可能性があることに注意してください。

この質問/回答は非常に役に立ちました。しかし、私は立光の答えほどやりたくありませんでした。 NavigationPropertyに関係する列名を見つける必要があるだけで、どのサンプルでもそれを取得する方法がわかりませんでした(少なくとも、そこからプルするedmxがない限り)。

<#
  var association = (AssociationType)navProperty.RelationshipType;
#>  //  <#= association.ReferentialConstraints.Single().ToProperties.Single().Name #>
3
BikeMrown

選ばれた答えは素晴らしく、私は確かに正しい方向に進みました。しかし、それに関する私の大きな問題は、すでに機能しているナビゲーションプロパティをすべて取り、それらに基本型名を追加したことです。そのため、次のような結果になります。

public virtual Need UnitNeed { get; set;}
public virtual ShiftEntered UnitShiftEntered {get; set;}`

そこで、.ttファイルへの提案された追加を掘り下げ、それらを少し変更して、重複する型の名前付けを削除し、少し整理しました。同じことを望んでいる誰かが他にいるはずだと思ったので、ここに私の解決策を投稿すると思いました。

public class CodeStringGenerator内で更新するコードは次のとおりです

public string GetPropertyNameForNavigationProperty(NavigationProperty navigationProperty, string entityname = "")
{
    var ForeignKeyName = navigationProperty.RelationshipType.Name.Split('_');
    var propertyName = "";

    if (ForeignKeyName[ForeignKeyName.Length-1] != entityname){
        var prepender = (ForeignKeyName[ForeignKeyName.Length-1].EndsWith(entityname)) ? ReplaceLastOccurrence(ForeignKeyName[ForeignKeyName.Length-1], entityname, "") :  ForeignKeyName[ForeignKeyName.Length-1];
        propertyName = prepender + navigationProperty.ToEndMember.Name;
    }
    else {
        propertyName = navigationProperty.ToEndMember.Name;
    }

    return propertyName;
}

public string NavigationProperty(NavigationProperty navigationProperty, string name)
{
    var endType = _typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType());

    var truname = name;

    if(navigationProperty.ToEndMember.RelationshipMultiplicity != RelationshipMultiplicity.Many){
        if(name.Split(endType.ToArray<char>()).Length > 1){
            truname = ReplaceLastOccurrence(name, endType, "");
        }
    }

    return string.Format(
        CultureInfo.InvariantCulture,
        "{0} {1} {2} {{ {3}get; {4}set; }}",
        AccessibilityAndVirtual(Accessibility.ForProperty(navigationProperty)),
        navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? ("ICollection<" + endType + ">") : endType,
        truname,
        _code.SpaceAfter(Accessibility.ForGetter(navigationProperty)),
        _code.SpaceAfter(Accessibility.ForSetter(navigationProperty)));
}

public static string ReplaceLastOccurrence(string Source, string Find, string Replace)
{
        int place = Source.LastIndexOf(Find);

        if(place == -1)
           return Source;

        string result = Source.Remove(place, Find.Length).Insert(place, Replace);
        return result;
}

モデル生成内で更新するコードは次のとおりです

これの両方のオカレンスを更新します。

var propName = codeStringGenerator.GetPropertyNameForNavigationProperty(navigationProperty)

これに

var propName = codeStringGenerator.GetPropertyNameForNavigationProperty(navigationProperty, entity.Name);
0
Dylan Hayes