Using System.ComponentModel.DataAnnotations with Entity Framework 4.0 - asp.net-mvc

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

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

Same as above but with all the details, and it works
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;
}
}

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

Related

ASP.NET MVC Razor - All form fields are required?

I have a form that is generated by ASP.NET.
I have some required fields, and I am using the [Required] dataAnnotation for that.
However, the elements that don't have the [Required] DataAnnotation are also required according to my webpage. These are not required at all yet I cannot submit the form if they are empty.
I used scaffolding to make the pages, jquery validator is used (by default) for the validation.
Model class (some fields have been omitted for clarity)
public class Room
{
[Key]
public int ID { get; set; }
[Required(ErrorMessage = "Please enter the minimum (default) price for this room.")]
[DataType(DataType.Currency)]
[Display(Name = "Minimum price")]
public decimal MinPrice { get; set; }
[Display(Name = "Alternative price")]
[DataType(DataType.Currency)]
public decimal AltPrice { get; set; }
}
The code that creates the form fields in de .cshtml file:
<div class="form-group">
#Html.LabelFor(model => model.MinPrice, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.MinPrice)
#Html.ValidationMessageFor(model => model.MinPrice)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.AltPrice, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.AltPrice)
#Html.ValidationMessageFor(model => model.AltPrice)
</div>
</div>
The required field correctly displays the error message as defined (thus it reads the annotations).
The non required field displays a generic error message instead ("The Alternative price field is required.").
I've searched quite a lot, but everywhere it says that if the [Required] DataAnnotation is not there, it won't be required in the form.
Make the non-required fields nullable.
I was having the same problem. I had to go into my model and put ? marks in the int fields to make them null. The fields that were set as string were fine it was just the int fields that were causing the issue.

MVC 5 scaffolding not emitting bootstrap classes for basic EF derived data

First off, I'm a newcomer to MVC and ASP.NET so apologies if I'm missing something simple.
I'm working on a code first MVC 5 application, for the sake of brevity lets say I have two Models defined like this:
public class Platform
{
[Key]
public int Id { get; set; }
[Required(ErrorMessage = "You must select a manufacturer")]
[DisplayName("Manufacturer")]
public int ManufacturerId { get; set; }
public virtual Manufacturer Manufacturer { get; set; }
[Required(ErrorMessage="You must enter a name for the platform")]
[StringLength(50, ErrorMessage="Reduce length to 50 characters or less")]
public string Name { get; set; }
}
and
public class Manufacturer
{
[Key]
public int Id { get; set; }
[Required(ErrorMessage="You must enter a name for the manufacturer")]
[StringLength(50, ErrorMessage="Reduce length to 50 characters or less")]
public string Name { get; set; }
public virtual ICollection<Platform> Platforms { get; set; }
}
When creating 'MVC 5 Controller with views, using Entity Framework' scaffolding for these models all the CRUD actions are working correctly. The form elements on the other hand are unformatted, missing the form-control class that Bootstrap wants.
I can work around this per type by adding an EditorTemplate with something like #Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { #class = "form-control" }) in it, but surely the scaffolding for a Bootstrap MVC 5 project should be emitting valid code already? Looking at various MVC 5 tutorials they seem to be working correctly with proper styling, so I'm left somewhat confused as to what I'm doing wrong.
For reference, the output in the Create.cshtml file for each model (clipped to the important form elements for brevity) is:
<div class="form-group">
#Html.LabelFor(model => model.ManufacturerId, "ManufacturerId", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("ManufacturerId", String.Empty)
#Html.ValidationMessageFor(model => model.ManufacturerId)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Name, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
</div>
and
<div class="form-group">
#Html.LabelFor(model => model.Name, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
</div>
Why is the ManufacturerId being specified as the label text for the drop down when a DisplayName is set in the model?
Why is the drop down using DropDownList() instead of the strongly typed DropDownListFor()?
Thank you in advance.
It looks like MVC 5.1 will have a workaround for the form-control problem with Bootstrap 3, as stated here. It will be possible to pass styles for EditorFor.
Sounds like you did what I did - upgrade Bootstrap 2.x to 3.0 or 3.0.1. The Microsoft project templates are based on Bootstrap 2.x not 3.x. You can read about others bumping into on
GitHub.
I also got a reply from Rick Anderson of Microsoft who wrote several of the Tutorials on www.asp.net and he confirmed this...
Great catch. Actually, some of the images in my tutorial are from Beta and RC that are using bootstrap v2, not V3, so our images will differ. Look at the views for your account controller to see "form-control". You can download my complete project and compare with your project.
Regarding your other question, I ran into the same issue and think it may be a bug. Certain annotations appear to get ignored. Try adding a [ForeignKey()] annotation to Platform.ManufacturerId and see if it affects the field label.

