asp.net mvc3 dynamic validation and entity framework - asp.net-mvc-3

Thanks for any thoughts.
while I am working my way through some custom validationAttributes, I have come across a problem which should be simple, but has me stumped.
An authorized user will have a UserProfile which includes a key to the site they work in. This site is a record set within a database. 1 field in this site record set is a regular expression which denotes what would be a valid format for a field in a completely separate table. The data entered into this other table will be common to all registered users, but a particular field relates to the ID format used at their institution.
Is there a clean way I can dynamically add a regular expression validator to a property?
Thank you as always.

This is what I came up with, but keen to know if there are better solutions:
Naming conventions are to allow automapper to flatten the model (each StudyCentre has a many to 1 relationship with the RecordSystem (some systems share the patient indexing system)
Mapper.CreateMap<StudyCentre, ParticipantRegistration.StudyCentreViewData>();
As a nested class within the ViewModel for an indidual TrialParticipant
public StudyCentreViewData ViewData { get; set; }
public class StudyCentreViewData
{
public string Abbreviation { get; set; }
public string RecordSystemName { get; set; }
public string RecordSystemHospitalNoRegEx { get; set; }
public string RecordSystemNotationDescription { get; set; }
public IDictionary<string, object> HospitalNoRegEx()
{
return DynamicClientValidation.RegEx(errorMessage:String.Format("{0} must consist of {1}",
RecordSystemName,
RecordSystemNotationDescription),
regExPattern: RecordSystemHospitalNoRegEx);
}
}
The other properties (such as StudyCentre.Abbreviation are for the labels)
The function RegEx is simply:
public static class DynamicClientValidation
{
public static IDictionary<string, object> RegEx(string errorMessage, string regExPattern)
{
var returnVal = new Dictionary<string, object>(3);
returnVal.Add("data-val-regex", errorMessage);
returnVal.Add("data-val-regex-pattern", regExPattern);
returnVal.Add("data-val", "true");
return returnVal;
}
}
The Controller sets up the viewmodel like so:
model.ViewData = Mapper.Map<StudyCentre, ParticipantRegistration.StudyCentreViewData>(_studyCentre.GetCentreByUser(_currentUserName));
and in the view (LabelDetailsfor is a custom helper):
<div class="editor-label">
#Html.LabelDetailsFor(model => model.HospitalID,Model.ViewData.Abbreviation + " ID", Model.ViewData.RecordSystemName)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.HospitalID, Model.ViewData.HospitalNoRegEx())
#Html.ValidationMessageFor(model => model.HospitalID)
</div>

Related

MVC 3 / Entity Framework: Binding Collections

I have 2 models, employee and person:
public class Employee
{
[Key]
public int Id { get; set; }
public int? PersonId { get; set; }
[ForeignKey("PersonId")]
public virtual Person Person { get; set; }
}
public class Person
{
public IList<PhoneNumber> PhoneNumbers { get; set; }
public int Id { get; set; }
public string FName { get; set; }
public string LName { get; set; }
public Person()
{
PhoneNumbers = new List<PhoneNumber>
{
new PhoneNumber()
};
}
}
Editor Template for Phone:
#Html.TextBoxFor(x => x.Number)
#Html.DropDownListFor(m => m, new SelectList(Enum.GetNames(typeof (WebMVC.Core.Common.PhoneType))))
To reduce clutter, I removed the other (non-pertinent) properties.
The difficulty I am having is while in the Employee Create(), I can bind the person FName & LName, I cannot bind the PhoneNumbers collection.
I know about the 2008 Haack blog but I do not think it mirrors this situation.
Does anyone know a solution to bind the person phone numbers collection in the employee's Create()?
I'm not exactly sure if PhoneNumber is a custom class that you created, or one that is built into the framework. But if you're having problems with MVC3 mapping posted data to the Employee class like you specified, you might want to look at creating a custom binding. Keep in mind that if your editor template code is incorrect this wont really matter, so I would take a look at that using fiddler first.
Here are a few good sites to get you started, I found them all on SO at one point.
http://odetocode.com/blogs/scott/archive/2009/04/27/6-tips-for-asp-net-mvc-model-binding.aspx
http://odetocode.com/blogs/scott/archive/2009/05/05/iterating-on-an-asp-net-mvc-model-binder.aspx
http://www.singingeels.com/Articles/Model_Binders_in_ASPNET_MVC.aspx
Creating a custom binder gives you complete control over the way that MVC parses your posted model data and populates the object. There are 2 main functions that most people override, CreateModel and BindModel. BindModel is the function you will most likely want to override if this is the way you would like to go.
I don't know what the html from the editor template looks like, but to bind to a collection of custom types it should look something like this:
<input name="[0].Number">
<input name="[0].PhoneType">
<input name="[1].Number">
<input name="[1].PhoneType">
<input name="[2].Number">
<input name="[2].PhoneType">

MVC 3 IList<T> Model Properties NULL on POST

I'll let the code do the talking here, I have something like this:
class Problem
{
public string Title { get; set; }
public string Description { get; set; }
public virtual IList<Symptom> Symptoms { get; set; }
}
class Symptom
{
public string Comments { get; set; }
public virtual Category Category { get; set; }
}
class Category
{
public string Name { get; set; }
}
I have a modal that allows users to add a list of symptoms on my view. Each symptom being added produces an INPUT that looks like this (where N is the index):
<input type="text" name="Symptom[N].Name" value="#Model.Symptom[N].Name">
<input type="text" name="Symptom[N].Category" value="#Model.Symptom[N].Category">
Once I POST the data to my controller, the model contains a valid list of Symptom (if I add 3, my Product.Symptom list has 3 entities) and the [Comments] of each symptom has persisted, but the [Category] property of each is NULL. What am I doing wrong here? I've tried numerous things but I still end up with NULL as the [Category] for each.
I'm using Entity Framework 4.1 Code First with Fluent API developing in MVC 3 using Razor syntax.
Try this:
<input type="text"
name="Symptom[N].Category.Name"
value="#Model.Symptom[N].Category.Name">
What I think is happening is that it's trying to bind a string to a Category which is invalid. If you want to map the text to the Name property on the Category class, you will need to specify it one level deeper.

How to add images as details in a ASP MVC 3 Razor View using Entity Framework

For an ongoing project i have (amongst other classes) the following:
public class Page
{
[Key]
public int PageId { get; set; }
public string Name { get; set; } //eg. "AboutUs", "Location"
[Column(TypeName = "ntext")] //force Entity Framework to create a ntext column
public string Title { get; set; }
public string Subtitle { get; set; }
public string Content { get; set; }
//navigational properties
public virtual ObservableCollection<Image> Images{ get; set; } //one Page has many Images
public Page()
{
Images= new ObservableCollection<Image>();
}
}
I'm using Entity Framework code first approach in this ASP MVC 3 project (using Razor) and do not have any problem inserting and updating objects of this type.
BUT: how can i have a master detail view in which the detail part is composed by images only (see class definition).
So how is it possible to add an image, if the user doesn't want it to have it deleted and of course how to show all the images in a list?
Any hint is deeply appreciated!
Look at this post: Display image from database in asp mvc
If you are trying to render from the database, create a method in your controller to get the image and use that as your image source like in the example above. The only difference is yours will be contained in a
<ul>
#foreach(var image in Model.Images) {
<li><img src="#Url.Action("Show", "Image", new {id = image.Id})" /></li>
}
</ul>
Then your model would contain the Id's of the images, and it would be the job of the action method to retrieve the image.

