Шаблон EntityFramework t4 — XML-документация

У меня есть следующая проблема с моим файлом EDMX.

В нем я написал некоторую документацию для свойств, а также для объектов, но шаблон t4 моего EF 5 не генерирует эти значения.

Мой желаемый результат должен быть

public class Person
{
    /// <summary>
    /// Hello World Summary!!
    /// </summary>
    /// <remarks>
    /// Hello World Remarks!!
    /// </remarks>
    public string Name { get; set; }
}

Но я получаю только

public class Person
{
    public string Name { get; set; }
}

И если бы не это, зачем бы мне еще задавать свойства документации в файле edmx.


person Rand Random    schedule 11.12.2013    source источник


Ответы (3)


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

Вы можете изменить шаблон T4, чтобы сделать это за вас без особых хлопот. Чтобы начать работу, если вы откроете шаблон T4 для своих сущностей и найдете строку, которая выглядит следующим образом (для меня это строка 74):

    <#=codeStringGenerator.Property(edmProperty)#>

Затем измените его на что-то вроде этого:

<# if (edmProperty != null && edmProperty.Documentation != null) {#>
/// <summary>
/// <#= edmProperty.Documentation.Summary #>
/// </summary>
/// <remarks>
/// <#= edmProperty.Documentation.LongDescription #>
/// </remarks>
<# } #>
    <#=codeStringGenerator.Property(edmProperty)#>

Это создаст документацию для ваших свойств.

А также если перейти к строке, которая выглядит так (у меня примерно строка 28):

<#=codeStringGenerator.EntityClassOpening(entity)#>

Затем измените его на что-то вроде этого:

<# if (entity != null && entity.Documentation != null) {#>
/// <summary>
/// <#= entity.Documentation.Summary #>
/// </summary>
/// <remarks>
/// <#= entity.Documentation.LongDescription #>
/// </remarks>
<# } #>
<#=codeStringGenerator.EntityClassOpening(entity)#>

Это должно дать вам документацию вашего класса.

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

Мэтт

