I want to fill in multiple tables with one form - c#

I've checked a lot of other threads before and I couldn't really find anything specific to my issue, which is the following:
I'm working with ApplicationUsers on my project. Next to that, each user has a profile (which is a different table). I want to fill in the profile while I am "creating" a new ApplicationUser in my backoffice, considering they are connected to one another by the userId. The issue is that this is not quite working. I'm using ViewModels and I've made a specific Viewmodel for ApplicationUsers as well (UserViewModel).
So far, I can make a new ApplicationUser perfectly fine, but once i start to try and create a Profile using the same form as the one I've used for my ApplicationUser things starts to go wrong. I have a few hunches where I'm going wrong (for example only using 1 model in my view (create/edit.cshtml, though i'm pretty sure you can only use 1 model on create in a view?).
Below is my viewmodel for user (UserViewModel.cs)
As you can see here my UserViewModel has a virtual property Profile which should enable the possibility of creating profiles together with the users? Or maybe I'm just dead wrong here already.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using App.Models;
using App.Models.ViewModels;
namespace App.Models.Identity.ViewModels
{
public class UserViewModel
{
public string UserId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public virtual Profile Profile { get; set; }
}
}
The following are my edit functions (UserController.cs)
Keep in mind that everything already pretty much works when I remove any reference to the profile. It's when I start to try and add profile fields (be it here or in the view below) that the problems start to arise.
[HttpGet]
public IActionResult Edit(string id)
{
if (id == null)
{
return new HttpStatusCodeResult(400);
}
var model = _kletsContext.Users.FirstOrDefault(m => m.Id == id);
var profile = _kletsContext.Profiles.FirstOrDefault(m => m.UserId == model.Id);
if(model == null)
{
return RedirectToAction("Index");
}
var viewModel = new UserViewModel
{
UserId = model.Id,
UserName = model.UserName,
Email = model.Email,
};
return View(viewModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(UserViewModel model, Profile profile)
{
UserViewModel viewModel = null;
try
{
if(!ModelState.IsValid)
throw new Exception("The User model is not valid!");
var originalModel = _kletsContext.Users.FirstOrDefault(m => m.Id == model.UserId);
var originalProfile = _kletsContext.Profiles.FirstOrDefault(m => m.UserId == model.UserId);
if(originalModel == null)
throw new Exception("The existing User: " + model.UserName + " doesn't exists anymore!");
originalModel.UserName = model.UserName;
originalModel.Email = model.Email;
originalProfile.Age = profile.Age;
_kletsContext.Users.Attach(originalModel);
_kletsContext.Profiles.Attach(profile);
_kletsContext.Entry(originalModel).State = EntityState.Modified;
_kletsContext.Entry(profile).State = EntityState.Modified;
if (_kletsContext.SaveChanges() == 0)
{
throw new Exception("The User model could not be saved!");
}
return RedirectToAction("Index");
}
catch(Exception ex)
{
ModelState.AddModelError(string.Empty, "Unable to save changes.");
viewModel = new UserViewModel
{
UserId = model.UserId,
UserName = model.UserName,
Email = model.Email,
};
}
return View(viewModel);
}
Below is my Edit.cshtml:
#model App.Models.Identity.ViewModels.UserViewModel
#{
ViewBag.Title = "User";
ViewBag.SubTitle = "Nieuwe";
ViewBag.Description = "Aanmaak van een nieuwe User";
Layout = "~/Areas/Backoffice/Views/Shared/_Layout.cshtml";
}
<div class="row">
<div class="col-xs-12">
<div class="panel panel-default">
<div class="panel-heading">
Aanmaak van een nieuwe User
</div>
<div class="panel-body">
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.HiddenFor(m => m.UserId)
<fieldset>
<legend class="hidden">Aanmaak van een nieuwe User</legend>
#Html.ValidationSummary("", new {#class="alert-danger"})
<div class="form-group">
#Html.LabelFor(m => m.UserName)
#Html.TextBoxFor(m => m.UserName, new { #class= "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Email)
#Html.TextBoxFor(m => m.Email, new { #class= "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Profile.Age)
#Html.TextBoxFor(m => m.Profile.Age, new { #class= "form-control" })
</div>
#Html.ActionLink("Terug naar het overzicht", "Index", new { }, new { #class= "btn btn-default" })
<input type="submit" value="Save" class="btn btn-primary" />
</fieldset>
}
</div>
</div>
</div>
</div>
Extra: I'll add my ProfileViewModel if necessary, or my DBContext file (or any model). Just let me know. I've been looking at this for a while, but I'm pretty sure that I'm just misunderstanding something pretty basic?
PS: I end up getting the exceptionError so I know there has to be a general problem where nothing inside of my Try works. See the image below
https://gyazo.com/399e59d7d3cfdab2141726fc49ad6786

You should try to use a lean flat viewmodel
public class UserProfileViewModel
{
public string UserId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public int Age { get; set; }
}
and in your GET action
public ActionResult Edit(string id)
{
var model = _kletsContext.Users.FirstOrDefault(m => m.Id == id);
var profile = _kletsContext.Profiles.FirstOrDefault(m => m.UserId == model.Id);
if(model == null)
{
return RedirectToAction("Index");
}
var vm = new UserProfileViewModel
{
UserId = model.Id,
UserName = model.UserName,
Email = model.Email,
Age = profile.Age
};
return View(vm);
}
Your razor view will be strongly typed to this view model
#model YourNamespaceHere.UserProfileViewModel
#using(Html.BeginForm())
{
<label>UserName</label>
#Html.TextBoxFor(s=>s.UserName)
<label>Email</label>
#Html.TextBoxFor(s=>s.Email)
<label>Age</label>
#Html.TextBoxFor(s=>s.Age)
#Html.HiddenFor(s=>s.UserId)
<input type="submit" />
}
And in your HttpPost action, We will use this view model as the method parameter and the posted form will be converted to an object of this class by the default model binder.
[HttpPost]
public ActionResult Edit(UserProfileViewModel model)
{
if(ModelState.IsValid)
{
var u = _kletsContext.Users.FirstOrDefault(m => m.Id == model.UserId);
var p= _kletsContext.Profiles.FirstOrDefault(m => m.UserId == model.UserId);
//Set the new values
u.Email = model.Email;
if(p!=null)
{
p.Age=model.Age;
_kletsContext.Entry(p).State = EntityState.Modified;
}
else
{
// to do :Create a new profile record
}
_kletsContext.Entry(u).State = EntityState.Modified;
_kletsContext.SaveChanges();
// to redirect to some success page
}
return View(model);
}

Related

Edit in mvc4 didn't work

I want to edit this data in database and return new data
when i click on save button data doesn't change
Here is controller :
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(CustomPerformancePerformersModel customPerformancePerformersModel)
{
if (ModelState.IsValid)
{
int perfromanceId = Convert.ToInt32(TempData.Peek("CurrentPerformanceId"));
customPerformancePerformersModel.performanceObj = db.Performances.Where(x => x.PerformanceId == perfromanceId).FirstOrDefault();
db.Entry(customPerformancePerformersModel.performanceObj).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.EventId = new SelectList(db.Events, "EventId", "Name", customPerformancePerformersModel.performanceObj.EventId);
ViewBag.VenueId = new SelectList(db.Venues, "VenueId", "Name", customPerformancePerformersModel.performanceObj.VenueId);
ViewBag.Performers = new SelectList(db.PerformerPerformances, "Performers", "Name", customPerformancePerformersModel.performanceObj.PerformerPerformances);
return View(customPerformancePerformersModel.performanceObj);
}
and here is the html:
<div class="form-group">
#Html.LabelFor(model => model.performanceObj.IsVisible, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<div class="checkbox">
#Html.EditorFor(model => model.performanceObj.IsVisible)
#Html.ValidationMessageFor(model => model.performanceObj.IsVisible, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.performanceObj.IsFeatured, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<div class="checkbox">
#Html.EditorFor(model => model.performanceObj.IsFeatured)
#Html.ValidationMessageFor(model => model.performanceObj.IsFeatured, "", new { #class = "text-danger" })
</div>
</div>
Try the following:
if (ModelState.IsValid)
{
int perfromanceId = Convert.ToInt32(TempData.Peek("CurrentPerformanceId"));
// There is no need to use Where. FirstOrDefault has an overload using predicates.
var savedPerformance = db.Performances.FirstOrDefault(x => x.PerformanceId == perfromanceId);
// If the performance couldn't be found, then you could add the error to the model state and return it to the view.
if(savedPerformance == null)
return View(customPerformancePerformersModel.performanceObj);
// Update properties from performance in database with new performance.
savedPerformance.someProperty = customPerformancePerformersModel.performanceObj.someProperty;
db.Performances.Attach(savedPerformance);
db.Entry(savedPerformance ).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
Ideally your code will look something like the following:
public ActionResult Edit(int performanceId)
{
var model = db.Performances.FirstOrDefault(m => m.PerformanceId == performanceId);
return View(model);
}
[HttpPost] //[HttpPatch] is technically correct, but most people I see tend to use only GET and POST actions.
[ValidateAntiForgeryToken]
public ActionResult Edit(CustomPerformancePerformersModel model)
{
if (ModelState.IsValid)
{
db.Entry(model).State = EntityState.Modified;
db.SaveChanges();
}
}
You're retrieving the object from the database and tracking it in your GET action, modifying it using your form, then marking it as modified in your update action. This is strictly if you're using the MVC pattern, and will look different (see below) if you're using separate data and view models. You'll likely run into trouble with this approach if your view doesn't have fields (hidden or not) for all properties on your model.
Using separate data and view models, you'd have something like this:
public ActionResult Edit(int performanceId)
{
var performance = db.Performances.FirstOrDefault(m => m.PerformanceId == performanceId);
var model = new PerformanceViewModel(performance); //In this constructor, copy properties from your data model to your view model
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(PerformanceViewModel model)
{
var performance = db.Performances.FirstOrDefault(m => m.PerformanceId == model.PerformanceId);
model.Update(performance);
db.SaveChanges();
}
With a sample view model:
public class PerformanceViewModel
{
public PerformanceViewModel(CustomPerformanceePerformersModel model)
{
PerformanceId = model.performanceObj.PerformanceId;
IsVisible = model.performanceObj.IsVisible;
IsFeatured = model.performanceObj.IsFeatured;
}
public int PerformanceId { get; set; }
public bool IsVisible { get; set; }
public bool IsFeatured { get; set; }
public void Update(CustomPerformancePerformersModel model)
{
model.performanceObj.IsVisible = IsVisible;
model.performanceObj.IsFeatured = IsFeatured;
}
}
Here you're creating a separate object (view model) that holds only the necessary data for your view, then using the data from that object to update your data model. I prefer this because it takes the ability to effectively directly modify the database, and because you can do any necessary intermediate processing (casting strings to bools, et cetera) in the Update(Model) method.

C# MVC Dropdownlist not updating Model

I am having problem understanding as to why my model is not updating when I select a new value from my dropdownlist control?
Here is my model
public class UserViewModel
{
public Users users { get; set; }
public IEnumerable<SelectListItem> UserRoles { get; set; }
}
Controller
//GET
public ActionResult Edit(int id)
{
var vm = new UserViewModel();
vm.users = repository.GetById(id);
vm.UserRoles = db.UserRoles.Select(
x => new SelectListItem
{
Selected = true,
Text = x.UserRoleName,
Value = x.UserRoleID.ToString()
}
);
if (vm == null)
{
return HttpNotFound();
}
return View(vm);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(UserViewModel model)
{
if(ModelState.IsValid)
{
db.Entry(model).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View();
}
And finally my View
<div class="form-group">
<label class="control-label col-md-2">User Role</label>
<div class="col-md-10">
#Html.HiddenFor(model => model.users.UserRoleID)
#Html.DropDownListFor(model => model.UserRoles, (IList<SelectListItem>)ViewBag.UserRoles, "-- Select One --", new { #class = "form-control" })
</div>
</div>
I have stepped through the code and in the Collection can see UserRoles in the collection but I am not sure if I am passing the value correctly?
UPDATE
I have updated my POST method for updating the model
public ActionResult Edit(int id, UserViewModel model)
{
var user = repository.GetById(id);
if (ModelState.IsValid)
{
if (user != null)
{
user.Username = model.users.Username;
user.Forename = model.users.Forename;
user.Lastname = model.users.Lastname;
user.Email = model.users.Email;
user.Status = model.users.Status;
user.UserRoleID = Convert.ToInt32(model.UserRoles);
db.Entry(user).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
else
{
return View();
}
}
return View();
}
However on Submit it is giving me a Null reference exception on the dropdownlist as shown below? Now sure why?
(IList<SelectListItem>)ViewBag.UserRoles
you data exist in vm.UserRoles lists not in ViewBag.UserRoles but you are attaching list using Viewbag
ViewBag.UserRoles = db.UserRoles.Select(
x => new SelectListItem
{
Selected = true,
Text = x.UserRoleName,
Value = x.UserRoleID.ToString()
}
assign the list to the view model then you will get access to the list from the page
Finally solved the issue. Created a new property inside my ViewModel which will store the selected value for When posting back to Controller
public class UserViewModel
{
public Users users { get; set; }
public IEnumerable<SelectListItem> UserRoles { get; set; }
public string selectedRole { get; set; }
}
Made change to my View to include the new property
#Html.DropDownListFor(model => model.selectedRole, Model.UserRoles, "-- Select One --", new { #class = "form-control" })
and on Post I pass the selected value
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int id, UserViewModel model)
{
var user = repository.GetById(id);
if (ModelState.IsValid)
{
if (user != null)
{
user.Username = model.users.Username;
user.Forename = model.users.Forename;
user.Lastname = model.users.Lastname;
user.Email = model.users.Email;
user.Status = model.users.Status;
user.UserRoleID = Convert.ToInt32(model.selectedRole);
db.Entry(user).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
else
{
return View();
}
return View();
}
I think the wrong code is in the View:
<div class="col-md-10">
#Html.HiddenFor(model => model.users.UserRoleID)
#Html.DropDownListFor(model => model.UserRoles (IList<SelectListItem>)ViewBag.UserRoles, "-- Select One --", new { #class = "form-control" })
</div>
The next code should work:
<div class="col-md-10">
#Html.DropDownListFor(model => model.users.UserRolesID, new SelectList(Model.UserRoles, "Id", "Name"), new { #class = "form-control" })
</div>
where there is a direct binding between model.users.UserRolesID and the list containing user roles (I suppose that UserRoles is done as id,description)

Why i failed to get DropDownList value on submit event

In my MVC4 project I failed to get my DropDownList data on edit controller.
My UI syntax is bellow:
<div class="form-group">
#Html.HiddenFor(model => model.School.SchoolID)
#Html.LabelFor(model => model.School.SchoolName, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.School.SchoolName)
#Html.ValidationMessageFor(model => model.School.SchoolName)
</div>
</div>
<div class="form-group">
#Html.HiddenFor(model => model.StudentCLass.ID)
#Html.LabelFor(model => model.StudentCLass.ClassName, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.StudentCLass.ID, #ViewBag.StudentCLassList as SelectList,"Select Class")
#Html.ValidationMessageFor(model => model.StudentCLass.ID)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
To fill the DropDownList I use bellow syntax:
public ActionResult Edit(int Id)
{
using (DB = new StudentContext())
{
var result = DB.Students.FirstOrDefault(c => c.ID == Id);
ViewBag.StudentCLassList = new SelectList(DB.StudentClasses
.Select(sc => new ViewModelClass
{
ID = sc.ID,
ClassName = sc.ClassName
}).ToList(), "ID", "ClassName");
return View(StudentInfo(result));
}
}
After click the submit button I can not get DropDownList value on my controller action.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(ViewModel.ViewModelStudents student)
{
var tempResult = student.StudentCLass.ID;
//return RedirectToAction("Index");
// return View(student);
}
Model structure
public partial class StudentClass
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public StudentClass()
{
Students = new HashSet<Student>();
}
public int ID { get; set; }
[StringLength(100)]
public string ClassName { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Student> Students { get; set; }
}
MVC Doen't post the DropDown list back to the Controller, You will have to populate dropdown list again in POST method:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(ViewModel.ViewModelStudents student)
{
var tempResult = student.StudentCLass.ID;
ViewBag.StudentCLassList = new SelectList(DB.StudentClasses
.Select(sc => new ViewModelClass
{
ID = sc.ID,
ClassName = sc.ClassName
}).ToList(), "ID", "ClassName");
return RedirectToAction("Index");
}
You could write the Dropdown list code in a function, if you don't want to read this dropdown list from DB evertime you can save it to Session[]:
public void PopulateDropDownList(){
var items = Session["MyDropDown"] != null ? (SelectList)Session["MyDropDown"] : null;
if(items ! null) {ViewBag.StudentCLassList; return;}
items = new SelectList(DB.StudentClasses
.Select(sc => new ViewModelClass
{
ID = sc.ID,
ClassName = sc.ClassName
}).ToList(), "ID", "ClassName");
Session["MyDropDown"] = ViewBag.StudentCLassList = items;
}
Note: If you save the DropDown list in Session, you don't have write it to ViewBag, but you can access it directly in View.
And call this method in Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(ViewModel.ViewModelStudents student)
{
var tempResult = student.StudentCLass.ID;
PopulateDropDownList();
return RedirectToAction("Index");
}
EDIT
I don't understand you are saying that you want the DropDown to be selected but you are Redirecting to `Index'.
If you do:
return View(student);
Instead of
return RedirectToAction("Index");
return RedirectToAction("Index"); will redirect you to Index page, refreshing your webpage.
EDIT 2:
I just noticed you have
#Html.HiddenFor(model => model.StudentCLass.ID)
MVC is posting the Value from this Hidden Back to the Controller. Try removing this,
The thing is that you have two controls with the same id
#Html.DropDownListFor(model => model.StudentCLass.ID
AND
#Html.HiddenFor(model => model.StudentCLass.ID)
I think you want something like this:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(ViewModel.ViewModelStudents student)
{
if (ModelState.IsValid)
{
// save changes and redirect
return RedirectToAction("Index");
}
else
{
using (DB = new StudentContext())
{
ViewBag.StudentCLassList = new SelectList(DB.StudentClasses.ToList(), "ID", "ClassName");
}
return View(student);
}
}
The framework will take care of preserving selected values across requests.

How authenticate a user using ASP.NET MVC?

I'm creating a web app with .net using a login for users. After registration the users' data is stored in my local SQL database and the user must be able to log in using these.
Currently I've managed to register users and they can log in when I use a hard coded password and user name but I can't figure out how I can check the user's given credentials and the ones in the database to check if these match.
I did extensive research but didn't find a proper solution but surprisingly could not come up with a working solution.
Here is my current code:
My users model:
namespace Models
{
public class Users
{
public int Id { get; set; }
public string userName { get; set; }
public string userPassword { get; set; }
public string userEmail { get; set; }
public string userCompany { get; set; }
public class UsersDBContext : DbContext
{
public DbSet<Users> Users { get; set; }
}
}
}
My controller
namespace Eindwerk.Controllers
{
public class UsersController : Controller
{
private Users.UsersDBContext db = new Users.UsersDBContext();
// GET: /Users/
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(Users users)
{
if (ModelState.IsValid)
{
if(users.userName == "NAAM" && users.userPassword == "WACHTWOORD")
{
FormsAuthentication.SetAuthCookie(users.userName, false);
return RedirectToAction("", "Home");
}
{
ModelState.AddModelError("", "Invalid username and/or password");
}
}
return View();
}
My View
#model Eindwerk.Models.Users
#{
ViewBag.Title = "Login";
Layout = "../_Login.cshtml";
}
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<div class="panel-body">
<fieldset>
<div class="editor-field">
#Html.TextBoxFor(model => model.userName, new { #class = "form-control", Value="Username"})
#Html.ValidationMessageFor(model => model.userName)
</div>
<br />
<div class="editor-field">
#Html.TextBoxFor(model => model.userPassword, new { #class = "form-control", Value= "Password" })
#Html.ValidationMessageFor(model => model.userPassword)
</div>
<br />
<br />
<p>
<input type="submit" value="SIGN IN" class="btn btn-outline btn-primary btn-lg btn-block"/>
</p>
</fieldset>
</div>
}
So instead of using if(users.userName == "NAAM" && users.userPassword == "WACHTWOORD")
I want to check properly if the user is valid and registered in my database or not so I can grant or deny access.
Anybody a proper solution? Or link to decent documentation in order to resolve this issue?
Any help is really appreciated!
You need to reference the db.Users collection.
[HttpPost]
public ActionResult Index(Users users)
{
if (ModelState.IsValid)
{
var hash = GenerateHash(users.userPassword,Settings.Default.salt);
var authUser = db.Users.FirstOrDefault(row => row.userName == users.userName && row.userPassword == hash )
if ( authUser != null )
{
Session["role"] = authUser.Role;
FormsAuthentication.SetAuthCookie(users.userName, false);
return RedirectToAction("", "Home");
}
else
{
ModelState.AddModelError("", "Invalid username and/or password");
}
}
return View();
}
private static string GenerateHash(string value, string salt)
{
byte[] data = System.Text.Encoding.ASCII.GetBytes(salt + value);
data = System.Security.Cryptography.MD5.Create().ComputeHash(data);
return Convert.ToBase64String(data);
}
See MD5 hash with salt for keeping password in DB in C# for a more indepth discussion of salting and hashing. Note that I'm assuming you have a Settings class with the salt value. You can replace this with another mechanism for retrieving the salt.

MVC4 Validate Item Selected In DropDownList

I have a page to create objects, and in this I have a DropDownList. If I select an item from the list my page will save correctly, however if I don't select an item it looks like it fails on a postback as the objects will be null.
What I want is to try and validate whether the user has selected an item (default is "Please Select...").
I have code that will check and see in the controller if the item is null, but it's how do I then display a message? Keeping all other details if they exist.
public ActionResult Create(int objectId = 0)
{
var resultModel = new MyObjectModel();
resultModel.AllObjects = new SelectList(_system.GetAllObjects(objectId));
// GetAllObjects juts returns a list of items for the drop down.
return View(resultModel);
}
[HttpPost]
public ActionResult Create(int? objectId, FormCollection collection)
{
try
{
int objectIdNotNull = 0;
if (objectId > 1)
{
objectIdNotNull = (int) objectId;
}
string objectName = collection["Name"];
int objectTypeSelectedResult = 1;
int.TryParse(collection["dllList"], out objectTypeSelectedResult);
if (!Convert.ToBoolean(objectTypeSelectedResult))
{
// So here I have discovered nothing has been selected, and I want to alert the user
return RedirectToAction("Create",
new {ObjectId = objectIdNotNull, error = "Please select an Object Type"});
}
....
return RedirectToAction(...)
}
catch
{
return View();
}
}
The above code just goes to the Create page but doesn't display an error. In my View for Create I have the following line which I assumed would display any errors:
#ViewData["error"]
Additional code
Model:
using System.Collections.Generic;
using System.Web.Mvc;
using System.ComponentModel.DataAnnotations;
namespace MyNameSpace
{
public class MyObjectModel
{
[Required(ErrorMessage = "Please select an Object Type")]
public SelectList AllObjects { get; set; } // I populate the drop down with this list
}
}
View:
#model MyNameSpace.MyObjectModel
#{
ViewBag.Title = "Create";
}
<h2>Create </h2>
<p class="text-error">#ViewData["Message"]</p>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"> </script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"> </script>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<div class="editor-label">
#Html.LabelFor(model => model.MyObject.Name)
</div>
<div class="editor-field">
#Html.TextBoxFor(model=>model.MyObjectType.Name, new {style="width: 750px"})
#Html.ValidationMessageFor(model => model.MyObjectType.Name)
</div>
<div>
<label for="ddlList">Choose Type</label>
#if (#Model != null)
{
#Html.DropDownList("ddlList", Model.AllObjects, "Please Select...")
#Html.ValidationMessageFor(model => model.AllObjects, "An object must be selected", new { #class = "redText"})
}
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
You are validating the SelectList which is wrong
[Required(ErrorMessage = "An object must be selected")]
public SelectList AllObjects { get; set; }
Your Model should be
[Required(ErrorMessage = "Please select an Object Type")]
public int ObjectId { get; set; }
public string ObjectName { get; set; }
Your Controller(no need for form collection thats the whole point of MVC)
public ActionResult Create(int Id = 0)
{
MyObjectModel resultModel = new MyObjectModel();
var ObjectResultList = _system.GetAllObjects(Id);
var ObjectSelectList = new SelectList(ObjectResultList, "id", "Name");
ViewBag.ObjectList = ObjectSelectList;
return View(resultModel);
}
Your Post controller:
[HttpPost]
public ActionResult Create(MyObjectModel o)
{
try
{
if (ModelState.IsValid)
{
//It's valid , your code here!
return RedirectToAction("ObjectCreated", new { id = o.objectId });
}
else
{
var errors = ModelState
.Where(x => x.Value.Errors.Count > 0)
.Select(x => new { x.Key, x.Value.Errors })
.ToArray();
}
}
}
catch (Exception ex)
{
Response.Write(ex.InnerException.Message);
}
//If we get here it means the model is not valid, We're in trouble
//then redisplay the view repopulate the dropdown
var ObjectResultList = _system.GetAllObjects(objectId);
var ObjectSelectList = new SelectList(ObjectResultList, "id", "value");
ViewBag.ObjectList = ObjectSelectList;
return View(o);
}
Your View should be strongly Typed
<div class="editor-label">
#Html.LabelFor(model => model.ObjectId)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.ObjectId,
(IEnumerable<SelectListItem>)ViewBag.ObjectList, "-- Select One Object --")
#Html.ValidationMessageFor(model => model.ObjectId)
</div>

Categories

Resources