Geeks With Blogs
Marcin Kasprzykowski`s blog

 

INTRODUCTION
 
In these days Microsoft developers tend to compose their Data Access Layers using LINQ2SQL or Entity Framework most of the times. The good ol’ ADO.NET was terrific in the past, but its days of glory are clearly over. Both of these frameworks offer great functionality and both of them can save up a lot of time. Assuming that you have a complete database prepared, generating business classes for that is just a matter of few clicks. And those classes are generated in a shipshape fashion – they implement INotifyPropertyChanging and INotifyPropertyChanged interfaces and they also expose several useful partial methods.  Sounds great, but it may also be the spring of some problems.
 
 
THE PROBLEM
 
OK, using those frameworks we have all business classes “for free”. But what if you would like to add some custom properties or methods to them? That’s no problem as all such classes are generated as partial classes. This means that you can develop additional “parts” of such class thus virtually extending it. By taking advantage of this feature you can easily add new elements (methods, properties, events, etc.) to generated class and you can also leverage existing partial methods. But what if you would like to decorate a generated property with some attribute?
 
There are many reasons why you may want to do that. For example you can develop some custom attribute and apply it to business object’s properties to inform ValidationBindings how to perform validation. You may also use such attribute for dynamic UI forms generation. Or you can use attributes to customize serialization.
 
All generated classes are saved in *.designer.cs files. This means that although it is possible to modify those classes and decorate them with any attribute, all changes will be lost when they are regenerated. Therefore another approach must be used.
 
 
SOLUTION 1
 

One possible solution for that problem is described in Steve Smith’s blog (http://stevesmithblog.com/blog/adding-attributes-to-generated-classes/). It relies on using MetadataType, which is used to decorate new class containing information about the generated one. 

This solution would work and is pretty succinct, but it doesn’t have any type or name validation. In other words, if you change the name or type of some property in your model and regenerate business classes, you won’t get any error saying that your metadata class contains some obsolete stuff.  

 

SOLUTION 2 

Other solution implies using TypeDescriptor class (http://msdn.microsoft.com/en-us/library/system.componentmodel.typedescriptor.aspx). It is quite a bit overhead though for such simple scenario.
 
 
SOLUTION 3
 
My solution leans on the usage of partial classes and interfaces. I will use LINQ2SQL in my example but things would work in the same way for EntityFramework.
 
Let’s assume we have generated Company class:
[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.Company")]
       public partial class Company : INotifyPropertyChanging, INotifyPropertyChanged
       {           
             private string _ID;
            
             ...
            
             [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ID", DbType="NVarChar(5) NOT NULL", CanBeNull=false, IsPrimaryKey=true)]
             public string ID
             {
                    get
                    {
                           return this._ID;
                    }
                    set
                    {
                           if ((this._ID != value))
                           {
                                  this.OnIDChanging(value);
                                  this.SendPropertyChanging();
                                  this._ID = value;
                                  this.SendPropertyChanged("ID");
                                  this.OnIDChanged();
                           }
                    }
             }
 
Our aim is to decorate ID property with some ‘WhateverAttribute’.
 
Since Company is a partial class, we can define another part (that will NOT change when regeneration occurs, because it will be declared in separate file):
public partial class Company
{
}
 
We cannot simply define ID property again, as we will get an error: Ambiguity between ‘Company.ID' and ‘Company.ID:
public partial class Company
{
        public int ID { get; set; }
}
 
What we can do is to define a new interface:
public interface ICompany
{
        ...
}
 
And put in it a requirement for ID property:
public interface ICompany
{
        string ID { get; set; }
}
 
It is now perfectly possible to decorate this property with any attribute, for example:
public interface ICompany
{
        [PropertyInformation]
        string ID { get; set; }
}
public class PropertyInformationAttribute:Attribute{...}
 
Now all we have to do is to make our base class to implement this interface:
public interface ICompany : IBusinessObject
{
        [PropertyInformation]
        string ID { get; set; }
}
 
And this is it. We have successfully decorated ID property with custom attribute.
 
 
Now I will show on simple example you how to use that feature in a dynamic way.
 
First let’s get a Company object:
Company c = new MainDBMLDataContext().Companies.First();
 
Not let’s get Type of interface that this object implements:
var interfaceType = c.GetType().GetInterfaces().
First(arg => arg.GetInterfaces().
Count(arg2 => arg2.UnderlyingSystemType == typeof(IBusinessObject)) > 0);
 
 interfaceType is equal to typeof(ICompany). In this example this is obvious that Company implements ICompany interface so we could do that in much simpler way, but if you would use EntityFramework, you could leverage this feature in the following way:
 
EntityObject c = <get this object from somewhere>
 
In this example all you know about this object is that it is part of your model. It could be Company, Employee, Car or whatever. Using the code snippet above, you can get interface this object implements.
 
Now we can finally get values of properties of that object and a collection of attributes assigned to them:
var properties = interfaceType.GetProperties();
var propValue = properties[0].GetValue(c, null);
var propAttributes = properties[0].GetCustomAttributes(false);
 
 
CONCLUSION
 
In this article I have shown that generated code is not an impenetrable black box. There are many ways of tweaking generated code. Partial classes (with partial methods) are amazingly powerful feature of C# which you can easily leverage to solve this problem.

 

REFERENCES 

  1. Josh Smith blog: http://stevesmithblog.com/blog/adding-attributes-to-generated-classes/
  2. MSDN library – Type Descriptor: http://msdn.microsoft.com/en-us/library/system.componentmodel.typedescriptor.aspx
  3. MSDN library – Partial classes and methods: http://msdn.microsoft.com/en-us/library/wa80x488.aspx
Posted on Monday, September 26, 2011 11:52 AM | Back to top


Comments on this post: How to add attributes to classes generated by LINQ2SQL or Entity Framework?

# re: How to add attributes to generated classes
Requesting Gravatar...
Fu.. brilliant!
Left by Vovka on Dec 08, 2011 3:51 AM

Your comment:
 (will show your gravatar)


Copyright © Martinez | Powered by: GeeksWithBlogs.net