Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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

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

And replace 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)));
}

You can create interface and declare attribute on interface.

partial class Person : IPerson {}

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

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

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

And replace 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

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.
  • 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; }

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.

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

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.

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top