Asp.net MVC 3 "parameter conversion from type 'System.String' to type failed" when using SelectList Dropdown box - asp.net

I'm stuck and after looking this up for hours, I think I need more eyeballs.
The situation is the following:
It's an Asp.Net MVC3 with Entity Framework 4 project. And I have two classes. One ConfigurationFile and another one Action. There is a one-to-many relationship between the two. Here is a simplified view on the code:
public class ConfigurationFile
{
[Key, Required]
[Column(TypeName = "uniqueidentifier")]
public Guid Id { get; set; }
[Required]
public string Name { get; set; }
[Column(TypeName = "uniqueidentifier")]
[Required]
public Guid ActionId { get; set; }
public virtual Models.Action Action { get; set; }
}
public class Action
{
[Key, Required]
[Column(TypeName = "uniqueidentifier")]
public Guid Id { get; set; }
[Required]
public string ActionValue { get; set; }
}
Then I want to create a new ConfigurationFile, and are my two controller methods (and at this point, this is 95% Visual Studio 10 generated code):
// db is my context class.
//
// GET: /Configuration/Create
public ActionResult Create()
{
ViewBag.ActionId = new SelectList(db.Actions, "Id", "ActionValue");
return View();
}
//
// POST: /Configuration/Create
[HttpPost]
public ActionResult Create(Models.ConfigurationFile configurationfile)
{
if (ModelState.IsValid)
{
configurationfile.Id = Guid.NewGuid();
db.ConfigurationFiles.Add(configurationfile);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.ActionId = new SelectList(db.Actions, "Id", "ActionValue", configurationfile.ActionId);
return View(configurationfile);
}
And here is a snippet of my Create view:
#model MyProject.Areas.ConfigurationFile.Models.ConfigurationFile
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Configuration File</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.ActionId, "Action")
</div>
<div class="editor-field">
#Html.DropDownList("ActionId", String.Empty)
#Html.ValidationMessageFor(model => model.ActionId)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
When I open the Create page, I can clearly see that my dropdown for the Action class is fine (correct value -- the Action.Id -- and text -- Action.ActionValue -- ) but when I submit the form, I have the following error: "The parameter conversion from type 'System.String' to type 'MyProject.Models.Action' failed because no type converter can convert between these types."
Help please !!

Right now MVC has no way of connecting your dropdownlist from your view to the ActionId of your ConfigurationFile object.
I would try replacing this line:
#Html.DropDownList("ActionId", String.Empty)
for this
#Html.DropDownListFor(model => model.ActionId, ViewBag.ActionId)
Other than that, I can't think of what else you might have done wrong.
I hope that helps!

This is how I did to circumvent the problem. I just changed my controller this way:
Models.Action act = db.Actions.Find(configurationfile.ActionId);
ModelState.Clear();
configurationfile.Action = act;
TryValidateModel(configurationfile);
And after that, the validation was Ok. A bit hacky (and another possible hit on the DB), but at least, I can keep going.

Related

how and where to connect viewmodel to dbcontext