How to turn off MVC form validation

I have a data-first set-up so my models are generated by the entity framework from my database and there is no default [Required] annotations. I have a simple table with three fields. One ID and two VARCHAR / text based fields.
No matter what I try, I cannot get the CRUD forms to stop validation. I disabled in the Web.config, I add [ValidateInput(false)] to the Create() method in the controller, but has no effect. I set the #Html.ValidationSummary to false,
This is the basic view:
#using (Html.BeginForm()) {
#Html.ValidationSummary(false)
<fieldset>
<legend>CallType</legend>
<div class="editor-label">
#Html.LabelFor(model => model.CALLTYPE)
</div>
<div class="editor-field">
#Html.TextBox("calltype", "", new { style = "width: 50px;" })
#Html.ValidationMessageFor(model => model.CALLTYPE)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.DESCRIPTION)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.DESCRIPTION)
#Html.ValidationMessageFor(model => model.DESCRIPTION)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
Model (generated by Framework):
public partial class CALLTYPES2
{
public int ID { get; set; }
public string CALLTYPE { get; set; }
public string DESCRIPTION { get; set; }
}
Even if I insert just one character in each field, it still says: "The Value 'x' is invalid"
(I leave the validation messages on so I can see what is going on.)
What am I supposed to do? And how would I validate these fields later on - can I just add [Required] to Model generated code? What if I regenerate the Model from the database?
Does this have something to do with the model state in the controller?
[HttpPost]
public ActionResult Create(CALLTYPES2 calltype)
{
if (ModelState.IsValid)
{
db.CALLTYPES2.Add(calltype);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(calltype);
}
Not sure what I am missing and the tutorials I have read do not shed much light. Thanks for your response and apologies for my ignorance.
UPDATE
Found my error - The object name "calltype" in the Method Create() is the same as the name/id of the form field "calltype". I guess the binder tries to bind the string "calltype" to the object "calltype". Renamed it to:
public ActionResult Create(CALLTYPES2 ctype)
Now it works in both the Edit and Create Windows. "ctype" is not clashing with "calltype".
You forgot to include the ID field in your form. You could include it as a hidden field:
#Html.HiddenFor(model => model.ID)
Now the value of the ID property will be sent to the server when the form is submitted and the default model binder should not complain.

Generating scaffolding template recursively for MVC

Consider following compostion relation between Child & Root classes-
public class Child
{
public string ChildProperty { get; set; }
}
public class Root
{
public Child child { get; set; }
public string RootProperty { get; set; }
}
Creating strongly typed view for Root class, does not include child property.
<fieldset>
<legend>Root</legend>
<div class="editor-label">
#Html.LabelFor(model => model.RootProperty)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.RootProperty)
#Html.ValidationMessageFor(model => model.RootProperty)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
How do I generate scaffolding template to include child properties as well?
Thank you for your help.
Generally you would need to create your own T4 templates that do just that:
https://stackoverflow.com/a/16072432/176877
But, I'm not certain you could actually loop through those child models; I'm fairly certain you would need to take things a step farther and actually generate a Custom Scaffold with your own PowerShell script that loops through those models and calls a new set of templates you create.
http://blog.stevensanderson.com/2011/04/07/mvcscaffolding-creating-custom-scaffolders/
In other words, you have a lot of work ahead of you if you want to do this.

Remove Prefix from ViewModel in ASP.NET MVC

I have a ViewModel class composed of several nested classes:
public class UserAccountViewModel : UserProfileViewModel
{
public UserAccountEmailViewModel UserAccountEmail { get; set; }
public UserAccountLocationViewModel UserAccountLocation { get; set; }
public UserAccountPasswordViewModel UserAccountPassword { get; set; }
}
The HTML rendered from this (pay attention to model.UserAccountEmail.Email):
<div class="editor-label">
#Html.LabelFor(model => model.UserAccountEmail.Email)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.UserAccountEmail.Email)
#Html.ValidationMessageFor(model => model.UserAccountEmail.Email)
</div>
Is this:
name="UserAccountEmail.Email"
I would prefer the name to simply be Email
Changing ViewData.TemplateInfo.HtmlFieldPrefix didn't help. Overloading the htmlFieldName in #Html.EditorFor isn't going to work because I still want the label and validation message to match the rendered HTML element (no overload for htmlFieldName in these functions).
I'd prefer to not create partials for this.
Edit:
Meh...using partials actually isn't too bad. It actually makes quite a lot of sense.
See my answer here about this design, it might make sense for this particular strategy
Many models to view with many partial views

Resources