In order to experience the new features of Entity Framework I created a new MVC 4 internet application. I connected it to an existing database and generated the Model classes with the dbContext generator.
By running the application I got some validation errors while editing a form. As example for a DateTime field the system was complaining if the date was inserted as "12/10/2012" instead of 2012-10-12 (as in the SQ Server notation). I tried to find the validation code in the project, but I could not find it anywhere in the generated code.
One of my model classes is the following:
public partial class Artist
{
public Artist()
{
}
public int Id { get; set; }
public string Name { get; set; }
public Nullable<System.DateTime> DateOfBirth { get; set; }
public virtual Countries Countries { get; set; }
}
How can I customize the validation errors if using the Database first approach? If I decor the models with my validation attributes, then they would be erased once the model classes are generated again.
Moreover in a "real world" project where the use of an existing database is mandatory, what would be the best approach with the development of the model classes? Extending the classes automatically generated by adding partial classes with the same name?
EDIT (Introduced part of the View):
#using (Html.BeginForm()) {
#Html.ValidationSummary(false)
<fieldset>
<legend>Movie</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.DateOfBirth)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.DateOfBirth)
#Html.ValidationMessageFor(model => model.DateOfBirth)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
Well, you can always use a metadata class
public partial class ArtistMetaData
{
[Required]
[StringLength(20)]
public string Name;//it can be a field instead of a property, must just have the same name and type than in your Model class
}
and a partial class
[MetadataType(typeof(ArtistMetaData)]
public partial class Artist {
}
Or (my preferred solution) you can use an external Validation library, like the excellent FluentValidation
You have a "basic" validation by default (which can be removed in the global.asax), checking : that the non nullables values... are not null (like a default Required attribute), and that values are of the right type.
Check your browser and machine culture settings and then the jquery validation cultures.
I tend to have this problems since I have the Spanish config, but mvc comes with the English dates and currency formats, etc. For example dd/mm/yyyy vs mm/dd/yyyy
You could also use a regular expression attribute to validate the field as you wish.
Related
I'm new to MVC, please help me out for this.
I have to manage the profile of Employee in my application.So i created 3 model
DetailPersonal(contain field- First Name,Middle Name, Last Name, DOB)
DetailAddress(contain field- AddressLine1,AddressLine2,City,State,etc)
DetailContact(contain field-Primary Mobile no.,Secondary Mobile No, Off. No. etc)
Can i create a single view for these models and do ajax posting.
I want to display these in tabs Like Personal || Address || Contact
The cleanest way would be to create a new class with three properties, and use that as your model.
Or, if you don't want to do that, you could use a list of objects:
#model List<object>
#{
var detailPersonal = Model[0] as DetailPersonal;
var detailAddress = Model[1] as DetailAddress;
var detailContact = Model[2] as DetailContact;
}
And pass all three from the controller:
View(new object[] {MyDetailPersonal, MyDetailAddress, MyDetailContact})
But it could be more prone to error.
This situation is very common where you have defined classes that need to be combined before being presented to the user.
Typically, this is handled by using a View Model to consolidate separate classes into something that can be modified by the end user. Translating between your entity objects and your view models can be done in the constructor for a simple application.
I would look at this:
What is ViewModel in MVC?
Also, if you want to introduce more sophistication, I would read up on the repository pattern, and also look up domain layer. Those two subjects deal with the situation you are facing.
Will the user submit the contents of all three models using a single form (a shared submit button)? I think that makes the most sense, unless there are circumstances that make AJAX necessary. If I'm right about that, here's what you would do.
Create a new model with three model-typed properties. Let's call it CustomerModel.
public class CustomerModel
{
[DisplayName("Address")]
public DetailAddress AddressDetails { get; set; }
[DisplayName("Contact information")]
public DetailContact ContactDetails { get; set; }
[DisplayName("Personal information")]
public DetailPersonal PersonalDetails { get; set; }
}
Then create the editor templates in the framework-defined folder. These contain the editable fields for each model.
~/Views/Shared/EditorTemplates/DetailAddress.cshtml
~/Views/Shared/EditorTemplates/DetailContact.cshtml
~/Views/Shared/EditorTemplates/DetailPersonal.cshtml
Then add the editors to your main form. Something like...
#using (Html.BeginForm())
{
<div id="addressDetailsTab">
#Html.LabelFor(model => model.AddressDetails)
#Html.EditorFor(model => model.AddressDetails)
</div>
<div id="contactDetailsTab">
#Html.LabelFor(model => model.ContactDetails)
#Html.EditorFor(model => model.ContactDetails)
</div>
<div id="personalDetailsTab">
#Html.LabelFor(model => model.PersonalDetails)
#Html.EditorFor(model => model.PersonalDetails)
</div>
<input type="submit" value="Submit" />
}
I am trying to create a form which allows the creation and editing of a hierarchy of entities using MVC.
The top level is project which can have 1 or many environments associated with it. The environments are predefined, eg Dev, Test and Prod.
For each environment that is added it is possible to add several interfaces.
So a user would enter the project information. Select which environments which are relevant and then for each environment section add several interfaces.
I've created 3 view models, project, environment and interface. Like so
public class ProjectViewModel
{
public int Id { get; set; }
public string ProjectTitle { get; set; }
public List<SelectListItem> EnvironmentChoices { get; set; }
public List<EnvironmentViewModel> EnvironmentModel { get; set; }
}
public class EnvironmentViewModel
{
public IList<InterfaceViewModel> Interfaces { get; set; }
public string Environment { get; set; }
}
public class InterfaceViewModel
{
public string InterfaceName { get; set; }
}
Then created 1 project template and 2 editor templates for the environment model and the interface model.Like so
<p>
#Html.LabelFor(x => x.ProjectTitle)
#Html.TextBoxFor(x => x.ProjectTitle)
#Html.ValidationMessageFor(x => x.ProjectTitle)
</p>
<p>
#Html.LabelFor(x => x.EnvironmentModel)
#for (int i = 0; i < Model.EnvironmentChoices.Count; i++)
{
#Html.CheckBoxFor(m => m.EnvironmentChoices[i].Selected, new { id = Model.EnvironmentChoices[i].Value })
#Html.HiddenFor(m => m.EnvironmentChoices[i].Value)
#Html.DisplayFor(m => m.EnvironmentChoices[i].Text)
}
</p>
<p>
#Html.EditorFor(x => x.EnvironmentModel)
</p>
for the environment template
#model EnvironmentViewModel
<fieldset style="margin-left: -10px;">
<legend>#Model.Environment</legend>
#Html.ActionLink("Add Interface", "AddInterface", Model, new { #class = "button icon-file-plus" })
#Html.EditorFor(x => x.Interfaces)
</fieldset>
for the interface template
#model InterfaceViewModel
<p>
#Html.LabelFor(x => x.InterfaceName)
#Html.TextBoxFor(x => x.InterfaceName)
#Html.ValidationMessageFor(x => x.InterfaceName)
</p>
What I am finding is that when I click the add button on the environment section. The controller only picks up the environment model and loses the project model context so cannot modify it to add the new interface model.
Am I going about this in the wrong way? If so, are there examples of best practice. If not, what am I doing wrong.
Thanks
Thanks to Stephen Muecke for pointing me in the right direction.
I found this example to be very close to what I was trying to achieve
http://jarrettmeyer.com/post/2995732471/nested-collection-models-in-asp-net-mvc-3
Basically it uses to MVC to dynamic render JavaScript which in turn will add a detail view model to the form which conforms to the MVC indexing convention. Therefore when the master model and sub models are posted back the whole thing is bound to one big hierarchical model.
I have a simple question. I'm implementing a CRUD for a n:m relationship (users / roles namely) using the SimpleMembershipProvider (which is awkward by its own nature).
I created a view class:
public class AssignedRoleData
{
public string RoleName { get; set; }
public bool Assigned { get; set; }
}
I added this to my user class:
public List<AssignedRoleData> AssignedRoles { get; set; }
This is my AssignedRoleData.cshtml:
#model NfseEasyWeb.Models.AssignedRoleData
#Html.HiddenFor(model => model.RoleName)
#Html.LabelFor(model => model.Assigned, Model.RoleName)
#Html.EditorFor(model => model.Assigned)
I want them to appear on a horizontal line.
<table>
<tr>
#foreach (var item in Model.AssignedRoles) {
<td>
<div class="editor-field">
#Html.Editor What goes here?
</div>
</td>
}
</tr>
</table>
I know I could just use
#Html.EditorFor(model => model.AssignedRoles)
And the framework would detect it's a collection and render as many AssignedRoleData.cshtml as necessary, but they appear in pure form in html, vertical, I want them to appear on a horizontal line.
This is what I wanted. Did it applying CSS to the editor template, now need to work on margin.
Thanks
The way to go in this case would be create the template for the single entity and use an EditorFor for the collection in the view, like this:
#Html.EditorFor(model => model.AssignedRoles)
The MVC framework figures it's a collection and renders as many templates as necessary (MVC can't override EditorTemplate name when used in EditorFor for child object). This should be the preferred way, instead of concocting for loops in the elements, or using Viewbag (I say this by experience and reading, it's better not to stray from the pattern).
As the rendering part, to make the checkboxes appear side by side, it's possible to use CSS:
<div class="checkbox-editor-field">
#Html.HiddenFor(model => model.RoleName)
#Html.LabelFor(model => model.Assigned, Model.RoleName)
#Html.EditorFor(model => model.Assigned)
</div>
And create a CSS class for this:
.checkbox-editor-field {
display:inline-block;
}
So that the div elements will be accomodated in a single block in the same line.
I am trying to abstract my view model from various types of views. The entire thing compiles without a issue but I am having issues with "reflecting" (formally known as unboxing) the data annotations.
I have an interface:
public interface IPerson
{
string FirstName { get;set;}
string LastName {get;set;}
}
And I have two class which implement the interface as such:
public class Employee : IPerson
{
[Required]
[Display(Description = "Employee First Name", Name = "Employee First Name")]
public string FirstName {get;set;}
[Required]
[Display(Description = "Employee Last Name", Name = "Employee Last Name")]
public string LastName {get;set;}
public int NumberOfYearsWithCompany {get;set;}
}
public class Client : IPerson
{
[Required]
[Display(Description = "Your first Name", Name = "Your first Name")]
public string FirstName {get;set;}
[Display(Description = "Your last Name", Name = "Your last Name")]
public string LastName {get;set;}
[Display(Description = "Company Name", Name = "What company do you work for?")]
public string CompanyName {get;set;}
}
Person Edit View: views/Person/Edit as such:
#model IPerson
<div class="clear paddingbottomxxsm">
<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>
<div class="clear paddingbottomxxsm">
<div class="editor-label">
#Html.LabelFor(model => model.LastName)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.LastName)
#Html.ValidationMessageFor(model => model.LastName)
</div>
</div>
Employee Edit View: views/Employee/Edit:
#model Employee
Html.RenderAction("Edit", "Person", new { person = Model });
<div class="clear paddingbottomxxsm">
<div class="editor-label">
#Html.LabelFor(model => model.CompanyName)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.CompanyName)
#Html.ValidationMessageFor(model => model.CompanyName)
</div>
</div>
where the PersonController is:
public ActionResult Edit(IPerson person)
{
return PartialView(person);
}
Everything compiles and renders fine. However, the data annotations are being lost.
So, Employee/Edit is coming out like:
FirstName [textfield]
LastName [textfield]
What Company do you work for? [textfield] Company Name is a required field
Is there anyway of unboxing those data annotations for the concrete class?
Side note
I tried explicitly casting the IPerson to Employee as such:
#model IPerson
#{
var employee = (Employee)Model;
}
<div class="clear paddingbottomxxsm">
<div class="editor-label">
#Html.LabelFor(model => employee.FirstName)
</div>
<div class="editor-field">
#Html.EditorFor(model => employee.FirstName)
#Html.ValidationMessageFor(model => employee.FirstName)
</div>
</div>
<div class="clear paddingbottomxxsm">
<div class="editor-label">
#Html.LabelFor(model => employee.LastName)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => employee.LastName)
#Html.ValidationMessageFor(model => employee.LastName)
</div>
</div>
Doing this made the first name required but didn't take the display properties from the label.
Update
After much discussion as to whether this is or is not unboxing, I have not yet found a simple solution of grabbing the data annotation from the (more basic) concrete class. It would really defeat the goal of simplicity to use reflection in view (or helper) to get at the data annotations of the concrete class.
We have a few views that essentially are the same but have slightly different required fields and display names. It would be REALLY convenient if I could just pass a view model into an interfaced view and it would figure out the required fields and display properties. If anyone has figured out a way to do this it would be greatly appreciated.
I had the same problem with the TextBoxFor helper not generating the correct markup validation.
The way I was able to solve it was to use the TextBox helper instead of the TextBoxFor helper.
Here is the partial snippet which worked for me
#model Interfaces.Models.EntryPage.ICustomerRegisterVM
<p>
#Html.ValidationMessageFor(model => model.Department)
#Html.TextBox(Html.NameFor(model => model.Department).ToString(), Model.Department)
</p>
As you can see I used the Html.NameFor helper to generate the correct name from the expression and then passed in the property. Using this approach MVC was able to successfully generate the correct unobtrusive validation markup for the concrete class which implemented the interface which is referenced as the viewmodel.
I have not tried this approach for LabelFor or other helpers. But I hope that the result would be the same.
Please note the Html.NameFor helper is available in MVC5
You're specifying a model (IPerson) which has no data attributes when you're calling the PersonController.Edit action. The default metadata provider will only pick up the data attributes defined explicitly on the specified type (in this case, IPerson) or those that are inherited. You can share a metadata class or interface or copy the data annotations attributes to the interface.
However, I think you may want to redesign how this works a bit (for example, calling RenderAction to include another view into the current view is a code smell).
I would create a partial view for Person. Then, you can create a partial view for each type of person (Client, etc.). You can then add any additional markup, and include your Person view by using #Html.Partial("Person", Model).
You might also want to use a base class Person instead of an interface, otherwise it'll get tricky overriding the data attributes for FirstName and LastName.
public abstract class Person
{
public virtual string FirstName {get;set;}
public virtual string LastName {get;set;}
}
public class Employee : Person
{
[Required]
[Display(Description = "Employee First Name", Name = "Employee First Name")]
public override string FirstName {get;set;}
[Required]
[Display(Description = "Employee Last Name", Name = "Employee Last Name")]
public override string LastName {get;set;}
public int NumberOfYearsWithCompany {get;set;}
}
public class Client : Person
{
[Required]
[Display(Description = "Your first Name", Name = "Your first Name")]
public override string FirstName {get;set;}
[Display(Description = "Your last Name", Name = "Your last Name")]
public override string LastName {get;set;}
[Display(Description = "Company Name", Name = "What company do you work for?")]
public string CompanyName {get;set;}
}
Views/Shared/Person.cshtml
#model Person
<div class="clear paddingbottomxxsm">
<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>
<div class="clear paddingbottomxxsm">
<div class="editor-label">
#Html.LabelFor(model => model.LastName)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.LastName)
#Html.ValidationMessageFor(model => model.LastName)
</div>
</div>
Views/Employees/Edit.cshtml
#model Employee
#Html.Partial("Person", Model);
<div class="clear paddingbottomxxsm">
<div class="editor-label">
#Html.LabelFor(model => model.CompanyName)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.CompanyName)
#Html.ValidationMessageFor(model => model.CompanyName)
</div>
</div>
Controllers/EmployeesController.cs
public class EmployeesController : Controller
{
public ActionResult Edit(int id)
{
var model = GetEmployee(id); // replace with your actual data access logic
return View(model);
}
}
I found this post struggling with the exact same issue.
(On a side note: I had already implemented my own DataAnnotationsModelMetadataProvider following this sample to be able to access custom attributes inside my editor templates:
http://weblogs.asp.net/seanmcalinden/archive/2010/06/11/custom-asp-net-mvc-2-modelmetadataprovider-for-using-custom-view-model-attributes.aspx. Ensure to not miss the step in application start if you want to use your own DataAnnotationsModelMetadataProvider for this problem)
So after almost giving up on getting this to work I decided to debug my CreateMetadata override to see what I could get hold of there. I also found this post:
Obtain containing object instance from ModelMetadataProvider in ASP.NET MVC
This in combination with some reflection of the DataAnnotationsModelMetadataProvider class lead me to the following solution:
public class MyModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(
IEnumerable<Attribute> attributes,
Type containerType,
Func<object> modelAccessor,
Type modelType,
string propertyName)
{
//If containerType is an interface, get the actual type and the attributes of the current property on that type.
if (containerType != null && containerType.IsInterface)
{
object target = modelAccessor.Target;
object container = target.GetType().GetField("container").GetValue(target);
containerType = container.GetType();
var propertyDescriptor = this.GetTypeDescriptor(containerType).GetProperties()[propertyName];
attributes = this.FilterAttributes(containerType, propertyDescriptor, Enumerable.Cast<Attribute>((IEnumerable)propertyDescriptor.Attributes));
}
var modelMetadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
//This single line is for the "sidenote" in my text above, remove if you don't use this:
attributes.OfType<MetadataAttribute>().ToList().ForEach(x => x.Process(modelMetadata));
return modelMetadata;
}
}
So now I can have an EditorTemplate that has an interface type as the model and then use different implementations of it to be able to have different field names and validation rules via data annotations. I'm using this for a form that takes three different addresses; home address, work address and invoice address. The user interface for these groups of inputs are exactly the same but the validation rules differ.
This is of course a bit of a convention based solution saying that this behaviour should always apply when the editor template model is an interface. If you have existing editor templates where the model is an interface type that it self has data annotations, this solution will of course break that. For my case we are just starting up our usage of MVC and for now this convention will work. It would be interesting to maybe send a combination of the attributes from the interface and the actual type to the base-implementation but I'll save that experiment for later.
Please also let me know if you're a reader that is aware of some serious flaw with this solution.
I am working on an app where I have a requirement to be able to load object properties at runtime from a database. The customer wants to be able to add attributes to the database and have them show up in the app. I am accomplishing this by giving my model a list of Field objects that contain a name, a type, and a value. This works well for displaying and editing project properties, but I'm having trouble with validation in the editor view. Thanks for your help.
I want to be able to do this in my Edit action:
[HttpPost]
public ActionResult Edit(Movie movie)
{
if (ModelState.IsValid)
{
db.Entry(movie).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
}
Normal view:
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Movie</legend>
#Html.HiddenFor(model => model.ID)
<div class="editor-label">
#Html.LabelFor(model => model.Title)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Title)
#Html.ValidationMessageFor(model => model.Title)
</div>
</fieldset>
}
What I need to do:
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Movie</legend>
#Html.HiddenFor(model => model.ID)
for(i = 1 to n) {
<div class="editor-label">
#Html.LabelFor(model => model.Fields[i].Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Fields[i].Value)
#Html.ValidationMessageFor(model => model.Fields[i].Value)
</div>
}
</fieldset>
}
Model:
public class Movie
{
public Movie()
{
this.Fields = new List<Field>();
}
public List<Movie> Movies { get; set; }
}
Field Class:
public class Field
{
public string Name { get; set; }
public Type Type { get; set; }
public object Value { get; set; }
}
I believe something like this should work:
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Movie</legend>
#Html.HiddenFor(model => model.ID)
#{int i = 0;}
#foreach(var field in model.Fields) {
var htmlFieldName = string.Format("Fields[{0}]", i);
<div class="editor-label">
<label for="#htmlFieldName">#field.Title</label>
</div>
<div class="editor-field">
#Html.EditorFor(model => field, null, htmlFieldName)
#Html.ValidationMessage(htmlFieldName)
</div>
}
</fieldset>
}
(Note that I made up how you're producing your label text, since using the actual value as the label didn't make sense to me).
The POST should end up with values like this:
ID=123
Fields[0]=Jaws
Fields[1]=VeggieTales
...
... and that should automatically bind to your Movie model, provided the model has, for example, a List<string> named Fields. If your model doesn't look like that, this should at least get you on the right track.
Update
In your comment, you explain that you are trying to produce an editor for an object. There are two major points of difficulty here:
MVC relies on the static type returned in the lambda expression you give to EditorFor to determine which kind of editor it should produce. To override this, you will need to provide a specific template name where my original suggestion shows you providing null:
#Html.EditorFor(model => field.Value, field.Type.Name, htmlFieldName + ".Value")
You'll probably need to tweak this to make it provide the right template name for types like Integer, but this should give you the general idea.
When posting back, there is no way for the server to know that Field[0] is an int, etc. You can either:
Provide hidden values to specify each type, and then use a custom model binder that can consume this information to build each Field based on the combined Type and Value.
Recreate the structure of the Movie object on the server side based on the Movie's ID, and then walk through each of it's Fields calling:
TryUpdateModel((dynamic)field, string.Format("Field[{0}]", i));
There are probably other options, but that's about all the time I'm willing to put into this today.