问题
I wish to set an attribute on a public property in .NET, however I do not have access to the explicit property itself, as this has been code generated in another file.
I have this field:
public virtual string Name { get; set; }
I wish to set this:
[ValidateNonEmpty("Name is required", ExecutionOrder = 1)]
public virtual string Name { get; set; }
My class is marked as partial, but you cannot have partial properties. I thought I was on to something with the MetadataType class which is a new feature of Dynamic Data and DataAnnotations, but alas I feel it can only be used with Dynamic Data, is this true?
Citations: http://blogs.oosterkamp.nl/blogs/jowen/archive/2008/10/16/metadatatype-attribute.aspx http://blogs.msdn.com/davidebb/archive/2008/06/16/dynamic-data-and-the-associated-metadata-class.aspx
Is there any way I can set this attributes (even through web.config!) without touching the code generated class?
Thanks in advance, Graham
回答1:
This is a known nuisance; you simply can't add metadata to the generated members.
There are 6 options here (in order of increasing effort):
- if you own the attribute, make it possible to declare it against the class, for example:
[ValidateNonEmpty("Name", "Name is required", ExecutionOrder = 1)]- then add multiple attributes to the partial class definition - use a virtual / interface / etc method to query this, rather than via attributes
- sublass the generated type; override or re-declare the member, adding the metadata (really messy)
- use a custom
TypeDescriptionProviderto provide dynamic metadata (lots and lots of work) - assuming that the consumer respectsTypeDescriptor; most binding-related consumers do, but for example,Expression(used by many LINQ providers) doesn't - change the code-generator / write your own
- try to extend something like PostSharp to do the work (I haven't found a way to do this, but I've love to hear if you find a way!)
I usually have success with the first option, unless it is a system-defined attribute ([DisplayName], etc). If [ValidateNonEmpty] is defined by dynamic data, then you might not be able to do this.
回答2:
Since the generated class is a partial class, the following should work:
- Create an interface that has this property declared in it, and decorate it with ValidateNonEmpty attribute.
- Create your own partial class with same name as the AutoGenerated class, and make this implement the interface that you just created.
- The property should now be decorated with that attribute
For example:
// Decorate the properties with attributes as required
public interface IMyInterface
{
[ValidateNonEmpty("Name is required")]
string Name { get; set; }
}
// This is the partial class I created, that implements the interface
public partial class MyGeneratedClass : IMyInterface
{
}
// This is the auto-generated class
public partial class MyGeneratedClass
{
public virtual string Name { get; set; }
}
I got this idea from geekswithblogs.
回答3:
This is a great solution, but it didn't work for my problem. I'm using EF 6 with code-first generated classes from an existing database. One of the columns in a table is an IDENTITY with auto generated values. However, the generated partial class did not provide the [DatabaseGenerated(DatabaseGeneratedOption.Identity)] attribute needed to have the database generate the key. The result is the error "Cannot insert explicit value for identity column in table 'mytable' when IDENTITY_INSERT is set to OFF.". I tried your solution but it didn't work. But if I add the attribute to the original generated class, it does work. So I'm still trying to find a solution that does not require the modifying of the auto generated file.
Here is the code I tried using your solution:
public interface IMyTable
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
int ID { get; set; }
}
public partial class MyTable : IMyTable
{
}
original generated code:
[Table("MyTable")]
public partial class MyTable
{
[Key]
[Column(Order = 1)]
public int ID { get; set; }
}
回答4:
Another option is to wrap the properties inside non-generated properties in the same class. Not ideal because you may end up having double properties but if you can make your generator make protected properties it'd be a pretty good approach.
Just had to deal with this problem: Entity Framework generates classes, I want to serialize them to JSON with simpler names.
// GENERATED BY EF
public partial class ti_Users
{
public ti_Users()
{
this.ti_CardData = new HashSet<ti_CardData>();
this.ti_Orders = new HashSet<ti_Orders>();
}
protected int userId { get; set; }
protected string userName { get; set; }
protected string userEmail { get; set; }
protected string userPassHash { get; set; }
protected Nullable<System.DateTime> userLastLogin { get; set; }
protected string userLastIP { get; set; }
public virtual ICollection<ti_CardData> ti_CardData { get; set; }
public virtual ICollection<ti_Orders> ti_Orders { get; set; }
}
and the add-on class:
[JsonObject(memberSerialization: MemberSerialization.OptIn)]
public partial class ti_Users
{
[JsonProperty]
public int UserId
{
get { return this.userId; }
set { this.userId = value; }
}
[JsonProperty]
public string Name
{
get { return this.userName; }
set { this.userName = value; }
}
[JsonProperty]
public string Email
{
get { return this.userEmail; }
set { this.userEmail = value; }
}
[JsonProperty]
public string PassHash
{
get { return this.userPassHash; }
set { this.userPassHash = value; }
}
}
来源:https://stackoverflow.com/questions/456624/associate-attribute-with-code-generated-property-in-net