58

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

残念ながら、ナビゲーション プロパティには意味のある名前がありません。たとえば、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) の後のすべて。これはおそらく最も制御性の高いソリューションですが、既存のテンプレートとの統合はより困難になる可能性があります。
  • 外部キー列に対応するプロパティ名を使用します: 接尾辞ID( TechnicalContactID--> TechnicalContact)を取り除きます。
  • プロパティ名と既存のソリューションの連結を使用: 例TechnicalContactIDProjects(コレクション) およびTechnicalContactIDContact

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

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


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

でナビゲーション プロパティの作成を変更し、次の結果Entity.ttMapping.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; }
}
4

4 に答える 4

51

.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; }

今あなたが尋ねるかもしれないコード。CodeStringGeneratorT4ファイルのクラスに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つの部分を変更する必要があります。コンストラクター部分とナビゲーションプロパティ部分がエンティティで構築されている場所を見つける必要があります。GetPropertyNameForNavigationPropertyコンストラクター部分(60行目あたり)では、メソッドを呼び出してこれをエスケープメソッドに渡すことにより、既存のコードを置き換える必要があります。

      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関数をデバッグして、プロパティの名前を少し試してみることができます。

于 2012-10-25T08:16:20.377 に答える
9

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

MSSQL 関係

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)#>
<#
        }
    }

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

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

于 2016-03-22T16:02:05.197 に答える
4

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

<#
  var association = (AssociationType)navProperty.RelationshipType;
#>  //  <#= association.ReferentialConstraints.Single().ToProperties.Single().Name #>
于 2014-11-21T15:58:49.843 に答える
1

選択された回答は素晴らしいものであり、確かに正しい方向に進むことができました. しかし、私の大きな問題は、既に動作しているすべてのナビゲーション プロパティを取得し、それらに基本型名を追加したことです。そのため、次のような結果になってしまいます。

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);
于 2016-10-25T15:45:32.137 に答える