在Entity Framework代码中添加自定义属性属性

时间:2022-11-27 14:26:01

Is there any way to add custom attributes to properties in EF generated code? The only thing I can see as a plausible solution would be to come up with a custom T4 template. However, because of the nature of the attribute it would be impossible to determine the correct attribute parameter per EF property.

有没有办法在EF生成的代码中为属性添加自定义属性?我唯一可以看到的是一个合理的解决方案是提出一个自定义的T4模板。但是,由于属性的性质,不可能为每个EF属性确定正确的属性参数。

6 个解决方案

#1


You can do this by specifying a metadata type that mirrors the properties and is used simply for attribution.

您可以通过指定镜像属性的元数据类型来执行此操作,并仅用于归因。

[MetadataType(typeof(Dinner_Validation))] 
public partial class Dinner 
{} 

public class Dinner_Validation 
{ 
    [Required] 
    public string Title { get; set; } 
}

Steve Smith blogs about it here.

史蒂夫史密斯在这里写博客。

Unfortunately the above approach is brittle to refactoring. Another option is to use the new POCO entities. These avoid compile-time code generation altogether as far as I can tell. I haven't used them yet so can't comment on any pitfalls or tradeoffs.

不幸的是,上述方法对于重构来说是脆弱的。另一种选择是使用新的POCO实体。就我所知,这些完全避免了编译时代码生成。我还没有使用它们,所以不能评论任何陷阱或权衡。

#2


You can add this to EDMX file, with Designer also:

您可以将此项添加到EDMX文件中,还可以使用Designer:

<Property Name="Nome" Type="String" Nullable="false" MaxLength="50" Unicode="true" FixedLength="false" >
            <Documentation>
              <Summary>[MyCustomAttribute]</Summary>
            </Documentation>
</Property>

And replace T4:

并取代T4:

void WriteProperty(CodeGenerationTools code, EdmProperty edmProperty)
{
    WriteProperty(Accessibility.ForProperty(edmProperty),
                  code.Escape(edmProperty.TypeUsage),
                  code.Escape(edmProperty),
                  code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
                  code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
}

With:

void WriteProperty(CodeGenerationTools code, EdmProperty edmProperty)
{
    if(edmProperty.Documentation != null && string.IsNullOrWhiteSpace(edmProperty.Documentation.Summary) == false)
    {
    #>
    <#=edmProperty.Documentation.Summary#>
<#+
    }
    WriteProperty(Accessibility.ForProperty(edmProperty),
                  code.Escape(edmProperty.TypeUsage),
                  code.Escape(edmProperty),
                  code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
                  code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
}

#3


You can create interface and declare attribute on interface.

您可以在界面上创建界面和声明属性。

partial class Person : IPerson {}

public interface IPerson
{
    [Required]
    string Name { get; set; }
}

#4


You can add this to EDMX file, with Designer also:

您可以将此项添加到EDMX文件中,还可以使用Designer:

<Property Name="Nome" Type="String" Nullable="false" MaxLength="50" Unicode="true" FixedLength="false" >
            <Documentation>
              <Summary>[MyCustomAttribute]</Summary>
            </Documentation>
</Property>

And replace T4:

并取代T4:

void WriteProperty(CodeGenerationTools code, EdmProperty edmProperty)
{
    WriteProperty(Accessibility.ForProperty(edmProperty),
                  code.Escape(edmProperty.TypeUsage),
                  code.Escape(edmProperty),
                  code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
                  code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
}

With:

void WriteProperty(CodeGenerationTools code, EdmProperty edmProperty)
{
    if(edmProperty.Documentation != null && string.IsNullOrWhiteSpace(edmProperty.Documentation.Summary) == false)
    {
    #>
    <#=edmProperty.Documentation.Summary#>
<#+
    }
    WriteProperty(Accessibility.ForProperty(edmProperty),
                  code.Escape(edmProperty.TypeUsage),
                  code.Escape(edmProperty),
                  code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
                  code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
}

And for Entity Framework 6, replace

对于Entity Framework 6,替换

public string Property(EdmProperty edmProperty)
{
    return string.Format(
        CultureInfo.InvariantCulture,
        "{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)));
}

with

public string Property(EdmProperty edmProperty)
{
    var description = String.Empty;
    bool isAttribute = false;

    if(edmProperty.Documentation != null &&
        string.IsNullOrWhiteSpace(edmProperty.Documentation.Summary) == false)
    {
        string summary = edmProperty.Documentation.Summary;
        if (!String.IsNullOrEmpty(summary) && summary.First() == '[' && summary.Last() == ']')
        {
            isAttribute = true;
        }

        if (isAttribute)
        {
            description = String.Format("\r\n\t{0}\r\n\t", summary);
        }
        else
        {
            description = String.Format("\r\n\t/// <summary>\r\n\t/// {0}\r\n\t/// </summary>\r\n\t", 
                summary);
        }

    }

    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)),
        description);
}

Warnings:

  • Namespaces need to be resolved absolutely.
  • 命名空间需要绝对解决。

  • Assumes attributes begin with '[' and end with ']' -- no other error checking
  • 假设属性以'['开头并以']'结尾 - 没有其他错误检查

  • If an opening and closing brace isn't found, the entity framework property summary is wrapped in an XML triple slash comment.
  • 如果未找到开始和结束括号,则实体框架属性摘要将包装在XML三重斜杠注释中。

  • Attempts to match default visual studio styling information (really just indents) which may or may not be the case for your project. This includes new lines.
  • 尝试匹配默认的视觉工作室样式信息(实际上只是缩进),这可能是您的项目的情况,也可能不是。这包括新行。

sample output:

/// <summary>
/// content type
/// </summary>
public System.Guid ContentType { get; set; }

[System.ComponentModel.DisplayName("Last Modified")]
public System.DateTime LastModified { get; set; }

#5


I don't believe you can. The generator declares all classes as partial allowing you to extend it, but it will not allow you to mark properties with custom attributes as it will simply generate over them. The one thing you can do is write your own entities.

我不相信你能。生成器将所有类声明为部分允许您扩展它,但它不允许您使用自定义属性标记属性,因为它将简单地生成它们。您可以做的一件事就是编写自己的实体。

#6


In Addition to BurnsBA's reply, To apply this to Navigation properties too, update NavigationProperty() as well:

除了BurnsBA的回复,要将此应用于导航属性,还要更新NavigationProperty():

public string NavigationProperty(NavigationProperty navProp)
{
    var description = String.Empty;
    if(navProp.Documentation != null && string.IsNullOrWhiteSpace(navProp.Documentation.Summary) == false)
    {
        string summary = navProp.Documentation.Summary;
        if (!String.IsNullOrEmpty(summary) && summary.First() == '[' && summary.Last() == ']')
        {
            description = String.Format("\r\n\t{0}\r\n\t", summary);
        }
        else
        {
            description = String.Format("\r\n\t/// <summary>\r\n\t/// {0}\r\n\t/// </summary>\r\n\t", summary);
        }
    }

    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)),
        description);
}