Can AutoMapper map object to model property of the same type

I am attempting to improve my data flow between my MVC 3 Model and Views (mainly CRUD). I have taken the approach of using ViewModels and FormModels. My ViewModel contains everything it need to represent the view FormData, DropDownLists etc. The FormModel simply contains the FormData fields that are submitted by the form and are needed to update a record.
My question is can I use AutoMapper to map UserDto information onto my FormData field in my ViewModel?
Obviously my mapping below is only mapping between the two object and not an object to property but I have tried using the ‘.ForMember’ mapping options but they are again for object members not an object to an object member. I have also looked at Custom Type Convertors but not sure if this is the right way to go.
Mapper.CreateMap<UserDto, UserViewModel>();
Mapper.CreateMap<UserViewModel, UserDto>();
public class UserViewModel
{
public User FormData { get; set; }
// DropDownLists
// Other view specific data
}
public class UserFormModel
{
public int UserId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Age { get; set; }
[Required]
public string Email { get; set; }
}
Any help would be much appreciated.
You need to create the map to the FormData property type and then tell AutoMapper to use this map.
(The following will likely not compile; I'm in the process of recreating my work machine and am working from memory).
Mapper.CreateMap<UserDto, User>(); // set up property mapping
Mapper.CreateMap<UserDto, UserViewModel>()
.ForMember(vm => vm.FormData, map => map.MapFrom(dto => Mapper.Map<UserDto, User>(dto)));

How to validate ViewModels derived from an abstract class?

I have the following EditorTemplate
#model ESG.Web.Models.FileInfo // <-- Changed to BaseFileInfo
#{
ViewBag.Title = "FileInfoEditorTemplate";
}
<fieldset>
<table class="fileInfoEdit">
<tr>
<td>Base Directory:</td>
<td>#Html.EditorFor(model => model.Directory)</td>
<td>#Html.ValidationMessageFor(model => model.Directory)</td>
</tr>
<tr>
<td>Filename:</td>
<td>#Html.EditorFor(model => model.Filename)</td>
<td>#Html.ValidationMessageFor(model => model.Filename)</td>
</tr>
</table>
</fieldset>
which corresponds to this ViewModel
public class FileInfo
{
[Display(Name = "Directory")]
[Required(ErrorMessage="Please specify the base directory where the file is located")]
public string Directory { get; set; }
[Display(Name = "File Name")]
[Required(ErrorMessage = "Please specify the name of the file (Either a templated filename or the actual filename).")]
public string Filename { get; set; }
}
What I want to do is reuse the above EditorTemplate but customise the ErrorMessage based on the context the FileInfo class is used in. I can have a standard file name eg abc.txt or a 'templated' file name eg abc_DATE.txt where DATE will be replaced with some user specified date. I want an appropriate error message in each case. In essence, the only difference should be the Annotations. (I think this the key, but am not sure how to tackle this, thus my convoluted approach!)
I have tried creating an abstract Base view model and then deriving a standard file and templated FileInfo classes. I change the declaration on the current EditorTemplate to
`#model ESG.Web.Models.BaseFileInfo`
and use it like
#Html.EditorFor(model => model.VolalityFile, "FileInfoEditorTemplate")`
where model.VolalityFile is a TemplatedFileInfo. The values are correctly displayed on the Edit page, however, there is no client-side validation when fields are not correctly filled. My initial guess is that this has something to do with the abstract class definition (not having any annotations on the fields).
public abstract class BaseFileInfo
{
public abstract string Directory { get; set; }
public abstract string Filename { get; set; }
}
// eg of derived class
public class TemplatedFileInfo : BaseFileInfo
{
[Display(Name = "File Name")]
[Required(ErrorMessage = "Please specify the name of a templated file eg someFileName_DATE.csv")]
public override string Filename { get; set; }
[Display(Name = "Directory")]
[Required(ErrorMessage="Please specify the base templated directory where the file is located")]
public override string Directory { get; set; }
}
This is the only way I could think of to tackle my requirement, thus the question - How to validate ViewModels derived from an abstract class? However, if there is another more feasible way to achieve this, please advise.
I had a similar situation where I had to change the message based the value of another property.
I made the whole validation server side and without the annotations.
Then I just added the ModelErrors this way:
ModelState.AddModelError("YourPropertyName", "The Error Message you want to show.);
It's a bit extra work and overkill maybe but it did the trick for me.

Resources