58

我正在使用 Entity Framework 5 和 Visual Studio 以及 Entity Framework Power Tools 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) 之后的所有内容。尽管这可能是控制能力最强的解决方案,但与现有模板集成可能会更加困难。
  • 使用外键列对应的属性名ID:去掉后缀(TechnicalContactID--> TechnicalContact
  • 使用属性名称和现有解决方案的连接:示例TechnicalContactIDProjects(集合)和TechnicalContactIDContact

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

必须对Entity.tt和进行修改Mapping.tt。由于缺乏智能感知和调试的可能性来进行这些更改,我发现这很困难。


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

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

4 回答 4

51

您需要在 .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; }

现在你可能会问的代码。CodeStringGenerator我在 T4 文件中的类中添加了 2 个函数。一种构建接收 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 个部分。您需要找到构建实体的构造函数部分和导航属性部分的位置。在构造函数部分(大约第 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功能并在属性的命名方面发挥一些作用。

于 2012-10-25T08:16:20.377 回答
9

基于 BikeMrown 的回答,我们可以使用RelationshipName在 MSSQL 中设置的属性将 Intellisense 添加到属性中:

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 会根据它们各自的数据库字段名称的字母优先级生成导航属性名称!

于 2016-03-22T16:02:05.197 回答
4

发现这个问题/答案非常有帮助。但是,我并不想像 Rikko 的回答那样做那么多。我只需要找到 NavigationProperty 中涉及的列名,并且没有看到如何在任何示例中获取它(至少没有 edmx 可以从中提取)。

<#
  var association = (AssociationType)navProperty.RelationshipType;
#>  //  <#= association.ReferentialConstraints.Single().ToProperties.Single().Name #>
于 2014-11-21T15:58:49.843 回答
1

选择的答案很棒,肯定让我朝着正确的方向前进。但我最大的问题是它占用了我所有已经工作的导航属性并将基本类型名称附加到它们,所以你最终会得到如下内容。

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);
于 2016-10-25T15:45:32.137 回答