52

I'm working with MVC3, and using Entity Framework 4.0 Entities as my model. So far, everything works great as far as using it as a model (all the crud operations/page generations work out of the box). I'm wondering, though, how do you get the same robust labels and validation information as when you generate a model manually?

Here's an example of what I mean. This is a class generated by the sample MVC3 project:

public class LogOnModel
{
    [Required]
    [Display(Name = "User name")]
    public string UserName { get; set; }

    [Required]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    [Display(Name = "Remember me?")]
    public bool RememberMe { get; set; }
}

With the example above, you can specify what gets rendered in a label for the field (Display), and what type of field to use (Password). However, when I try to use the entity framework and push it to the view below, I see the automatically generated labels are just the field names, and not anything I want the user to see/have to read:

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>Person</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.FirstName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.FirstName)
            @Html.ValidationMessageFor(model => model.FirstName)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.MiddleName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.MiddleName)
            @Html.ValidationMessageFor(model => model.MiddleName)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.LastName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.LastName)
            @Html.ValidationMessageFor(model => model.LastName)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Birthdate)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Birthdate)
            @Html.ValidationMessageFor(model => model.Birthdate)
        </div>

        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>}

enter image description here

My question is: How do I add these extra decorations to the entities that are generated using EF4? Is there something besides System.ComponentModel.DataAnnotations that I should be using? I know entities get regenerated and it's probably not a good idea to add this to entities' code directly, but for some reason I can't think of a better approach than manually entering the label text in the view (lame, there's no reason to have to do that, this is MVC!). I want to keep it so that the application is dynamic enough to be able to have the correct display information for my model come through and keep an MVC approach. How do I do it?

tereško
  • 58,060
  • 25
  • 98
  • 150
Ryan Hayes
  • 5,290
  • 4
  • 42
  • 52
  • Related: http://stackoverflow.com/q/2999936/11912 – James Skemp Mar 27 '13 at 14:12
  • its good idea to nest the Metadata Class inside the partial class , but unfortunately Entity Framework will always overwrite it ... i am working on MVC5 and the first solution to put it in same folder but separate class is working fine – Mohamed_Essa Apr 15 '19 at 07:21
  • Something like this also works : #if !NET40 using System.ComponentModel.DataAnnotations; #endif /* your code here */ #if !NET40 [StringLength(256)] #endif /*MVC / DB model definitions*/ – Ivan Silkin Dec 03 '21 at 00:52

3 Answers3

83

I haven't done this for ASP.NET MVC (only for Silverlight) but I believe the same principles would apply. You can create a "metadata buddy class" as below, because the types generated by EF should be partial, thus you can add a bit more to them (like the MetadataTypeAttribute) and then you create this sibling class that holds the metadata.

It's kind of ugly, but should work. It goes something like this (assuming the EF entity is named "Person"):

[MetadataType(typeof(PersonMetadata))]
public partial class Person { 
  // Note this class has nothing in it.  It's just here to add the class-level attribute.
}

public class PersonMetadata {
  // Name the field the same as EF named the property - "FirstName" for example.
  // Also, the type needs to match.  Basically just redeclare it.
  // Note that this is a field.  I think it can be a property too, but fields definitely should work.

   [Required]
   [Display(Name = "First Name")]
  public string FirstName;
}
Austin Lamb
  • 3,116
  • 1
  • 22
  • 13
  • Huh, that's an interesting approach. I'll try it out. – Ryan Hayes Feb 06 '11 at 21:03
  • thanks for the sample. Do you know what the chance of being able to put validation attributed into model-first entity framework as an attribute of the entity property might be? – John Kaster Mar 30 '11 at 03:54
  • 1
    Excellent answer - but does anyone know how to integrate this into a t4 template? – Raithlin May 09 '12 at 10:53
  • @Raithlin: For the T4 template, look here: http://blogs.msdn.com/b/lgmorand/archive/2010/12/31/entity-framework-template-t4-and-data-annotations.aspx – Leniel Maccaferri Jun 05 '12 at 02:08
  • 1
    @Raithlin: You can also take advantage from this: http://t4metadatatemplate.codeplex.com/ – Leniel Maccaferri Jun 05 '12 at 02:17
  • 2
    You might have PersonMetadata replaced by an interface IPersonMetaData and have Person implement it so you would have a compilation error in case of a field with a wrong name – Kek Sep 03 '12 at 10:22
  • I was thinking few days ago if this would be possible using Database First approach and happily I found the answer here. I've to admit that I couldn't do it by myself because I didn't know that the statement "public string FirstName;" is the way to go in this context. – vcRobe Aug 20 '14 at 15:40
  • I have a question pertaining to this approach.if I add annotations to the public partial class Person,will they be removed when I update from the db? – Napstar Nov 07 '14 at 09:43
  • Usage like suggested causes compilation error in one tries to add object to entities context, bacause somehow it is not the same type anymore. Statement like this: Context.Persons.Add(person) is not possible. The error is something like: Cannot convert form type Person to type Person. Althoug they booth belong to same namespace. Why is that? Hot to come around that? – Anderson Dec 13 '14 at 16:27
  • Regarding my previous comment: Partial class that adds annotation to entity framework entity class must be defined in same project as entity framework model. It is not possible to add annotations (via partial class) in another project. @Austin Lamb: Could you please add that notion somewhere in your answer. I will not fiddle with your work. – Anderson Dec 13 '14 at 18:28
  • Just to verify: you are able to use `properties` instead of `fields`, if so desired, as you hinted in your code comment. – Nick Jul 27 '15 at 19:30
  • why if I use the metadata class the Validator.TryValidateObject always returns true? – cpiock Sep 19 '17 at 19:44
2

Same as above but with all the details, and it works

enter image description here

enter image description here

enter image description here

enter image description here

And Here is the Code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;

namespace Validate.Models
{
    [MetadataType(typeof(PersonMetadata))]
    public partial class Person
    {
        // Note this class has nothing in it.  It's just here to add the class-level attribute.
    }

    public class PersonMetadata
    {
        // Name the field the same as EF named the property - "FirstName" for example.
        // Also, the type needs to match.  Basically just redeclare it.
        // Note that this is a field.  I think it can be a property too, but fields definitely should work.

        [Required]
        [Display(Name = "Enter Your Name")]
        public string FirstName;
    }
}
Arun Prasad E S
  • 9,489
  • 8
  • 74
  • 87
2

Like Austin Lamb's answer, but instead, nesting the MetaData class within the entity class, thereby reducing the number of classes in your public namespace list, and eliminating the need to have a unique name for each metadata class.

using System.ComponentModel.DataAnnotations;

namespace Validate.Models
{
    [MetadataType(typeof(MetaData))]
    public partial class Person
    {
        public class MetaData
        {
            [Required]
            [Display(Name = "Enter Your Name")]
            public string FirstName;

            //...
        }
    }
}
Carter Medlin
  • 11,857
  • 5
  • 62
  • 68