I inherited a mvc app. This app uses entity framework with database first. It was made with no viewmodels and viewbags everywhere for the dropdowns and error messages. Now I am tasked with making many changes to it because you can not validate the related properties that are not in the main class among other things.
I am trying to create a viewmodel so I can display only necessary data, validate it and not be linked directly to the model. So far I get null for all my fields on a form using the viewmodel I created. I have tried to use automapper but get a mapping error: "Missing type map configuration or unsupported mapping"
Here is part of the controller:
public ActionResult ChangeOwner(int id = 0)
{
var combine = new combineValidationAssetViewModel();
Mapper.CreateMap<ToolingAppEntities1, combineValidationAssetViewModel>();
Mapper.CreateMap<combineValidationAssetViewModel, ToolingAppEntities1>();
Asset asset = db.Assets.Find(id);
Mapper.Map(combine, asset, typeof(combineValidationAssetViewModel), typeof(Asset));
.....
return View(combine);
}
Here is part of the view model:
public class combineValidationAssetViewModel
{
public Asset Assets { get; set; }
public Transaction Transactions { get; set; }
public LocationType LocationTypes { get; set; }
public ToolType ToolTypes { get; set; }
public OwnerType OwnerTypes { get; set; }
public int AssetId { get; set; }
public int fkToolTypeId { get; set; }
[Required]
[Display(Name = "Owner")]
public int fkOwnerId { get; set; }
[Required]
[Display(Name = "Location")]
public int fkLocationId { get; set; }
public int LocationTypeId { get; set; }
public int OwnerTypeId { get; set; }
Here is part of the view:
#model ToolApp.ViewModels.combineValidationAssetViewModel
.....
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Asset</legend>
#Html.HiddenFor(model => model.AssetId)
#Html.HiddenFor(model => model.CreatedByUser)
#Html.HiddenFor(model => model.CreateDate)
#Html.HiddenFor(model => model.SerialNumber)
#Html.HiddenFor(model => model.LocationTypeId)
<div class="editor-label">
#Html.LabelFor(model =>model.SerialNumber)
</div>
<div class="editor-field">
#Html.DisplayFor(model => model.SerialNumber)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.fkToolTypeId, "Tool Name")
</div>
<div class="editor-field">
#Html.DisplayFor(model => model.Description)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.fkOwnerId, "New Owner")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.fkOwnerId, new SelectList(ViewBag.fkOwnerId, "Value", "Text"), new{style="width:320px;height:25px;"})
#Html.ValidationMessageFor(model => model.fkOwnerId),
The form displays but it is null (no values in any of the fields displayed. I would like to map it manually so I understand it. Have tried the automapper but it's not working yet. I have tried some ideas from here and other websites but same result. I don't completely understand the linq to ef yet so my problem may lie there also.
This main controller has 10 different action results on it and is filled with data calls and viewbags. I'm looking for advice on the direction I should go. I need to get the thing working but also want to make changes to it that will move it in the direction of a viable mvc app. Main issue at the moment is how to connect the viewmodel with the dbcontext. I found the context at the top of the controller like this:
{ private ToolingAppEntities1 db = new ToolingAppEntities1();
followed by many includes...
any suggestions would be appreciated
You map into the wrong direction:
Mapper.Map(combine, asset,
typeof(combineValidationAssetViewModel), typeof(Asset));
This maps the empty combine object to asset. You should reverse it, and use a strong-typed (generic) overload:
var combine = Mapper.Map<combineValidationAssetViewModel>(asset);

MVC File upload with Custom Validation is null

I am uploading and image and verifying its validity as per this post here:
How to validate uploaded file in ASP.NET MVC?
However, my example differs slightly because I am not just receiving the file, I am also receiving some properties for my model. However, my validator always fires, I debugged and found that my file is always null, so validator always fires back 'false'. I don't understand why, my input in view seems to be correct. Any ideas?
namespace PhotoManagement.Models
{
public class Photo
{
public virtual int PhotoId { get; set; }
public virtual int ClientId { get; set; }
public virtual string PhotoDescription { get; set; }
[ImageValidation(ErrorMessage="Please select a PNG/JPEG image smaller than 10 MB")]
[NotMapped]
public HttpPostedFileBase File { get; set; }
}
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Photo photo)
{
if (ModelState.IsValid)
{
db.Photos.Add(photo);
db.SaveChanges();
// File upload occurs now
var FilePath = Path.Combine(Server.MapPath("~/App_Data/" + photo.ClientId), photo.PhotoId.ToString());
photo.File.SaveAs(FilePath);
return RedirectToAction("Create");
}
else return View();
}
#using (Html.BeginForm(new { enctype = "multipart/form-data" })) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Photo for #Session["Name"]</legend>
<div class="editor-field">
#Html.Hidden("ClientId",(int)Session["UserId"])
</div>
<div class="editor-label">
#Html.LabelFor(model => model.PhotoDescription)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.PhotoDescription)
#Html.ValidationMessageFor(model => model.PhotoDescription)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.File)
</div>
<div class="editor-field">
<input type="file" name="File" id="File"/>
#Html.ValidationMessageFor(model => model.File)
</div>
You are using a wrong overload of the Html.BeginForm helper.
The correct call is this:
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
}
You were calling:
Html.BeginForm(object routeValues)
instead of:
Html.BeginForm(
string actionName,
string controllerName,
FormMethod method,
object htmlAttributes
)
Look at the generated markup in your browser and you will see the fundamental difference.
Instead of
public ActionResult Create(Photo photo)
Try
public ActionResult Create(Photo photo, HttpPostedFileBase file)
EDIT: Don't forget to set the HTTP method to POST in the view:
#using (Html.BeginForm("ActionName", "ControllerName", FormMethod.Post, new { enctype = "multipart/form-data" }))
The file in Model will always give you null. In order to get file :
[HttpPost]
public ActionResult Create(UserViewModel model,
FormCollection formCollection, HttpPostedFileBase file){
/* Your code here */
if(file==null)
{
ModelState.AddModelError("NoFile", "Upload File");
}
}
Here HttpPostedFileBase file will give you the complete object of file uploaded. You can have check condition on the object file. Don't forget to add the below mentioned validation message in view.
#Html.ValidationMessage("NoFile")

DbContext.SaveChanges

I have an issue where inserting one record to the entity ProfessionalEmploymentRecord EF ends up duplicating existing Professional, Program, Role records in each entitiy, respectively. I have tried to explicitely initilize Professional, Program, Role with existing key/value pairs in my database, but EF still creates duplicate values with new keys.
public class ProfessionalEmploymentRecord
{
public int ProfessionalEmploymentRecordId {get; set; }
public virtual Professional Professional { get; set; }
public virtual Program Program { get; set; }
public virtual Role Role { get; set; }
}
public class Program
{
public int ProgramId { get; set; }
[MaxLength(20)]
public string Name { get; set; }
}
public class Role
{
public int RoleId { get; set; }
[MaxLength(50)]
public string Name { get; set; }
}
public class Professional
{
public int ProfessionalId { get; set; }
[MaxLength(20)]
public string Name { get; set; }
}
When using Create below I end up with new records in the three related entities although they exist and have a key set. In other words I end up with copies of values with new primary keys (incremented by one) in the three related entities.
public ActionResult Create(ProfessionalEmploymentRecord professionalemploymentrecord)
{
if (ModelState.IsValid)
{
db.ProfessionalEmploymentRecords.Add(professionalemploymentrecord);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(professionalemploymentrecord);
}
As requested
#model My.Models.ProfessionalEmploymentRecord
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend> Professional Employment Record</legend>
<div class="editor-label">
Professional
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Professional.Name)
#Html.ValidationMessageFor(model => model.Professional.Name)
</div>
<div class="editor-label">
Program
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Program.Name)
#Html.ValidationMessageFor(model => model.Program.Name)
</div>
<div class="editor-label">
Role
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Role.Name)
#Html.ValidationMessageFor(model => model.Role.Name)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
I have used code below to test. (I see that executing SaveChanges sets *Id++ in reco).
public void TestDummyAdd()
{
Professional prof = new Professional { ProfessionalId = 1, Name = "Chris" };
Program prog = new Program { ProgramId = 1, Name = "A" };
Role role = new Role { RoleId = 1, Name = "B" };
ProfessionalEmploymentRecord reco = new ProfessionalEmploymentRecord { Professional = prof, Role = role, Program = prog, ProfessionalEmploymentRecordId = 0 };
if (ModelState.IsValid)
{
db.ProfessionalEmploymentRecords.Add(reco);
db.SaveChanges();
}
}
Add is operation executed on whole object graph. If you add your record you are also adding all its relations, their relations, etc. There are two options how to solve this issue and both require you to manually set state of some entities (and sometimes also relations) in your persisted object grap:
Call Add as you did but after that you must set state for all existing entities in the graph to either Unchanged or Modified (entity will be udpated in database)
Call Attach instead of Add and after that set state for all new entities to Added
Settings state can be done by:
db.Entry(entity).State = EntityState.Added;

