When using code generation (ORM), the model classes are
automatically created by the tools. We
usually try to edit the class and add annotations such as field requirements,
formatting and messages. The problem with this approach is that when we need to
add properties to the model class, we will need to re-generate the class and
all the data annotations will be wiped out which create extra work for us.
A quick way to address this challenge is to create a Metadata
or Buddy class that provides all the annotations that we need. We can then add
an attribute to a partial class with the same name as the class that was
generated to indicate that there is another class that provides the annotations
on its behalf. We can now take a look at an ASP.NET MVC
project which can be loaded from the source code link at the end of this
article.
Class Generated by Entity Framework (Database first approach)
We first create a simple
table with the following definition:
CREATE TABLE [dbo].[Car] (
[Id]
INT IDENTITY (1, 1) NOT NULL,
[Make]
NVARCHAR (50) NOT NULL,
[Model] NVARCHAR (50) NOT NULL,
[Trim]
NVARCHAR (50) NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
)
Note: This table is
created using the .mdf files under the app_data folder.
We now can use EF by adding an ADO.Net Entity Data Model to
create the model from our database which generates the following definition
(see Models/car.edmx file). Do not forget to compile after the table has been
imported.
|
namespace
og.samples.aspnet.MetaDataClass.Models
{
public partial class Car
{
public int Id { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public string Trim { get; set; }
}
}
|
As we can see, there are no annotations on this model. Since
this is a generated class, we would like to add our annotation class instead of
changing it. We need to do this before
we generate the views.
New Partial
and Metadata Classes
The steps to add our metadata class are the followings:
1)
Add a new class under the model folder
a.
Name it car.metadata.cs
2)
Create another class that contains all the data
annotations.
a.
This is not a partial class
b.
This is a sealed class as there is no need to
instantiate it
3)
Add a new partial class with the same name as
the ORM class
a.
Add the [MetadataType] attribute to the class
that was just created
b.
Set the type to the class that has the
annotations.
4)
Compile
The code should now look as follows:
namespace og.samples.aspnet.MetaDataClass.Models
{
/// <summary>
/// partial class definition to associate the ORM generated
class
/// NO NEED to add the properties here.
/// </summary>
[MetadataType(typeof(CarAnnotation))]
public partial class Car
{
}
/// <summary>
/// Buddy Class or Data Annotation Class
/// Add the properties here with the associated annotations
/// </summary>
internal sealed class CarAnnotation
{
[Required(ErrorMessage="{0}
is required")]
[MinLength(3,ErrorMessage="{0}
should have three or more letters")] //kia
public string Make { get; set; }
[Required(ErrorMessage = "{0}
is required")]
[MinLength(5)]
public string Model { get; set; }
}
}
We
are making Make and Model required. In addition, we are making the Make to have
a minimum of three characters.
Create
Controller and Views
We
now just need to add our controller and views by adding a controller item under
the controller folders and selecting the following properties:
- MVC 5 with controller with views, using Entity framework
- Enter the following settings and compile the project
Cars Create
View (see sample project)
Run
the application and select Run Demo under the Metadata classes section.
This
is how the view should look. Press Create for the validation to take place as
shown below.
With the above view, we can show that our data annotation validations are shown when the user does not meet the input requirements.
Conclusion
With this approach we can show that we can still enable the use of ORM tools to generate the models and continue to support our application specific data annotation requirements without the concern of losing any information.
Code Sample
Available at:
GitHub (see Dev Branch for latest changes)
Thanks.