Подтвердить что ты не робот

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

Я использую Entity Framework 5 с Visual Studio с инструментами Powerity Entity Framework 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_TechnicalContactTechnicalContact). Хотя это, вероятно, было бы решением с наибольшим контролем, это может быть сложнее интегрировать с существующими шаблонами.
  • Используйте имя свойства , соответствующее столбцу внешнего ключа: Сбросьте суффикс ID (TechnicalContactIDTechnicalContact)
  • Используйте конкатенацию имени свойства и существующего решения: Пример TechnicalContactIDProjects (коллекция) и TechnicalContactIDContact

К счастью, можно изменить шаблоны, включив их в проект.

Модификации должны быть сделаны для Entity.tt и Mapping.tt. Мне становится трудно из-за отсутствия возможностей intellisense и debug для внесения этих изменений.


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

Как изменить создание навигационных свойств в 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; }
}
4b9b3361

Ответ 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. Один, который создает свойствоName, получая 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)#>
<#

Надеюсь, это поможет, и вы всегда можете отладить функцию GetPropertyNameForNavigationProperty и немного поиграть с присвоением имени.

Ответ 2

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

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

Ответ 3

На основе ответа на BikeMrown мы можем добавить Intellisense к свойствам с помощью RelationshipName, установленного в MSSQL:

отношения MSSQL

Измените 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

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

Ответ 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);