I use this to add [Newtonsoft.Json.JsonIgnore] to my properties.

我用它来将[Newtonsoft.Json.JsonIgnore]添加到我的属性中。

Note: You have to add these to <...>Model.tt and not <...>Model.Context.tt

注意:您必须将这些添加到<...> Model.tt而不是<...> Model.Context.tt

#1


You can do this by specifying a metadata type that mirrors the properties and is used simply for attribution.

您可以通过指定镜像属性的元数据类型来执行此操作,并仅用于归因。

[MetadataType(typeof(Dinner_Validation))] 
public partial class Dinner 
{} 

public class Dinner_Validation 
{ 
    [Required] 
    public string Title { get; set; } 
}

Steve Smith blogs about it here.

史蒂夫史密斯在这里写博客。

Unfortunately the above approach is brittle to refactoring. Another option is to use the new POCO entities. These avoid compile-time code generation altogether as far as I can tell. I haven't used them yet so can't comment on any pitfalls or tradeoffs.

不幸的是,上述方法对于重构来说是脆弱的。另一种选择是使用新的POCO实体。就我所知,这些完全避免了编译时代码生成。我还没有使用它们,所以不能评论任何陷阱或权衡。

#2


You can add this to EDMX file, with Designer also:

您可以将此项添加到EDMX文件中,还可以使用Designer:

<Property Name="Nome" Type="String" Nullable="false" MaxLength="50" Unicode="true" FixedLength="false" >
            <Documentation>
              <Summary>[MyCustomAttribute]</Summary>
            </Documentation>
</Property>

And replace T4:

并取代T4:

