Associate attribute with code generated property in .net

牧云@^-^@ 提交于 2019-12-08 22:57:00

问题


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 TypeDescriptionProvider to provide dynamic metadata (lots and lots of work) - assuming that the consumer respects TypeDescriptor; 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:

  1. Create an interface that has this property declared in it, and decorate it with ValidateNonEmpty attribute.
  2. Create your own partial class with same name as the AutoGenerated class, and make this implement the interface that you just created.
  3. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!