Remote Validate for DropDownList, MVC3, not firing in my case

I am using ASP.NET MVC3 and EF 4.1
I have two DropDownList in my Model, It is required and not duplicated too.
And I want the Remote validate function: ValidateDuplicateInsert get firing when user submit data. But I can NOT get the ValidateDuplicateInsert function firing.
Where am I wrong?
My Model
[Key]
public int CMAndOrgID { get; set; }
[Display(Name = "CM")]
[Required(ErrorMessage = "CM is required.")]
[Remote("ValidateDuplicateInsert", "CMAndOrg", HttpMethod = "Post", AdditionalFields = "CMID, OrganizationID", ErrorMessage = "CM is assigned to this Organization.")]
public int? CMID { get; set; }
[Display(Name = "Organization")]
[Required(ErrorMessage = "Organization is required.")]
public int? OrganizationID { get; set; }
public virtual CM CM { get; set; }
public virtual Organization Organization { get; set; }
The ValidateDuplicateInsert function in my CMAndOrg controller
[HttpPost]
public ActionResult ValidateDuplicateInsert(string cmID, string orgID)
{
bool flagResult = true;
foreach (CMAndOrg item in db.CMAndOrgs)
{
if (item.CMID.ToString() == cmID && item.OrganizationID.ToString() == orgID)
{
flagResult = false;
break;
}
}
return Json(flagResult);
}
And my View
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>CMAndOrg</legend>
<div class="editor-label">
#Html.LabelFor(model => model.CMID, "CM")
</div>
<div class="editor-field">
#Html.DropDownList("CMID", String.Empty)
#Html.ValidationMessageFor(model => model.CMID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.OrganizationID, "Organization")
</div>
<div class="editor-field">
#Html.DropDownList("OrganizationID", String.Empty)
#Html.ValidationMessageFor(model => model.OrganizationID)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
There is a bug in MVC3 related to unobtrusive validation on dropdownlist. Please reference to this http://aspnet.codeplex.com/workitem/7629[^] link for more detail explaination.
Briefly, you can't use the same name for category collection and category field, so just change your collection name and update following line in your view
#Html.DropDownList("CategoryID", String.Empty)
with this
#Html.DropDownListFor(model => model.CategoryID, new SelectList((System.Collections.IEnumerable)ViewData["Categories"], "Value", "Text"))
Thanks again Henry He
Original link
http://www.codeproject.com/Articles/249452/ASP-NET-MVC3-Validation-Basic?msg=4330725#xx4330725xx

