24
votes

Preface Feb 2015 If you are still using Entity Framework EDMX, please do yourself a favor and checkout using Entity Framework Code First instead. The difference is that your tables are created from your model classes, rather than in EDMX where your model classes are created with your tables. It's an all around easier solution, and the problem in this question doesn't even exist!

Getting Started with Entity Framework 6 Code First using MVC 5

I have an existing SQL database, and I am using ADO.NET Enity Data Model for the model. I am trying to build some CRUD features into my MVC application.

In all the tutorials I have found on the subject, they build the model from scratch and add the attributes to the model class. For example:

    [Required]
    [StringLength(10)]
    public string Name { get; set; }

However, the model classes are auto-generated, so I think changing them is a bad idea (and will get written over anyway if the the database model is refreshed).

How would I add validation attributes?

5
You should put the data annotations on the viewModel propertiesForty-Two
Reading about that now, thanks.Mason240

5 Answers

36
votes

You can create a partial class, separate from the EF generated class, to store the metadata in.

//Contact.cs - The original auto-generated file 
[System.ComponentModel.DataAnnotations.MetadataType(typeof(ContactMetadata))]
public partial class Contact
{
    public int ContactID { get; set; }
    public string ContactName { get; set; }
    public string ContactCell { get; set; }
}

//ContactMetadata.cs - New, seperate class

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
internal sealed class ContactMetadata
{
    [Required(ErrorMessage = "Name is required.")]
    [StringLength(5)]  
    public string ContactName;
}
19
votes

Mason240 answer works well, I will try to improve it : you could create a new ContactDataAnnotations.cs class with :

//ContactDataAnnotations.cs - A new file 
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

[MetadataType(typeof(ContactMetadata))]
public partial class Contact
{
    // No field here
}

internal sealed class ContactMetadata
{
    [Required(ErrorMessage = "Name is required.")]
    [StringLength(5)]  
    public string ContactName {get; set; }
}

This way, you can regenerate your Contact class through EF without touching DataAnnotations - and without warning, by the way.

4
votes

This has been answered correctly already, but I wanted to also add that I always find nesting the metadata seemed a bit cleaner to me, IMHO.

[MetadataType(typeof(ProductDescription.Metadata))]
public partial class ProductDescription
{
    sealed class Metadata
    {
        [Key]
        public long id { get; set; }
        [Display(Name = "Title")]
        public string title { get; set; }
        // ...
    }
}

I also noticed an added benefit of keeping the Metadata private to the class. The attribute will only work on the correct class, preventing a bug that may occur if you duplicate the class (to create a similar one). The bug can occur if you forget to change the class name in the attribute when renaming the duplicated class.

4
votes

I know this has been marked answered but I want to clear some stuffs up.

@SteveCav said: "This member is defined more than once". I had the same exact same error. Spent hours trying to figure out.

To finally correct it, you have to create a separate file class in the same Assembly(I think this is already mentioned here). But what I want to stress is that this class should be nested with a partial class representing the Inner Class.

And then you decorate that inner class with the Annotation class. Like this:

//ContactMap.cs - Present in the same namespace as Contact.cs
[System.ComponentModel.DataAnnotations.MetadataType(typeof(ContactMap))]
partial class Contact // Present in the ContactMap class. This represent the Inner Class
{
}

//ContactMap.cs - This represent the outer class

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
public class ContactMetadata
{
    [Required(ErrorMessage = "Name is required.")]
    [StringLength(5)]  
    public string ContactName;
}

Hope this is clearer or more understandable.

3
votes

There is a variation of the suggested answers here that allows you to use classes in different assemblies and namespaces. I haven't actually tested it with EF, but I am using this for Swagger codegen API model classes.

In a nutshell: inherit from the model class and add metadata on the inherited class. An added benefit is that with Swagger codegen you can use the API model directly without mapping and for initial forms you can use the protected default ctor.

[MetadataType(typeof(LocalAssemblyModelMetadata))]
public class LocalAssemblyModel : IO.Swagger.Model.OtherAssemblyModel 
{
    public LocalAssemblyModel() : base ()     { }
}



public sealed class LocalAssemblyModelMetadata
{
    [Required(ErrorMessage = "BaseclassProperty is mandatory.")]
    public string BaseclassProperty { get; set; }
}