在逆向工程数据库时改进导航属性名称
我使用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,但与现有的模板集成起来可能更难。 - 使用对应于外键列的属性名称 :剥离后缀
ID
(TechnicalContactID
– >TechnicalContact
) - 使用属性名称和现有解决scheme的连接 :示例
TechnicalContactIDProjects
(collection)和TechnicalContactIDContact
幸运的是, 可以通过将它们包含在项目中来修改模板 。
必须对Entity.tt
和Mapping.tt
。 由于缺乏智能感知和debugging的可能性,我发现很难做出这些改变。
连接属性名称 (上面列表中的第三个)可能是最容易实现的解决scheme。
如何在Entity.tt
和Mapping.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
将属性添加到属性中:
在你的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);
- 如何在EF CF中设置POCO的默认值?
- 我如何通过编程读取EF DbContext元数据?
- 如何使用DbContext和SetInitializer修复datetime2超出范围的转换错误?
- EF 4.1代码优先:如何使用包含和/或select方法命令导航属性?
- 如何使用entity framework来声明一对一的关系4 Code First(POCO)
- entity framework代码首先将Guid用作另一个标识列的标识
- 针对传统数据库的CodeFirst EF4.1 MVC – 多重性冲突
- 非静态方法需要一个目标。 entity framework5 Code First
- entity framework迁移停止检测POCO更新