在逆向工程数据库时改进导航属性名称

我使用entity framework5与Visual Studio与entity framework电动工具testing版2来逆向工程中等大小的数据库(~100表)。

不幸的是, 导航属性没有有意义的名字 。 例如,如果有两个表:

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 )。 虽然这可能是控制力最强的解决scheme,但与现有的模板集成起来可能更难。
  • 使用对应于外键列的属性名称 :剥离后缀IDTechnicalContactID – > TechnicalContact
  • 使用属性名称和现有解决scheme的连接 :示例TechnicalContactIDProjects (collection)和TechnicalContactIDContact

幸运的是, 可以通过将它们包含在项目中来修改模板 。

必须对Entity.ttMapping.tt 。 由于缺乏智能感知和debugging的可能性,我发现很难做出这些改变。


连接属性名称 (上面列表中的第三个)可能是最容易实现的解决scheme。

如何在Entity.ttMapping.tt更改导航属性的创build,以达到以下效果

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

在.tt文件中需要更改一些内容。 我select使用您build议的第三个解决scheme,但这需要格式化为FK_CollectionName_RelationName。 我用“_”分割它们并使用数组中的最后一个string。 我使用RelationName和ToEndMember属性来创build属性名称。 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; } 

现在你可能会问的代码。 我添加了2个函数到T4文件中的CodeStringGenerator类。 其中一个构build了接收NavigationProperty的propertyName。 另一个为属性接收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个部分。 您需要find构造函数部分和导航属性部分正在构build实体的地方。 在构造函数部分(第60行左右),您需要调用GetPropertyNameForNavigationProperty方法来replace现有的代码,并将其传递给escape方法。

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

在NavigationProperties部分(第100行左右),你也需要用下面的代码replace。

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

我希望这会有所帮助,您可以随时debuggingGetPropertyNameForNavigationProperty函数,并使用属性命名。

发现这个问题/答案非常有帮助。 但是,我不想像Rikko的回答那样做。 我只需要find涉及NavigationProperty的列名称,并没有看到如何得到任何样本(至less不是没有edmx拉)。

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

build立在BikeMrown的答案上,我们可以使用在MSSQL中设置的RelationshipName将属性添加到属性中:

MSSQL关系

在你的VS Project中编辑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)#> <# } } 

现在,当你开始input一个属性名称,你会得到一个像这样的工具提示: 智能感知工具提示

可能值得注意的是,如果您更改数据库模型,则属性可能会发现自己指向不同的数据库字段,因为EF会根据各自的数据库字段名称的字母顺序生成导航属性名称!

选定的答案是真棒,让我朝着正确的方向肯定。 但我的大问题是,它已经把我所有的工作导航属性,并附加到他们的基本types的名称,所以你最终会像下面的东西。

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

因此,我深入研究了.tt文件的build议内容,并对其进行了一些修改,以删除重复的types命名和清理工作。 我认为那里会有其他人想要同样的东西,所以我想我会在这里发布我的决议。

以下是在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);