This is baffling. In one of my MVC models, Project, I have two properties which both save the IDs for other models, like so.
public class Project
{
...
public int PlatformID { get; set; }
public int DepartmentID { get; set; }
...
}
I use a custom editor in which I edit these fields with a kendo dropdown, using their respective names and IDs as text and value fields.
<div class="col-md-11 details-item">
#Html.LabelFor(m => m.PlatformID, new { #class = "col-md-4 details-label" })
#(Html.Kendo().DropDownListFor(m => m.PlatformID)
.Name("PlatformID")
.DataValueField("ID")
.DataTextField("PlatformName")
.BindTo((System.Collections.IEnumerable)ViewData["Platforms"])
.HtmlAttributes(new { #class = "col-md-7 details-editor" })
)
</div>
<div class="col-md-11 details-item">
#Html.LabelFor(m => m.DepartmentID, new { #class = "col-md-4 details-label" })
#(Html.Kendo().DropDownListFor(m => m.DepartmentID)
.Name("DepartmentID")
.DataValueField("ID")
.DataTextField("Name")
.BindTo((System.Collections.IEnumerable)ViewData["Departments"])
.HtmlAttributes(new { #class = "col-md-7 details-editor" })
)
</div>
What's crazy is that PlatformID saves perfectly, but DepartmentID does not even get sent to the controller. If DepartmentID is the only thing I change while editing, it does not even register a change has been made. Otherwise, it gets sent as default 0.
The Department class is insanely simple.
public class Department
{
[Key]
public int ID { get; set; }
[DisplayName("Department Name")]
public string Name { get; set; }
}
PlatformID is essentially identical. Maybe I'm missing something stupid, or maybe there is evil afoot. Thanks for any help you can provide!
Figured out the answer - turns out having a DropDown in an editor template does not work if the item is nullable! So I just changed public int? DepartmentIDto public int DepartmentIDin my Project model, and life was good again.
Related
Im try to give alert if there is a error in form in my form there are some text fields validation like below
<div class="form-group">
<div class="row">
<div class="col-lg-4 col-md-4 col-sm-4 col-xm-12">
<label class="control-label form-text-align text-top-padding ">
#Resources.StandardPrice
</label>
</div>
<div class="col-lg-8 col-md-8 col-sm-8 col-xm-12 text-top-padding">
#Html.TextBoxFor(model => model.products.BasicPrice, new { #class = "form-control errorClass", #id = "basicPrice", #placeholder = #Resources.StandardPrice, #onblur = "addClass(this)", #maxlength = Resources.AddNewProductFieldMaxLength })
#Html.ValidationMessageFor(model => model.products.BasicPrice, null, new { #class = "help-inline" })
</div>
</div>
</div>
if there is some error in form how can I give a alert
In order to display the error message and prevent the submission of your form you have to add controls on your model (or ViewModel).
For example if you want that field to be required so that the form will not be submitted only if the user give a value to that field you have to add the [Required] to your product's model attribute "BasicPrice" as follow :
public class products {
public int ID { get; set; }
[Required]
public string Name { get; set; }
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
[Required]
public string Genre { get; set; }
[Range(1, 100)]
[DataType(DataType.Currency)]
public decimal Price { get; set; }
[StringLength(5)]
public string Rating { get; set; }
}
Here's a link to the Microsoft official Documentation which explain the subject and give more details :
https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started-with-aspnet-mvc4/adding-validation-to-the-model
If you already did what #Mohamed Kamel Bouzekria suggested and still not working.
it's possible that you missing something in your controller which could this
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult YOurMethod( Model model)
{
if (ModelState.IsValid)//if there is no errors and valid values
{
//do something
db.SaveChanges();
return RedirectToAction("Index");
}
return View();//else return the same view that should display the errors
}
if it still not working then you missing something else in your view.if so post the full code of the view
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);
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;
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.
Hi I have a PersonName class that look like this:
public class PersonName
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string MiddleName { get; set; }
}
I have a UserCreateModel that is composed of PersonName among other properties like this:
public class UserCreateModel
{
public PersonName FullName { get; set; }
public string UserName { get; set; }
...........
}
I have a Editor template in /Views/Shared/EditorTemplates/PersonName.cshtml that looks like this (trimmed down):
#model PersonName
First: #Html.EditorFor(m => m.FirstName)
Last: #Html.EditorFor(m => m.LastName)
Middle: #Html.EditorFor(m => m.Middle)
However when I do (short version) :
#model UserCreateModel
#{Html.BeginForm("Create", "User");}
#Html.EditorForModel()
#{Html.EndForm();}
The PersonName does not bind to the editor and does not show up at all. I even tried UIHint, but not sure what I am missing. Also how do I debug this issue?
Please help!
You have an editor template for the PersonName class (~/Views/Shared/EditorTemplates/PersonName.cshtml) but not for the UserCreateModel which is your main model. So you need to either write an editor template for the UserCreateModel class and use EditorForModel or specify the property using EditorFor like this:
#model UserCreateModel
#using(Html.BeginForm("Create", "User"))
{
#Html.EditorFor(x => x.FullName)
}
If it's the exact code you use, i think you should change Model in lambda with m
#model PersonName
First: #Html.EditorFor(m => m.FirstName)
Last: #Html.EditorFor(m => m.LastName)
Middle: #Html.EditorFor(m => m.Middle)
and not Model.Firstname, etc
you should check out this page. I think it is what you want. It is basically saying that you will have to create a custom template for the model. It is very thorough.
http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-4-custom-object-templates.html
hope this helps you