Verbessern Sie die Namen der Navigationseigenschaften beim Reverse Engineering einer database

Ich benutze Entity Framework 5 mit Visual Studio mit Entity Framework Power Tools Beta 2, um mittelgroße databaseen (~ 100 Tabellen) zu rekonstruieren.

Leider haben die Navigationseigenschaften keine aussagekräftigen Namen . Zum Beispiel, wenn es zwei Tabellen gibt:

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), ... } 

Dies wird classn wie folgt generieren:

 public class Contact { public Contact() { this.Projects = new List(); this.Projects1 = new List(); } public int ContactID { get; set; } // ... public virtual ICollection Projects { get; set; } public virtual ICollection 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; } } 

Ich sehe mehrere Varianten, die alle besser sind als diese:

  • Verwenden Sie den Namen des Fremdschlüssels : Zum Beispiel alles nach dem letzten Unterstrich ( FK_Projects_TechnicalContact -> TechnicalContact ). Obwohl dies wahrscheinlich die Lösung mit der größten Kontrolle wäre, könnte es schwieriger sein, sie in die vorhandenen Vorlagen zu integrieren.
  • Verwenden Sie den Eigenschaftsnamen , der der Fremdschlüsselspalte entspricht: Entfernen Sie die Suffix- ID ( TechnicalContactID -> TechnicalContact )
  • Verwenden Sie die Verkettung des Eigenschaftennamens und der vorhandenen Lösung : Beispiel TechnicalContactIDProjects (Sammlung) und TechnicalContactIDContact

Glücklicherweise ist es möglich, die Vorlagen zu ändern, indem Sie sie in das Projekt einfügen .

Die Änderungen müssten an Entity.tt und Mapping.tt . Ich finde es schwierig aufgrund der fehlenden Intellisense- und Debug-Möglichkeiten, diese Änderungen vorzunehmen.


Die Verkettung von Eigenschaftsnamen (dritter in der obigen Liste) ist wahrscheinlich die einfachste zu implementierende Lösung.

So ändern Sie die Erstellung von Navigationseigenschaften in Entity.tt und Mapping.tt , um das folgende Ergebnis zu erzielen :

 public class Contact { public Contact() { this.TechnicalContactIDProjects = new List(); this.SalesContactIDProjects = new List(); } public int ContactID { get; set; } // ... public virtual ICollection TechnicalContactIDProjects { get; set; } public virtual ICollection 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; } } 

    Es gibt ein paar Dinge, die Sie in der .tt-Datei ändern müssen. Ich wähle die dritte Lösung, die Sie vorgeschlagen haben, aber dies muss wie FK_CollectionName_RelationName formatiert sein. Ich teile sie mit ‘_’ auf und benutze die letzte Zeichenfolge im Array. Ich verwende den RelationName mit der ToEndMember-Eigenschaft, um einen Eigenschaftsnamen zu erstellen. FK_Projects_TechnicalContact führt zu

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

    und deine Projekte werden so sein.

     public virtual ICollection SalesContactProjects { get; set; } public virtual ICollection TechnicalContactProjects { get; set; } 

    Jetzt können Sie den Code fragen. CodeStringGenerator der CodeStringGenerator class in der T4-Datei zwei functionen hinzugefügt. Eine, die den propertyName erstellt und eine NavigationProperty empfängt. und der andere erzeugt den Code für die Eigenschaft, die eine Navigationseigenschaft erhält, und den Namen für die Eigenschaft.

     //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))); } 

    Wenn Sie den obigen Code in die class einfügen, müssen Sie noch 2 Teile ändern. Sie müssen den Ort finden, an dem der Konstruktorteil und der Navigationseigenschaftsteil der Entität aufgebaut werden. Im Konstruktorteil (um Zeile 60) müssen Sie den vorhandenen Code ersetzen, indem Sie die Methode GetPropertyNameForNavigationProperty und diese an die Escape-Methode übergeben.

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

    Und in dem NavigationProperties-Teil (um Zeile 100) müssen Sie den Code außerdem durch folgend ersetzen.

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

    Ich hoffe, dies hilft und Sie können immer die GetPropertyNameForNavigationProperty function debuggen und ein wenig mit der Benennung der Eigenschaft spielen.

    Aufbauend auf der Antwort von BikeMrown können wir Intellisense zu den Eigenschaften hinzufügen, indem wir den RelationshipName , der in MSSQL festgelegt ist:

    MSSQL-Beziehungen

    Bearbeiten Sie model.tt in Ihrem VS-Projekt und ändern Sie dies:

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

    zu diesem:

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

    Wenn Sie nun mit der Eingabe eines Eigenschaftsnamens beginnen, erhalten Sie einen Tooltipp wie diesen: Intellisense-Tooltip

    Beachten Sie, dass die Eigenschaften bei der Änderung Ihres DB-Modells möglicherweise auf verschiedene DB-Felder verweisen, da die EF Navigationseigenschaftsnamen basierend auf der alphabetischen Priorität ihres jeweiligen DB-Feldnamens generiert!

    Fanden Sie diese Frage / Antwort sehr hilfreich. Ich wollte jedoch nicht so viel tun wie Rikkos Antwort. Ich musste nur den Spaltennamen finden, der in der Navigationseigenschaft enthalten ist, und ich habe nicht gesehen, wie man das in irgendeinem der Samples bekommt (zumindest nicht ohne ein edmx, von dem man ziehen kann).

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

    Die ausgewählte Antwort ist großartig und hat mich in die richtige Richtung gebracht. Aber mein großes Problem damit ist, dass es alle meine bereits funktionierenden Navigationseigenschaften nahm und den Basistypnamen an sie anfügte, so dass Sie mit Dingen wie dem folgenden enden würden.

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

    Also habe ich die vorgeschlagenen Erweiterungen der .tt-Datei untersucht und sie ein wenig modifiziert, um doppelte Namen zu entfernen und die Dinge etwas aufzuräumen. Ich nehme an, da draußen muss jemand anderes sein, der das Gleiche möchte, also dachte ich mir, ich würde hier meinen Entschluss posten.

    Hier ist der Code, der innerhalb der public class CodeStringGenerator aktualisiert werden 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()).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; } 

    und hier ist der Code, der innerhalb der Modellgenerierung zu aktualisieren ist,

    Aktualisieren Sie beide Vorkommen davon:

     var propName = codeStringGenerator.GetPropertyNameForNavigationProperty(navigationProperty) 

    dazu

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