void WriteProperty(CodeGenerationTools code, EdmProperty edmProperty)
{
    WriteProperty(Accessibility.ForProperty(edmProperty),
                  code.Escape(edmProperty.TypeUsage),
                  code.Escape(edmProperty),
                  code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
                  code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
}

With:

void WriteProperty(CodeGenerationTools code, EdmProperty edmProperty)
{
    if(edmProperty.Documentation != null && string.IsNullOrWhiteSpace(edmProperty.Documentation.Summary) == false)
    {
    #>
    <#=edmProperty.Documentation.Summary#>
<#+
    }
    WriteProperty(Accessibility.ForProperty(edmProperty),
                  code.Escape(edmProperty.TypeUsage),
                  code.Escape(edmProperty),
                  code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
                  code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
}

#3


You can create interface and declare attribute on interface.

您可以在界面上创建界面和声明属性。

partial class Person : IPerson {}

public interface IPerson
{
    [Required]
    string Name { get; set; }
}

#4


You can add this to EDMX file, with Designer also:

您可以将此项添加到EDMX文件中,还可以使用Designer:

<Property Name="Nome" Type="String" Nullable="false" MaxLength="50" Unicode="true" FixedLength="false" >
            <Documentation>
              <Summary>[MyCustomAttribute]</Summary>
            </Documentation>
</Property>

And replace T4:

并取代T4:

void WriteProperty(CodeGenerationTools code, EdmProperty edmProperty)
{
    WriteProperty(Accessibility.ForProperty(edmProperty),
                  code.Escape(edmProperty.TypeUsage),
                  code.Escape(edmProperty),
                  code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
                  code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
}

With:

void WriteProperty(CodeGenerationTools code, EdmProperty edmProperty)
{
    if(edmProperty.Documentation != null && string.IsNullOrWhiteSpace(edmProperty.Documentation.Summary) == false)
    {
    #>
    <#=edmProperty.Documentation.Summary#>
<#+
    }
    WriteProperty(Accessibility.ForProperty(edmProperty),
                  code.Escape(edmProperty.TypeUsage),
                  code.Escape(edmProperty),
                  code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
                  code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
}

And for Entity Framework 6, replace

对于Entity Framework 6,替换

public string Property(EdmProperty edmProperty)
{
    return string.Format(
        CultureInfo.InvariantCulture,
        "{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)));
}

with

public string Property(EdmProperty edmProperty)
{
    var description = String.Empty;
    bool isAttribute = false;

    if(edmProperty.Documentation != null &&
        string.IsNullOrWhiteSpace(edmProperty.Documentation.Summary) == false)
    {
        string summary = edmProperty.Documentation.Summary;
        if (!String.IsNullOrEmpty(summary) && summary.First() == '[' && summary.Last() == ']')
        {
            isAttribute = true;
        }

        if (isAttribute)
        {
            description = String.Format("\r\n\t{0}\r\n\t", summary);
        }
        else
        {
            description = String.Format("\r\n\t/// <summary>\r\n\t/// {0}\r\n\t/// </summary>\r\n\t", 
                summary);
        }

    }

    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)),
        description);
}

Warnings:

  • Namespaces need to be resolved absolutely.
  • 命名空间需要绝对解决。

  • Assumes attributes begin with '[' and end with ']' -- no other error checking
  • 假设属性以'['开头并以']'结尾 - 没有其他错误检查

  • If an opening and closing brace isn't found, the entity framework property summary is wrapped in an XML triple slash comment.
  • 如果未找到开始和结束括号,则实体框架属性摘要将包装在XML三重斜杠注释中。

  • Attempts to match default visual studio styling information (really just indents) which may or may not be the case for your project. This includes new lines.
  • 尝试匹配默认的视觉工作室样式信息(实际上只是缩进),这可能是您的项目的情况,也可能不是。这包括新行。

sample output:

/// <summary>
/// content type
/// </summary>
public System.Guid ContentType { get; set; }

[System.ComponentModel.DisplayName("Last Modified")]
public System.DateTime LastModified { get; set; }

#5


I don't believe you can. The generator declares all classes as partial allowing you to extend it, but it will not allow you to mark properties with custom attributes as it will simply generate over them. The one thing you can do is write your own entities.

我不相信你能。生成器将所有类声明为部分允许您扩展它,但它不允许您使用自定义属性标记属性,因为它将简单地生成它们。您可以做的一件事就是编写自己的实体。

#6


In Addition to BurnsBA's reply, To apply this to Navigation properties too, update NavigationProperty() as well:

除了BurnsBA的回复,要将此应用于导航属性,还要更新NavigationProperty():

public string NavigationProperty(NavigationProperty navProp)
{
    var description = String.Empty;
    if(navProp.Documentation != null && string.IsNullOrWhiteSpace(navProp.Documentation.Summary) == false)
    {
        string summary = navProp.Documentation.Summary;
        if (!String.IsNullOrEmpty(summary) && summary.First() == '[' && summary.Last() == ']')
        {
            description = String.Format("\r\n\t{0}\r\n\t", summary);
        }
        else
        {
            description = String.Format("\r\n\t/// <summary>\r\n\t/// {0}\r\n\t/// </summary>\r\n\t", summary);
        }
    }

    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)),
        description);
}

I use this to add [Newtonsoft.Json.JsonIgnore] to my properties.

我用它来将[Newtonsoft.Json.JsonIgnore]添加到我的属性中。

Note: You have to add these to <...>Model.tt and not <...>Model.Context.tt

注意:您必须将这些添加到<...> Model.tt而不是<...> Model.Context.tt