Why it is wrong to use the Asp.Net MVC MetaDataType Attribute
Posted on: 2014-04-03
First of all, let's talk about MetaDataType
attribute and why it exists. This one is from the System.ComponentModel.DataAnnotations.MetadataTypeAttribute
namespace. It roles is to add attribute to a class without having to modify this one. You can add this attribute that takes a single parameter to a class that will have all the attribute.
Some rules restraint the use of MetaDataType. It cannot be applied to a property and can be only be applied to a single class for each class type. This attribute cannot be inherited, so you cannot customize it. On the other side, this attribute can be applied to partial class
which is the main purpose of this attribute. This attribute will be respected by ASP.NET MVC but will not be read by Entity Framework. This is something interesting if you want to add additional validation or display attribute that does not affect the database. However, it is a good wake up call for any body who use this strategy for validation attributes to know why it should be validated only during the input of the value and not during the save.
Second, from the description we can come to the conclusion that it was implemented for the scenario of Entity Framework with the Database First Approach
. The model classes are automatically generated from the EDMX file
and even if they are in C# and that you can edit them, you should not. The reason is that if you change your database that you will have to generate the model again which will override your changes. This is why, Entity Framework creates those generated classes as partial class. This allow you to create your own class with the same name and define additional properties, fields and methods. But, what about existing properties that you would want to add attributes? This is where MetaDataType
come into play.
//Class from Entity Framework Database First
public partial class YourModelClass { public string YourProperty{get;set;} }
//Your Partial Class
[MetadataType(typeof(YourModelClassMetaData))] public partial class YourModelClass { }
//The class that add your attributes
public class YourModelClassMetaData { [Required] public object YourProperty{get;set;}; }
To illustrate the use of MetaDataType, let's see an example with Entity Framework. As you can see, the first class of the code snippet is the generated class. This one as all your properties that represent every columns of your table. This class should not be edited by your since the file can be overridden by an automatic tool. This is why the second class come into play. It uses the partial
keyword to give your a leverage. You can add new stuffs without touching the generated file. The only constraint is to have the partial keyword and the same class name. This way, .Net can match the classes and merge them into a single class. In this example, we add the MetaDataType attribute that give the capability to a class to delegate the attribute association. This is what we want: to add attribute to existing properties. Having only partial class is not enough because you can add but not attribute because these one must be set over an existing property.
The last class in the code snippet is the one referenced by the MetadataType attribute of the second. It tells .Net Framework to go into this third class, to do reflection to get a property-property association and to move the attribute from the referenced class into the class that the MetadataType attribute has been set. This is why the property type does not matter. Only the name.
In another scenario, where you do not want to add attributes to class that come from Entity Framework, you could have directly into your model class the MetadataType attribute and uncluttered your model from any attribute by having them all in another class.
At the end, what is wrong with MetadataType? It does not seem to hurt anything? Well, this is where I have something that bother me. How come the model class are directly used in the view? This mean that you are using ViewBag, ViewData or something else to pass additional information to the view. This mean that your design is less testable since it relies on a static object mechanism. It is also not required and someone could omit it without breaking anything. It also means that you are splitting your model class into 3 files. One generated, one of yours and one with attributes. This generate a lot of noises just to by-pass a view model approach or to not use Entity Framework Code First (that can be used even if you are working with the database first).
I believe this approach is viable for small application that you know that will not be over 15 entities with not a lot of business logic. It is a tool in the middle of a lot of others. Nevertheless, this is far from being an approach that should be opted to be "the one" that must be used absolutely. This approach end often by having UIHint, mixed with Required, mixed with Display attribute. Some are back-end, some are front-ends, everything is mixed up in a huge class that seem not that huge because it is divided in three.