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.
6 Answers
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.

- 300,895
- 165
- 679
- 742
-
I was just falling in love with EF when I suddenly realized that attributes on my entities was gonna be practically non-existent due to the way they are handled. Grrrr. – CatDadCode Nov 23 '10 at 07:17
-
5When reflecting, `assembly.GetType(typeof(Dinner).ToString().GetProperties()`, `property.Attributes` is null and `property.GetCustomAttributes(typeof(RequiredAttribute))` returns a zero-length array - should one reflect Dinner_Validation or is something else awry? – lukiffer Jun 24 '11 at 23:13
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)));
}

- 121
- 1
- 2
-
HI, how do I change that T4 Template? I created a Model1.tt file and that its all text black and seems like another syntax. – pungggi May 29 '13 at 06:30
-
**Update because this answer might be outdated:** For EF version 6.2.0 you must edit the methods `public string Property(EdmProperty edmProperty)` and `public string NavigationProperty(NavigationProperty navProp)`. The if-statement in from the answer is the same. – H. Pauwelyn May 09 '19 at 07:19
You can create interface and declare attribute on interface.
partial class Person : IPerson {}
public interface IPerson
{
[Required]
string Name { get; set; }
}

- 5,272
- 3
- 26
- 29
-
3As a side note for future readers, this does *not* work with asp.net-mvc's validation. – Erik Philips Mar 17 '14 at 22:29
-
If you implement ValidationAttribute.IsValid you can reflect into the object and get the interfaces. From there you get the members for that interface and look for any that have the custom attribute. I'm using the above method to filter out any properties that have a [Password] attribute from logging – spowser Oct 24 '19 at 05:17
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; }

- 4,347
- 27
- 39
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

- 3,630
- 3
- 33
- 55
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.
-
This may have been the case, but there are a few options nowadays. See my answer for info. – Drew Noakes Aug 18 '10 at 21:01