18

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

GONeale
  • 26,302
  • 21
  • 106
  • 149

4 Answers4

23

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.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Thanks Marc, I thought this might be the case. I've managed to iterate through the properties of my "MetadataType" declared class, at the point I wished to enquire on the attributes, and I simply compare the name of the "meta" property against the real property. – GONeale Jan 19 '09 at 06:14
  • It is not the same as inquisting the true attributes, I understand, but for what I need it looks like it will serve the purpose in this case. Which is great. – GONeale Jan 19 '09 at 06:14
  • Hopefully this makes sense. I can now see if a validation attribute was declared and work accordingly. I now just hope there is no overhead with me utilising the `MetadataType` attribute class rather than making my own that simply tells it what class to look at properties on. – GONeale Jan 19 '09 at 06:16
6

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.

Benaiah
  • 150
  • 1
  • 5
  • I think you would then need every single property from your base class also put into the interface, a tedious exercise if you have many generated classes and many properties. – secretwep Oct 29 '14 at 17:42
  • The link is (effectively) broken. It redirects to *[Geekswithblogs.net, the End of an Era](http://julian.farm/geekswithblogs/?url=/mtreadwell/archive/2004/06/06/6123.aspx)*. – Peter Mortensen Jan 25 '22 at 16:40
2

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; }
}
1

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; }
    }
} 
Sten Petrov
  • 10,943
  • 1
  • 41
  • 61