24

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?

3
  • 1
    You should put the data annotations on the viewModel properties Commented Dec 27, 2012 at 18:41
  • Reading about that now, thanks. Commented Dec 27, 2012 at 18:49
  • stackoverflow.com/a/16737247/900284 this show how to make it Commented Jul 4, 2015 at 13:49

5 Answers 5

37

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;
}
Sign up to request clarification or add additional context in comments.

7 Comments

Actually you should not try to modify the original auto-generated file , just create another file as the partial keyword will make it work like a charm.
The compiler produces warnings that say that ContactName "is never assigned to, and will always have its default value null"; is it safe to default properties like these?
It's giving me "This member is defined more than once" :(
I have noticed that once you have a Metadata class for an EF model, e.g. to add extra validation, you seem to lose any existing validation the EF model already provided. Is it correct that the Metadata acts as a replacement for attributes rather than extend the existing set of attributes?
@SteveCav I also got that error. You have probably have figured it out by now, but if you haven't, you need to have the metadata class in the same assembly as the model class with the same namespace.
|
19

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.

Comments

4

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.

Comments

4

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.

Comments

3

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.