person Matt Whetton    schedule 11.12.2013
comment
Спасибо, очень полезно! Любые предложения о наилучшем способе обработки новых строк в документации? При описанном выше подходе комментарий (///) не повторяется для каждой новой строки в Summary\LongDescription. - person mutex; 12.06.2014
comment
На самом деле выполнение чего-то вроде: edmProperty.Documentation.Summary.Replace(\n, \r\n\t/// ) делает свое дело, с дополнительным бонусом, который предотвращает раздражающие предупреждения от Visual Studio о несогласованности концов строк. - person mutex; 12.06.2014

Вот мой взгляд на это.

В сгенерированном классе CodeStringGenerator в вашем файле .tt я добавил этот метод:

private string Comments(EdmMember member)
{
    string comments = member.Documentation != null
        ? (string.IsNullOrEmpty(member.Documentation.Summary)
            ? ""
            : @"
/// <summary>
/// " + member.Documentation.Summary.Replace("\n", Environment.NewLine + "    /// ") + @" 
/// </summary>
")
            +
            (string.IsNullOrEmpty(member.Documentation.LongDescription)
            ? ""
            : @"/// <remarks>
/// " + member.Documentation.LongDescription.Replace("\n", Environment.NewLine + "    /// ") + @"
/// </remarks>
")
        : "";
    return comments;
}

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

Затем я вызываю этот метод из метода, генерирующего простые свойства и свойства навигации:

public string Property(EdmProperty edmProperty)
{
    string comments = Comments(edmProperty);
    return string.Format(
        CultureInfo.InvariantCulture,
        @"{5}{0} {1} {2} {{ {3}get; {4}set; }}",
        Accessibility.ForProperty(edmProperty),
        _typeMapper.GetTypeName(edmProperty.TypeUsage),
        _code.Escape(edmProperty),
        _code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
        _code.SpaceAfter(Accessibility.ForSetter(edmProperty)),
        comments);
}

public string NavigationProperty(NavigationProperty navProp)
{
    string comments = Comments(navProp);
    var endType = _typeMapper.GetTypeName(navProp.ToEndMember.GetEntityType());
    return string.Format(
        CultureInfo.InvariantCulture,
        "{5}{0} {1} {2} {{ {3}get; {4}set; }}",
        AccessibilityAndVirtual(Accessibility.ForNavigationProperty(navProp)),
        navProp.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? ("ICollection<" + endType + ">") : endType,
        _code.Escape(navProp),
        _code.SpaceAfter(Accessibility.ForGetter(navProp)),
        _code.SpaceAfter(Accessibility.ForSetter(navProp)),
        comments);
}
person Tsahi Asher    schedule 03.08.2014

Я хотел бы добавить больше деталей к ответу Мэтта Мхеттона, потому что из-за моей текущей конфигурации проекта (CA и Style Cop) все ДОЛЖНО быть задокументировано.

С одной стороны, вам нужно будет добавить свойства документации в дизайнере для всего, что вы хотите задокументировать (классы, простые свойства, сложные свойства и свойства навигации).

С другой стороны, вам нужно открыть созданный вами WhateverModel.tt и отредактировать его, чтобы он содержал документацию, я объясню это шаг за шагом:

1.- Чтобы найти документацию класса, найдите:

<#=codeStringGenerator.UsingDirectives(inHeader: false)#>
<#=codeStringGenerator.EntityClassOpening(entity)#>

(строка 26 в моем случае) и поместите свои комментарии между этими двумя строками, например:

<#=codeStringGenerator.UsingDirectives(inHeader: false)#>
<# if (entity != null && entity.Documentation != null) {#>
/// <summary>
/// <#= entity.Documentation.Summary #>
/// </summary>
<# } #>
<#=codeStringGenerator.EntityClassOpening(entity)#>

2.- Для документации конструктора найдите public <#=code.Escape(entity)#>(), что создает конструктор, поэтому поместите свою документацию непосредственно перед этой строкой, например:

<# if (entity != null && entity.Documentation != null) {#>
    /// <summary>
    /// <#= entity.Documentation.Summary #>
    /// </summary>
<# } #>
    public <#=code.Escape(entity)#>()
    {

3.- Чтобы задокументировать каждое свойство, найдите foreach (var edmProperty in simpleProperties), этот вид будет генерировать код свойств, поэтому он должен выглядеть следующим образом:

foreach (var edmProperty in simpleProperties)
        {
#>
<# if (edmProperty != null && edmProperty.Documentation != null) {#>    
    /// <summary>
    /// <#= edmProperty.Documentation.Summary #>
    /// </summary>
<# } #>
    <#=codeStringGenerator.Property(edmProperty)#>
<#

        }

(ПРИМЕЧАНИЕ: это для простых свойств, но для сложных свойств будет то же самое...)

4.- Для точно таких же свойств навигации найдите foreach (var navigationProperty in navigationProperties) и добавьте свою документацию следующим образом:

foreach (var navigationProperty in navigationProperties)
        {
#>
<# if (navigationProperty != null && navigationProperty.Documentation != null) {#>
    /// <summary>
    /// <#= navigationProperty.Documentation.Summary #>
    /// </summary>
<# } #>
    <#=codeStringGenerator.NavigationProperty(navigationProperty)#>
<#

        }

4. И последнее, но не менее важное: если вам также нужна документация в вашем классе контекста, вы можете использовать это (ищите <# if (code.Escape(container) != null) {#> ):

<# if (code.Escape(container) != null) {#>
/// <summary>
/// Represents <#= code.Escape(container) #>
/// </summary>
<# } #>
<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext
{

<# if (code.Escape(container) != null) {#>
    /// <summary>
    /// Initializes a new instance of the <see cref="<#= code.Escape(container) #>"/> class.
    /// </summary>
<# } #>
    public <#=code.Escape(container)#>()
    : base("name=<#=container.Name#>")
    {
<#
if (!loader.IsLazyLoadingEnabled(container))
{
#>
        this.Configuration.LazyLoadingEnabled = false;
<#
}
#>
    }

    /// <summary>
    /// On Model Creating
    /// </summary>
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        throw new UnintentionalCodeFirstException();
    }

<#
    foreach (var entitySet in container.BaseEntitySets.OfType<EntitySet>())
    {
#>

   <# if (code.Escape(entitySet) != null) {#>
    /// <summary>
    /// Gets or sets the <#=code.Escape(entitySet)#>
    /// </summary>
    <# } #>

Я надеюсь, что это поможет, как ответ Мэтта Уеттона помог мне (спасибо, Мэтт).

person Juan    schedule 09.03.2016