Улучшение имен свойств навигации при обратном проектировании базы данных


я использую 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. Мне трудно из-за отсутствия возможностей intellisense и отладки для внесения этих изменений.


объединение имен свойств (третий в списке) наверное простое решение для реализации.

как изменить создание навигационных свойств в Entity.tt и Mapping.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   51   2012-10-17 18:56:02

4 ответа:

там несколько вещей, которые вам нужно изменить внутри файла .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; }

теперь код, который вы можете задать. У меня есть добавил 2 функции CodeStringGenerator класс в файле T4. Тот, который строит имя_свойства получают NavigationProperty. а другой генерирует код для свойства, получая свойство 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 и передача этого в метод escape.

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

Я надеюсь, что это поможет, и вы всегда можете отладить

основываясь на ответе BikeMrown, мы можем добавить Intellisense к свойствам, используя RelationshipName это установлено в MSSQL:

MSSQL relationships

редактировать model.tt в вашем VS проекте, и изменить это:

[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)#>
<#
        }
    }

Теперь, когда вы начинаете вводить имя свойства, вы получите подсказку, как это: Intellisense tooltip

вероятно, стоит отметить, что если вы измените свою модель БД, свойства могут найти сами указывая на разные поля БД, потому что EF генерирует имена свойств навигации на основе алфавитного приоритета их соответствующего имени Поля БД!

нашел этот вопрос / ответ очень полезным. Однако я не хотел делать так много, как ответ Рикко. Мне просто нужно было найти имя столбца, участвующего в NavigationProperty, и не видел, как получить это в любом из образцов (по крайней мере, не без edmx для извлечения).

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

выбранный ответ является удивительным и заставил меня идти в правильном направлении наверняка. Но моя большая проблема с этим заключается в том, что он взял все мои уже работающие свойства навигации и добавил к ним имя базового типа, поэтому вы получите такие вещи, как следующее.

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