ASP.NET MVC 3 Remote validation stopped working

I'm using the client side validation for my views, and have just created a ViewModel which contains an organisation object and an address object.
I used to have a ViewModel that just mapped to the domain entity. On my domain entity I had the following:
[NotMapped]
[Remote("ValidOrganisation", "Manage", "Organisations", ErrorMessage = "That organisation doesn't exist")]
public string Organisation { get; set; }
However, I have now created a ViewModel for the view that contains the following:
public class PersonModel
{
public Person Person { get; set; }
public AddressModel Address { get; set; }
}
The person object contains the Organisation property.
In my view, I have the following:
<div>
<label for="Organisation">Organisation</label>
<div class="input large">
#Html.TextBoxFor(m => m.Person.Organisation)
<span class="help-block">Type the first letter of the organisation to search</span>
#Html.ValidationMessageFor(m => m.Person.Organisation)
#Html.Hidden("OrganisationID")
</div>
</div>
The only thing that changed was:
#Html.TextBoxFor(m => m.Organisation)
to:
#Html.TextBoxFor(m => m.Person.Organisation)
My remote validation code is:
public JsonResult ValidOrganisation(string organisation)
{
var exists = orgs.SelectAll().Where(o => o.Name.ToLower() == organisation.ToLower()).Count() > 0;
return Json(exists, JsonRequestBehavior.AllowGet);
}
The problem is that NULL is now always being passed in, which is always returning false.
Is this something to do with the Organisation property now changing to be Person.Organisation?
1] Open your view Page Source and see what is Id renderd for the
#Html.TextBoxFor(m => m.Person.Organisation)
2] and rename organisation in method to accept same ID I am expecting it to be Person_Organisation
public JsonResult ValidOrganisation(string organisation)
use below
public JsonResult ValidOrganisation(string Person_Organisation)

Resources