Migliora i nomi delle proprietà di navigazione durante il reverse engineering di un database

Sto utilizzando Entity Framework 5 con Visual Studio con Power Tools di Entity Framework Beta 2 per eseguire il reverse engineering di database di dimensioni moderate (~ 100 tabelle).

Sfortunatamente, le proprietà di navigazione non hanno nomi significativi . Ad esempio, se ci sono due tabelle:

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

Questo genererà classi come questa:

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

Vedo diverse varianti che sarebbero tutte migliori di questo:

  • Usa il nome della chiave esterna : ad esempio, tutto dopo l’ultimo trattino basso ( FK_Projects_TechnicalContact -> TechnicalContact ). Anche se questa probabilmente sarebbe la soluzione con il massimo controllo, questa potrebbe essere più difficile da integrare con i modelli esistenti.
  • Utilizzare il nome della proprietà corrispondente alla colonna chiave esterna: rimuovere l’ ID suffisso ( TechnicalContactID -> TechnicalContact )
  • Utilizzare la concatenazione del nome della proprietà e la soluzione esistente : Esempio TechnicalContactIDProjects (raccolta) e TechnicalContactIDContact

Fortunatamente, è ansible modificare i modelli includendoli nel progetto .

Le modifiche dovrebbero essere apportate a Entity.tt e Mapping.tt . Trovo difficile a causa della mancanza di intellisense e possibilità di debug per apportare tali modifiche.


Concatenare i nomi di proprietà (terzo nell’elenco sopra) è probabilmente la soluzione più semplice da implementare.

Come modificare la creazione di proprietà di navigazione in Entity.tt e Mapping.tt per ottenere il seguente risultato :

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

    Ci sono alcune cose che devi modificare all’interno del file .tt. Ho scelto di utilizzare la terza soluzione che hai suggerito, ma questo richiede di essere formattato come FK_CollectionName_RelationName. Li ho divisi con ‘_’ e uso l’ultima stringa nell’array. Io uso il RelationName con la proprietà ToEndMember per creare un nome di proprietà. Verrà generato FK_Projects_TechnicalContact

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

    e i tuoi progetti saranno così.

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

    Ora il codice che potresti chiedere. Ive ha aggiunto 2 funzioni alla class CodeStringGenerator nel file T4. Uno che costruisce il propertyName che riceve una proprietà NavigationProperty. e l’altra che genera il codice per la proprietà che riceve una proprietà Navigation e il nome per la proprietà.

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

    Se si inserisce il codice di cui sopra nella class è ancora necessario modificare 2 parti. È necessario trovare il luogo in cui la parte del costruttore e la parte della proprietà di navigazione vengono create dall’ quadro. Nella parte del costruttore (attorno alla riga 60) è necessario sostituire il codice esistente chiamando il metodo GetPropertyNameForNavigationProperty e passando questo nel metodo di escape.

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

    E nella parte NavigationProperties (attorno alla riga 100) è anche necessario sostituire il codice con quanto segue.

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

    Spero che questo aiuti e puoi sempre eseguire il debug della funzione GetPropertyNameForNavigationProperty e giocare un po 'con la denominazione della proprietà.

    Basandosi sulla risposta di BikeMrown, possiamo aggiungere Intellisense alle proprietà usando il RelationshipName impostato in MSSQL:

    Relazioni MSSQL

    Modifica model.tt nel tuo VS Project, e cambia questo:

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

    a questa:

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

    Ora quando inizi a digitare il nome di una proprietà, ottieni un suggerimento come questo: Suggerimento Intellisense

    Probabilmente vale la pena notare che se si modifica il modello DB, le proprietà potrebbero trovarsi a puntare a campi DB diversi poiché l'EF genera nomi di proprietà di navigazione in base alla precedenza alfabetica del rispettivo nome del campo DB!

    Trovato questa domanda / risposta molto utile. Tuttavia, non volevo fare tanto quanto la risposta di Rikko. Avevo solo bisogno di trovare il nome della colonna coinvolto in NavigationProperty e non vedevo come ottenerlo in nessuno dei campioni (almeno non senza un edmx da cui estrarre).

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

    La risposta selezionata è fantastica e mi ha fatto andare nella direzione giusta di sicuro. Ma il mio grosso problema è che ci sono volute tutte le mie proprietà di navigazione già funzionanti e ho aggiunto il nome del tipo di base a loro, così ti ritrovavi con cose come la seguente.

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

    Così ho scavato nelle aggiunte proposte al file .tt e li ho modificati un po ‘per rimuovere la denominazione dei tipi duplicati e ripulire un po’ le cose. Immagino che ci sia qualcun altro là fuori che vorrebbe la stessa cosa, quindi ho pensato di pubblicare la mia risoluzione qui.

    Ecco il codice da aggiornare all’interno della 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; } 

    ed ecco il codice da aggiornare all’interno della generazione del modello,

    aggiorna entrambe le occorrenze di questo:

     var propName = codeStringGenerator.GetPropertyNameForNavigationProperty(navigationProperty) 

    a questa

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