提高导航属性的名称时,逆向工程数据库(Improve navigation property nam

2019-06-18 13:41发布

我使用实体框架5与Visual Studio与实体框架电动工具Beta 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 )。 虽然这很可能是最控制的解决方案,这可能是更难以与现有的模板整合。
  • 使用对应的外键列的属性名称 :剥下后缀IDTechnicalContactID - > TechnicalContact
  • 使用属性名的串联和现有的解决方案 :例TechnicalContactIDProjects (集合)和TechnicalContactIDContact

幸运的是, 可以通过包括他们在项目中修改模板 。

这些修改必须要进行Entity.ttMapping.tt 。 我觉得很难,由于缺乏IntelliSense和调试可能性,使这些变化。


级联属性名称 (在上面列表第三)可能是最容易实现的解决方案。

如何更改导航性能的创建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; }
}

Answer 1:

在那里,你需要一些东西给.TT文件中改变。 我选择使用你所建议的第三个解决方案,但这需要像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; }

现在,你的代码可能会问。 IVE加入2-起到将CodeStringGenerator类在T4文件。 其中一个构建propertyName的recieving一个NavigationProperty。 并且另一个产生用于所述属性recieving一个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)));
}

如果你把上面的代码中的类,你仍然需要改变两个部分。 你需要找到构造部分和导航属性部分正在建立实体的地方。 在构造函数中的一部分(约60行),你需要通过调用方法,以取代现有的代码GetPropertyNameForNavigationProperty ,并把该进遁法。

      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功能和发挥一点与物业的命名。



Answer 2:

在BikeMrown的答案的基础上,我们可以通过添加智能感知的性能RelationshipName是在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)#>
<#
        }
    }

现在,当您开始键入属性名称,你会得到这样的提示:

这也许值得一提的是,如果你改变你的数据库模型中,性能可能会发现自己基于各自的数据库字段名称的字母先后顺序中的不同的数据库字段指向,因为EF生成导航属性的名称!



Answer 3:

发现这个问题/答案非常有帮助。 但是,我不想尽可能多的做,因为Rikko的答案。 我只需要找到参与NavigationProperty列名,并没有看到如何让在任何样品的(至少在没有一个EDMX从拉)。

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


Answer 4:

所选择的答案是真棒,让我在确定正确的方向前进。 但我与它的大问题是,它占据了我所有已经工作导航属性和附加基本类型名称给他们,所以你最终与东西像下面这样。

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


文章来源: Improve navigation property names when reverse engineering a database