I have the following server side code in place in my MVC controller, it's a basic post, get, redirect pattern using model binding.
The problem is that after I submit the page it does the RedirectToAction sending the route option bool parameter correctly. But when I then reload the page (F5) it's still sending the route option parameter (bool) as the previous value (true), when I would expect it to send null. It seems as if the value is being persisted somehow.
Any Ideas on how to resolve this issue? Much appreciated.
Controller code:
[HttpGet]
public ActionResult DischargeHuman(bool? HumanDischarged = null)
{
DischargeHumanViewModel dischargeHumanVM = new DischargeHumanViewModel();
dischargeHumanVM.HumanDischarged = HumanDischarged;
return View(dischargeHumanVM);
}
[HttpPost]
public ActionResult DischargeHuman(DischargeHumanViewModel dischargeHumanVM)
{
string username = HttpContext.User.Identity.Name.Substring(HttpContext.User.Identity.Name.LastIndexOf(#"\")).Trim('\\');
dischargeHumanVM.UserName = username;
if (ModelState.IsValid)
{
dischargeHumanVM.HumanDischarged = _adminTaskLogic.DischargeHuman(dischargeHumanVM.ClientID.Value, dischargeHumanVM.HumanID, dischargeHumanVM.UserName, dischargeHumanVM.DateOfDischarge.Value);
}
if (dischargeHumanVM.HumanDischarged.Value)
{
return RedirectToAction("DischargeHuman", new { dischargeHumanVM.HumanDischarged });
}
else
{
return View(dischargeHumanVM);
}
}
.cshtml code:
#using (Html.BeginForm())
{
#Html.ValidationSummary();
if (Model.HumanDischarged.HasValue)
{
if (Model.HumanDischarged.Value)
{
<span style="color:blue" id="msgSpan">Human successfully discharged.</span>
}
else
{
<span style="color:red" id="msgSpan">An error occurred.</span>
}
Model.PatientDischarged = null;
}
...
view model:
public class DischargePatientViewModel
{
public string UserName { get; set; }
[Required]
[Display(Name = "Client: ")]
public int? ClientID { get; set; }
[Required]
[Display(Name = "Patient ID: ")]
public string PatientID { get; set; }
[Required]
[Display(Name = "Date Of Discharge: ")]
public DateTime? DateOfDischarge { get; set; }
public bool? PatientDischarged { get; set; }
public IEnumerable<SelectListItem> SelectClientList
{
get { return new SelectList(new ClientLogic().GetClients(null, false, true), "ClientID", "ClientDisplayText"); }
}
}
Related
I've problem with creating new record to empty tables on SQLServer
When trying to pass new record I get 'Object reference not set to an instance of an object.' error
When I try edit existing record, tables display content properly, but save changes won't work. With Guests table it will only reload old entry without changes, and for Contacts it's returning same error as on creating new record.
App should let create and edit record by displaying as partial view editor forms for each table.
I'm beginner in MVC.
Below is my code.
Tables models:
[Table("GuestsTest")]
public class Guest
{
[Key]
[HiddenInput(DisplayValue = false)]
public int GuestID { get; set; }
public string GuestLastName { get; set; }
public string GuestFirstName { get; set; }
public string GuestMiddleName { get; set; }
public string GuestEmail { get; set; }
public string GuestSex { get; set; }
}
[Table("ContactsTest")]
public class Contact
{
[Key]
[HiddenInput(DisplayValue = false)]
public int ContactID { get; set; }
[HiddenInput(DisplayValue = false)]
public int GuestID { get; set; }
public int PostalCode { get; set; }
public string City { get; set; }
public string Street { get; set; }
public string HouseNumber { get; set; }
public string PhoneNumber { get; set; }
My view model
public class TableViewModel
{
public Guest GetGuest { get; set; }
public Contact GetContact { get; set; }
}
My controllers
public class AdminController : Controller
{
private IGuestRepository guestRepository;
private IContactRepository contactRepository;
private IQRCodeRepository qrcodeRepository;
public AdminController(IGuestRepository repoG, IContactRepository repoC, IQRCodeRepository repoQ)
{
guestRepository = repoG;
contactRepository = repoC;
qrcodeRepository = repoQ;
}
public ActionResult Index()
{
return View(guestRepository.Guests);
}
public ActionResult EditGuest(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
TableViewModel viewModel = new TableViewModel();
viewModel.GetGuest = guestRepository.Guests.FirstOrDefault(g => g.GuestID == id);
viewModel.GetContact = contactRepository.Contacts.FirstOrDefault(c => c.ContactID == id);
if (viewModel.GetGuest == null)
{
return HttpNotFound();
}
return View(viewModel);
}
public ActionResult GuestForm(int? id)
{
var viewModel = new TableViewModel();
viewModel.GetGuest = guestRepository.Guests.FirstOrDefault(g => g.GuestID == id);
return PartialView("_GuestForm", viewModel.GetGuest);
}
[HttpPost]
public ActionResult GuestForm(TableViewModel getGuest)
{
if (ModelState.IsValid)
{
guestRepository.SaveGuest(getGuest.GetGuest);
qrcodeRepository.CreateQRCode(getGuest.GetGuest);
TempData["message"] = string.Format("Zapisano {0} {1}", getGuest.GetGuest.GuestFirstName, getGuest.GetGuest.GuestLastName);
return RedirectToAction("EditGuest/" + getGuest.GetGuest.GuestID);
}
else
{
return PartialView(getGuest.GetGuest);
}
}
public ActionResult ContactForm(int? id)
{
var viewModel = new TableViewModel();
viewModel.GetContact = contactRepository.Contacts.FirstOrDefault(c => c.ContactID == id);
return PartialView("_ContactForm", viewModel.GetContact);
}
[HttpPost]
public ActionResult ContactForm(TableViewModel getGuest)
{
if (ModelState.IsValid)
{
contactRepository.SaveContact(getGuest.GetContact);
TempData["message"] = string.Format("Zapisano {0} {1}", getGuest.GetGuest.GuestFirstName, getGuest.GetGuest.GuestLastName);
return RedirectToAction("EditGuest/" + getGuest.GetGuest.GuestID);
}
else
{
return PartialView(getGuest.GetContact);
}
}
public ActionResult Create()
{
return View("EditGuest", new TableViewModel());
}
My view
#model MSConference.WebUI.Models.TableViewModel
#{
if (Model.GetGuest.GuestEmail == null)
{
ViewBag.Title = "Tworzenie nowego użytkownika";
}
else
{
ViewBag.Title = "Edycja";
}
Layout = "~/Views/Shared/_AdminLayout.cshtml";
}
#if (Model.GetGuest.GuestEmail == null)
{
<h2>Tworzenie nowego użytkownika</h2>
}
else
{
<h2>Edycja - #Model.GetGuest.GuestFirstName #Model.GetGuest.GuestLastName</h2>
}
#using (Html.BeginForm("EditGuest", "Admin"))
{
#Html.AntiForgeryToken()
<div class="container">
<ul class="nav nav-pills">
<li class="active"><a data-toggle="pill" href="#EditGuest">Edycja - Gość</a></li>
<li><a data-toggle="pill" href="#EditContact">Edycja - Kontakt</a></li>
<li><a data-toggle="pill" href="#EditBill">Edycja - Rezerwacja</a></li>
<li><a data-toggle="pill" href="#EditPlan">Edycja - Konferencja</a></li>
</ul>
<div class="tab-content">
<div id="EditGuest" class="tab-pane fade in active">#Html.Partial("_GuestForm", new MSConference.WebUI.Models.TableViewModel())</div>
<div id="EditContact" class="tab-pane fade">#Html.Partial("_ContactForm", new MSConference.WebUI.Models.TableViewModel())</div>
<div id="EditBill" class="tab-pane fade">sgdg</div>
<div id="EditPlan" class="tab-pane fade">gsdgsgsgsg</div>
</div>
</div>
}
<div>
#Html.ActionLink("Powrót do Listy", "Index", null, new { #class = "btn btn-success" })
</div>
I tried every method of passing model I could find and understand
EDIT
Here are my Repositories. Create error comes from if (contact.ContactID == 0)
public class EFGuestRepository : IGuestRepository
{
private EfDbContext context = new EfDbContext();
public IEnumerable<Guest> Guests
{
get { return context.Guests; }
}
public void SaveGuest(Guest guest)
{
if (guest.GuestID == 0)
{
context.Guests.Add(guest);
}
else
{
Guest dbEntry = context.Guests.Find(guest.GuestID);
if (dbEntry != null)
{
dbEntry.GuestLastName = guest.GuestLastName;
dbEntry.GuestFirstName = guest.GuestFirstName;
dbEntry.GuestMiddleName = guest.GuestMiddleName;
dbEntry.GuestEmail = guest.GuestEmail;
dbEntry.GuestSex = guest.GuestSex;
}
}
context.SaveChanges();
}
public Guest DeleteGuest(int guestId)
{
Guest dbEntry = context.Guests.Find(guestId);
if (dbEntry != null)
{
context.Guests.Remove(dbEntry);
context.SaveChanges();
}
return dbEntry;
}
}
public class EFContactRepository : IContactRepository
{
private EfDbContext context = new EfDbContext();
public IEnumerable<Contact> Contacts
{
get { return context.Contacts; }
}
public void SaveContact(Contact contact)
{
if (contact.ContactID == 0)
{
contact.GuestID = contact.ContactID;
context.Contacts.Add(contact);
}
else
{
Contact dbEntry = context.Contacts.Find(contact.ContactID);
if (dbEntry != null)
{
contact.GuestID = contact.ContactID;
dbEntry.PostalCode = contact.PostalCode;
dbEntry.City = contact.City;
dbEntry.Street = contact.Street;
dbEntry.HouseNumber = contact.HouseNumber;
dbEntry.PhoneNumber = contact.PhoneNumber;
}
}
context.SaveChanges();
}
public Contact DeleteContact(int guestId)
{
Contact dbEntry = context.Contacts.Find(guestId);
if (dbEntry != null)
{
context.Contacts.Remove(dbEntry);
context.SaveChanges();
}
return dbEntry;
}
public interface IGuestRepository
{
IEnumerable<Guest> Guests { get; }
void SaveGuest(Guest guest);
Guest DeleteGuest(int guestId);
}
public interface IContactRepository
{
IEnumerable<Contact> Contacts { get; }
void SaveContact(Contact guest);
Contact DeleteContact(int guestId);
}
I've built whole project working with Adam Freeman pro asp.net mvc 5 book (SportsStore project).
Passing entities to the view isn't a good practice, and depending on what you do with them when they are returned from the view, this can expose you to data tampering. Your "TableViewModel" should just consist of the flattened fields from the guest and contact, or a GuestViewModel and ContactViewModel revealing only the keys and details you need to display/edit. Entities are designed to be associated to a DbContext. Putting a reference to them in a view model is orphaning them. When you pass them back to the controller, they become just POCO instances that are deserialized from the JSON data coming from the view. They have no change tracking etc. that you might expect from using entities while they're freshly loaded from a DbContext. You can attach them to a DbContext, but you would have to manually set the entity State to "Modified" otherwise the context does not know the entity has been changed.
Your issue as it stands right now will probably be in what your SaveGuest method is doing.
The typical MVC lifecycle for the data would be roughly:
View:
Load entity(ies) from context
Populate view models
Pass to view.
Update:
Validate view model against current session
Load entity(ies) from context based on keys
Check that view model isn't stale (last mod date / timestamp / row version matches)
Validate and copy across only details that can be updated from view model into entity
SaveChanges.
Chances are if you're not seeing changes, you're probably attaching the entity to the new context without setting the entity State to "Modified". Note that this is not recommended as you are unconditionally trusting the data coming from the client. For instance you may intend to only see that a user has modified data that you created controls for, but by attaching the entity, you leave the door open for the POST call to be intercepted or played back with any/all data on the entity being altered. You would need to load the existing entity anyways to validate that nothing that shouldn't have been changed had been changed. Another possibility is you could be reloading the entity without realizing, not copying the values across from your view model's entity before calling SaveChanges, or adding the entity to the context thinking it would update the existing row, but it is saving a completely new row with new PK.
I solved my problem by replacing
#Html.Partial("PartialView", Model)
With
#{ Html.RenderPartial("PartialView", Model);}
I also rebuilded my models
Now my entity models looks like this:
[Table("GuestsTest")]
public class Guest
{
[Key]
public int GuestID { get; set; }
public string GuestLastName { get; set; }
public string GuestFirstName { get; set; }
public string GuestMiddleName { get; set; }
public string GuestEmail { get; set; }
public string GuestSex { get; set; }
[Required]
public virtual Contact Address { get; set; }
}
[Table("ContactsTest")]
public class Contact
{
public int ContactID { get; set; }
[Key, ForeignKey("Guest")]
public int GuestID { get; set; }
public int PostalCode { get; set; }
public string City { get; set; }
public string Street { get; set; }
public string HouseNumber { get; set; }
public string PhoneNumber { get; set; }
public virtual Guest Guest { get; set; }
}
And my view models got full rebuild to this:
public class TableViewModel
{
public GuestViewModel GetGuest { get; set; }
public ContactViewModel GetContact { get; set; }
}
public class GuestViewModel
{
[Key]
[HiddenInput(DisplayValue = false)]
public int? GuestID { get; set; }
[MaxLength(50)]
[Required(ErrorMessage = "Proszę podać nazwisko.")]
[Display(Name = "Nazwisko")]
public string GuestLastName { get; set; }
[MaxLength(50)]
[Required(ErrorMessage = "Proszę podać imię.")]
[Display(Name = "Imię")]
public string GuestFirstName { get; set; }
[MaxLength(50)]
[Display(Name = "Drugie imię")]
public string GuestMiddleName { get; set; }
[MaxLength(50)]
[Required(ErrorMessage = "Proszę podać adres email.")]
[RegularExpression(".+\\#.+\\..+", ErrorMessage = "Proszę podać prawidłowy adres e-mail.")]
[Display(Name = "Email")]
public string GuestEmail { get; set; }
[MaxLength(1)]
[Required(ErrorMessage = "Proszę podać płeć.")]
public string GuestSex { get; set; }
}
public class ContactViewModel
{
[HiddenInput(DisplayValue = false)]
public int ContactID { get; set; }
[Key, ForeignKey("Guest")]
[HiddenInput(DisplayValue = false)]
public int GuestID { get; set; }
[Required(ErrorMessage = "Proszę podać kod pocztowy.")]
[Display(Name = "Kod pocztowy")]
public int PostalCode { get; set; }
[Required(ErrorMessage = "Proszę podać Miejscowość.")]
[Display(Name = "Miejscowość")]
public string City { get; set; }
[Required(ErrorMessage = "Proszę podać ulicę.")]
[Display(Name = "Ulica")]
public string Street { get; set; }
[Required(ErrorMessage = "Proszę podać numer domu/mieszkania.")]
[Display(Name = "Numer domu/mieszkania")]
public string HouseNumber { get; set; }
[Required(ErrorMessage = "Proszę podać numer telefonu.")]
[Display(Name = "Numer telefonu")]
public string PhoneNumber { get; set; }
}
Last, I overloaded my save function to work with new models
Below are the steps that I've done to use RavenDB in client/server mode (not embedded) for my ASP.Net MVC application. Although I am following exactly the steps but the results are not as expected. Please correct me if there's any mistakes made.
Install RavenDB.Client & RavenDB.Server via Nuget.
Go to Packages folder, start Raven.Server.exe to get the service running
Open http://localhost:8080/ in browser, RavenStudio is up.
Created a database, say named as "testdb"
I have a RestaurantModel.cs.
internal class RestaurantModel{
public string ResName { get; set; }
public string ResAddress { get; set; }
public string ResCity { get; set; }
public string ResState { get; set; }
public int ResPostcode { get; set; }
public string ResPhoneNum { get; set; }
}
In my controller, I've initialized the document store, as well as opening the session.
public ActionResult Index()
{
using (var store = new DocumentStore
{
Url = "http://localhost:8080/",
DefaultDatabase = "testdb"
})
{
store.Initialize();
using (var session = store.OpenSession())
{
session.Store(new RestaurantModel
{
ResName = "TestName",
ResAddress = "Test Address",
ResCity = "TestCity",
ResState = "TestState",
ResPostcode = 82910,
ResPhoneNum = "02-28937481"
});
session.SaveChanges();
}
}
return View();
}
Build the solution. Refresh localhost:8080, data is still not inserted.
I have no idea what am I doing wrong although I'm following exactly all the tutorials I've gone through. So many attempts using different ways but still to no avail.
Thanks in advance for your help!
Tried hitting debug, it opens localhost:33062, but then it shows me server error as shown in below.
# To be more specific #
I have a RestaurantModel.cs
internal class RestaurantModel
{
public string ResName { get; set; }
public string ResAddress { get; set; }
public string ResCity { get; set; }
public string ResState { get; set; }
public int ResPostcode { get; set; }
public string ResPhoneNum { get; set; }
}
I have a AdminController
using FYP2.Models;
using Raven.Client.Document;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace FYP2.Controllers
{
public class AdminController : Controller
{
// GET: Admin
public ActionResult Index()
{
using (var store = new DocumentStore
{
Url = "http://localhost:8080/",
DefaultDatabase = "foodfurydb"
})
{
store.Initialize();
using (var session = store.OpenSession())
{
session.Store(new RestaurantModel
{
ResName = "Boxer Republic",
ResAddress = "NO 2A-G, Jalan BK 5A/2C",
ResCity = "Puchong",
ResState = "Selangor",
ResPostcode = 47180,
ResPhoneNum = "03-80748088"
});
session.SaveChanges();
}
}
return View();
}
public ActionResult AdminLogin()
{
return View();
}
public ActionResult AddRestaurant()
{
return View();
}
public ActionResult ManageFoodMenu()
{
return View();
}
public ActionResult ManageOrder()
{
return View();
}
public ActionResult ManageReservation()
{
return View();
}
}
}
I have Admin View, consist of
AddRestaurant, AdminLogin, ManageFoodMenu, ManageOrder, ManageReservation
I really don't know what can cause this problem. Just one question, you just compiled the project or you called your action domain:port/yourcontroller/index?
I've created a mvc project and copied your code:
public class HomeController : Controller
{
internal class RestaurantModel
{
public string ResName { get; set; }
public string ResAddress { get; set; }
public string ResCity { get; set; }
public string ResState { get; set; }
public int ResPostcode { get; set; }
public string ResPhoneNum { get; set; }
}
public ActionResult Index()
{
using (var store = new DocumentStore
{
Url = "http://locaslhost:8080/",
DefaultDatabase = "testdb"
})
{
store.Initialize();
using (var session = store.OpenSession())
{
session.Store(new RestaurantModel
{
ResName = "TestName",
ResAddress = "Test Address",
ResCity = "TestCity",
ResState = "TestState",
ResPostcode = 82910,
ResPhoneNum = "02-28937481"
});
session.SaveChanges();
}
}
return View();
}
}
When I accessed the path http://localhost:50791/ which corresponds to my HomeController/Index, everything went as expected:
Can you give more details about you are trying to do?
Model:
public class PublishedSongViewModel
{
public int Id { get; set; }
[Required(AllowEmptyStrings = false)]
public string SongName { get; set; }
//...
[Required]
public IEnumerable<string> Category { get; set; }
}
public class CategoryViewModel
{
public short Id { get; set; }
public string Title { get; set; }
public virtual ICollection<SongCategoryViewModel> SongCategory { get; set; }
}
public class SongCategoryViewModel
{
public int Id { get; set; }
[Required]
public int PublishedSongId { get; set; }
[Required]
public short CategoryId { get; set; }
}
View:
#model IList<PublishedSongViewModel>
#using (Html.BeginForm("PublishMusic", "Publish", FormMethod.Post, new { #enctype = "multipart/form-data", #id = "form-upload" }))
{
#Html.DropDownListFor(x => Model[i].Category, new SelectList(//Categories list here), new { #class = "form-control dl_Categories ", Multiple = "Multiple" })
}
Controller:
[HttpPost]
public ActionResult PublishMusic(IEnumerable<PublishedSongViewModel> songDetails)
{
if (songDetails != null)
{
IEnumerable<PublishedSongViewModel> savedSongs = (IEnumerable<PublishedSongViewModel>)(Session["UserSongs"]);
var lookupDetails = songDetails.ToDictionary(song => song.Id, song => song);
if (savedSongs != null)
{
foreach (var publishedSong in savedSongs)
{
var key = publishedSong.Id;
if (lookupDetails.ContainsKey(key))
{
var details = lookupDetails[key];
publishedSong.SongName = details.SongName;
}
db.SongCategories.Add(new SongCategoryViewModel { PublishedSongId = key, CategoryId = //categories id that user typed in on editorFor});
db.PublishedSongs.Add(publishedSong);
db.SaveChanges();
}
}
}
return View("Index");
}
I'v filled CategoryViewModel table up with data in my SQL.
1) How do I get the titles of CategoryViewModel and pass them in the SelectList(//Here) parameter in my viewmodel?
2) In the PublishMusic Action, how do I get the CategoryId for the SongCategoryViewModel from the one or more categories that the user selected from songDetails.Category?
I am not sure if I am on the right track with this. basically the categories are like tags, the user can select more than one. I'v also cut out unessential code to make easier to read.
I'm newbie to MVC architecture.When I'm trying to update, its showing error ,Its totally strange but the data is updating.
The model item passed into the dictionary is of type 'CMS.Domain.Models.Site', but this dictionary requires a model item of type 'CMS.Web.ViewModels.SiteModel'.'.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: The model item passed into the dictionary is of type 'CMS.Web.ViewModels.SiteModel', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[CMS.Web.ViewModels.SiteModel]'.
My code looks like:
ViewModels:
namespace CMS.Web.ViewModels
{
public class SiteModel
{
public SiteModel()
{
SiteStatus = new List<SelectListItem>();
}
[Key]
public int ID { get; set; }
[Required(ErrorMessage = "Site Name is required")]
[Display(Name = "Site Name")]
public string Title { get; set; }
[Display(Name = "Require Login")]
public bool RequiresLogin { get; set; }
[Display(Name = "Force HTTPS")]
public bool ForceHTTPS { get; set; }
[Display(Name = "Enable Approval")]
public bool Approval { get; set; }
[AllowHtml]
public IList<SelectListItem> SiteStatus { get; set; }
public bool Deleted { get; set; }
public string CreatedBy { get; set; }
public DateTime CreatedOn
{
get { return _createdOn; }
set { _createdOn = value; }
}
private DateTime _createdOn = DateTime.Now;
public string LastUpdatedBy { get; set; }
public DateTime LastUpdatedOn
{
get { return _lastUpdatedOn; }
set { _lastUpdatedOn = value; }
}
private DateTime _lastUpdatedOn = DateTime.Now;
[Display(Name = "Site State")]
public string SiteState { get; set; }
}
}
Model:
namespace CMS.Domain.Models
{
public partial class Site : Model
{
public string Title { get; set; }
public bool Approval { get; set; }
public bool RequiresLogin { get; set; }
public bool ForceHTTPS { get; set; }
public virtual string SiteStatus { get; set; }
public bool Deleted { get; set; }
}
}
Controller:
public ActionResult Index()
{
var _sites = _siterepository.FindAll();
return View(_sites);
}
public ActionResult Add()
{
var model = new SiteModel();
var _SiteStatus = _siterepository.GetSiteStatus();
foreach (var _sitestatus in _SiteStatus)
{
model.SiteStatus.Add(new SelectListItem()
{
Text = _sitestatus.StatusName,
Value = _sitestatus.StatusName.ToString()
});
}
return View(model);
}
[HttpPost]
public ActionResult Add(SiteModel _sitemodel)
{
var model = _sitemodel.ToEntity();
_siterepository.Add(model);
return View(model);
}
public ActionResult Edit(int id)
{
var model = new SiteModel();
var Site = _siterepository.Find(id);
model = Site.ToModel();
var _SiteStatus = _siterepository.GetSiteStatus();
foreach (var _sitestatus in _SiteStatus)
{
model.SiteStatus.Add(new SelectListItem()
{
Text = _sitestatus.StatusName,
Value = _sitestatus.StatusName.ToString(),
Selected = _sitestatus.StatusName == Site.SiteStatus
});
}
return View(model);
}
[HttpPost]
public ActionResult Edit(SiteModel _sitemodel)
{
var model = _sitemodel.ToEntity();
_siterepository.Update(model);
return View(model);
}
I'm struggling to resolve this , please help.
Check your View's model declaration. It is expecting an enumerable list (IEnumerable<CMS.Web.ViewModels.SiteModel>), but you are passing it a single instance of CMS.Web.ViewModels.SiteModel
I've got a form with a dropdownlist in my MVC app. Now that I'm trying to add validation to the mix it seems that a dropdownlist fails validation no matter what it's value is.
Without the validation it will allow the controller to work and redirect as planned. With the validation it does seem to allow the database changes to occur but ModelState.IsValid is false.
I'm stuck. Is this a known issue?
View:
<label for="parent">Child of:</label>
<%= Html.DropDownList("parent", (SelectList)ViewData["pageList"])%>
<%= Html.ValidationMessage("parent") %>
Controller action:
[AcceptVerbs(HttpVerbs.Post)]
[ValidateInput(false)]
[ValidateAntiForgeryToken()]
public ActionResult Create(Page page)
{
try
{
pageRepository.Insert(page);
}
catch (RuleException ex)
{
ex.CopyToModelState(ModelState);
}
if (!ModelState.IsValid)
{
var pageSelectList = pageRepository.GetTop().ToList();
pageSelectList.Add(new Page
{
menuTitle = "None"
});
ViewData["pageList"] = new SelectList(pageSelectList.OrderBy(x => x.listOrder), "ID", "menuTitle");
return View();
}
return RedirectToAction("List");
}
The error returned is: The value 'x' is invalid.
Where 'x' is the numeric value of the current selection. The failure occurs no matter what the chosen value is.
public class Page
{
private EntityRef<Page> _parent = default(EntityRef<Page>);
private EntitySet<Page> _children = new EntitySet<Page>();
public int ID { get; set; }
public string pageTitle { get; set; }
public string menuTitle { get; set; }
public string content { get; set; }
public int listOrder { get; set; }
public bool visible { get; set; }
public int parent { get; set; }
public DateTime? created { get; set; }
public DateTime? edited { get; set; }
public string createdBy { get; set; }
public string lastEditBy { get; set; }
public string linkInfo { get; set; }
public bool IsSelected { get; set; }
public Page Parent
{
// return the current entity
get { return this._parent.Entity; }
set { this._parent.Entity = value; }
}
public EntitySet<Page> Children
{
get { return this._children; }
set { this._children.Assign(value); }
}
public static Page Error404()
{
return (new Page
{
content = "<p>Page not found</p>",
pageTitle = "404. Page not found"
});
}
}
Here's what I tried for a workaround:
public ActionResult Create([Bind(Exclude="parent")] Page page)
{
page.parent = Convert.ToInt32(Request.Form["parent"]);
...
I just excluded the dropdownlist from the ModelBinding and reloaded it back in via the Request.Form. Is it good practice?
What's throwing the RuleException? I'm assuming you're using some sort of validation engine to determine whether the "parent" property is valid or not. I'd step through to see why this exception is being thrown. Maybe the value isn't passing into your controller action correctly or maybe your validation rules are different than what you think they are.
I ended up testing against ModelState["parent"].Value.AttemptedValue instead of the entity property which was nulling out at the attempt to put a string into an int?.