ASP.NET MVC Custom Validation Error Message - c#

So I have some custom validation implemented to prevent similar records being entered into the db. The only problem I have is that it is not presenting the user an error/validation message but rather an error page.
Where am I going wrong? Or how do I correctly implement this?
Validation:
public class ValidSimilarRequests : ValidationAttribute
{
private LotusWorksEntities db = new LotusWorksEntities();
protected override ValidationResult
IsValid(object value, ValidationContext validationContext)
{
var model = (Models.HolidayRequestForm)validationContext.ObjectInstance;
int empID = Convert.ToInt32(model.EmployeeID);
DateTime _startdate = Convert.ToDateTime(model.StartDate);
DateTime _finishdate = Convert.ToDateTime(model.FinishDate);
var holidayexist = db.HolidayRequestForms.Any( x => x.EmployeeID==empID && x.StartDate <= _startdate && x.FinishDate >= _finishdate );
if (holidayexist)
{
return new ValidationResult
("A holiday Request for this date range has already been requested");
}
else
{
return ValidationResult.Success;
}
}
}
Model:
public partial class HolidayRequestForm
{
public int RequestID { get; set; }
[ValidSimilarRequests(ErrorMessage =
"A holiday Request for this date range has already been requested")]
public int EmployeeID { get; set; }
[ValidSameWeek(ErrorMessage =
"Holiday Request Must be made on a weekly Period")]
[DisplayFormat(DataFormatString = "{0:dd/MMM/yy}", ApplyFormatInEditMode = true)]
public System.DateTime StartDate { get; set; }
[ValidStartFinishDate(ErrorMessage =
"Finish Date can not be Greater than Start date.")]
[DisplayFormat(DataFormatString = "{0:dd/MMM/yy}", ApplyFormatInEditMode = true)]
public System.DateTime FinishDate { get; set; }
[Range(0.0001, int.MaxValue, ErrorMessage = "Hours Requested must be greater than zero. ")]
public decimal HoursTaken { get; set; }
public string Comments { get; set; }
public int YearCreated { get; set; }
public int MonthCreated { get; set; }
public int DayCreated { get; set; }
public Nullable<int> YearOfHoliday { get; set; }
public Nullable<bool> Approved { get; set; }
public string SubmittedBy { get; set; }
public string ApprovedBy { get; set; }
public Nullable<int> WorkWeek { get; set; }
public Nullable<int> MonthOfHoliday { get; set; }
public virtual Employee Employee { get; set; }
}
The Error Page shows:
Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
Controller:
[Authorize(Roles = "Admin,User,SuperUser")]
public ActionResult Create()
{
ViewBag.EmployeeID = new SelectList(db.Employees, "EmployeeID", "FullName");
return View();
string name = Session["Name"].ToString();
var EmployeeIDCatch = db.Employees.Where(s => s.Email.Equals(name)).Select(s => s.EmployeeID);
}
// POST: HolidayRequestForms/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "RequestID,StartDate,FinishDate,HoursTaken,Comments,YearCreated,MonthCreated,DayCreated,YearOfHoliday,Approved,SubmittedBy,ApprovedBy")] HolidayRequestForm holidayRequestForm)
{
if (ModelState.IsValid)
{
if (Session["Name"] == null)
{
TempData["msg"] = "Your Session Expired - Please Login";
return RedirectToAction("Login", "Account");
}
string name = Session["Name"].ToString();
var employeeID = db.Employees.Where(s => s.Email.Equals(name)).Select(s => s.EmployeeID).FirstOrDefault();
holidayRequestForm.EmployeeID = employeeID;
var submittedby = db.Employees.Where(s => s.Email.Equals(name)).Select(s => s.Email).FirstOrDefault();
holidayRequestForm.SubmittedBy = submittedby;
//Saves changes and begins Email Actions
db.HolidayRequestForms.Add(holidayRequestForm);
db.SaveChanges();
SendMailToAreaManager();
SendMailToManager();
SendMailToAdmin();
return RedirectToAction("Index", "Calendar");
}
ViewBag.EmployeeID = new SelectList(db.Employees, "EmployeeID", "FullName", holidayRequestForm.EmployeeID);
return View(holidayRequestForm);
}
VIEW:
#model HolidayTracker.Models.HolidayRequestForm
<div>
<div class="col-md-12">
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<h2 align="center">Holiday Request Form</h2>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.StartDate, "Start Date", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.StartDate, "Start Date", new { htmlAttributes = new { #class = "form-control", #style = "width:400px", autocomplete = "off" } })
#Html.ValidationMessageFor(model => model.StartDate, "", new { #class = "text-warning" })
<p id="warning" style="color:orange"></p>
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.FinishDate, "Finish Date", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.FinishDate, new { htmlAttributes = new { #class = "form-control",#style = "width:400px", autocomplete = "off" } })
#Html.ValidationMessageFor(model => model.FinishDate, "", new { #class = "text-danger" })
<p id="warningFD" style="color:orange"></p>
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.HoursTaken, "Hours Requested", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.HoursTaken, new { htmlAttributes = new { #class = "form-control", #style = "width:400px" } })
#Html.ValidationMessageFor(model => model.HoursTaken, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Comments, htmlAttributes: new { #class = "control-label col-md-2"})
<div class="col-md-10">
#Html.TextAreaFor(
model => model.Comments,
new { placeholder = "Enter Dates and how many Hours per Date Here. ", style = "width: 400px; height: 200px;" })
#Html.ValidationMessageFor(model => model.Comments, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Submit" class="btn btn-warning" />
</div>
</div>
}
</div>
</div>
</div>

Related

The model item passed into the dictionary is of type 'koelkast.Models.ProductsModel

when i tried to submit my edit form i get this issue and i don't know why
productsController edit get
public async Task<ActionResult> Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
//ProductsModel productsModel = await db.ProductsModels.FindAsync(id);
var products = await db.ProductsModels.Include(a =>a.categorieId).Include(a => a.users).FirstAsync(a => a.id == id);
if (products == null)
{
return HttpNotFound();
}
var selectedOwnerId = products.users?.Id ?? string.Empty;
var users = db.Users.Select(userItem => new SelectListItem
{
Text = userItem.Email,
Value = userItem.Id,
Selected = userItem.Id == selectedOwnerId
}).ToSafeReadOnlyCollection();
var selectedCategoryId = products.categorieId.id;
var productCategories = db.ProductCategoriesModels
.Select(a => new SelectListItem
{
Value = a.id.ToString(),
Text = a.name,
Selected = a.id == selectedCategoryId
}).ToSafeReadOnlyCollection();
var viewmodel = new productCreatEditViewModel()
{
Products = products,
productCategories = productCategories,
users = users
};
//ViewBag.users = userList;
//ViewBag.productcategorieId = new SelectList(db.ProductCategoriesModels, "id", "Name", productsModel.productcategorieId);
return View(viewmodel);
}
productsController edit post
// POST: Products/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to, for
// more details see https://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include = "id,name,calorie,price,productcategorieId,userId")] ProductsModel productsModel)
{
if (ModelState.IsValid)
{
db.Entry(productsModel).State = EntityState.Modified;
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
ViewBag.productcategorieId = new SelectList(db.ProductCategoriesModels, "id", "Name", productsModel.productcategorieId);
return View(productsModel);
}
edit view
#model koelkast.ViewModels.productCreatEditViewModel
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>ProductsModel</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Products.id)
<div class="form-group">
#Html.LabelFor(model => model.Products.name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Products.name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Products.name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Products.calorie, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Products.calorie, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Products.calorie, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Products.price, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Products.price, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Products.price, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Products.categorieId.name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => Model.Products.categorieId.id,Model.productCategories, new { Name = "productCategoriesId", #class ="form-control"})
#Html.ValidationMessageFor(model => model.productCategories, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Products.users.Email, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => Model.Products.users.Id,Model.users, new {Name = "UserId", #class = "form-control" })
#Html.ValidationMessageFor(model => model.Products.users.Email, "", new { #class = "text-danger" })
</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>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
ViewModel
The model item passed into the dictionary is of type 'koelkast.Models.ProductsModel', but this dictionary requires a model item of type 'koelkast.ViewModels.productCreatEditViewModel'.
namespace koelkast.ViewModels
{
public class productCreatEditViewModel
{
[Required]
public ProductsModel Products { get; set; }
[Required]
public ICollection<SelectListItem> productCategories { get; set; }
[Required]
public ICollection<SelectListItem> users { get; set; }
}
}
productModel
namespace koelkast.Models
{
public class ProductsModel
{
[Key]
public int id { get; set; }
[Required,Display(Name = "name")]
public string name { get; set; }
//display name is de naam die hij gaat laten zien als nnaam in je view
[Required, Display(Name = "calorie")]
public int calorie { get; set; }
[Required, Display(Name = "price")]
public float price { get; set; }
[Display(Name = "categories")]
//hier zet je die foreing key
//zoals je kunt zien roep ik alleen de model aan
public int? productcategorieId { get; set; }
[ForeignKey("productcategorieId")]
public virtual ProductCategoriesModel categorieId { get; set; }
//je zegt hier dus dat dit de Id is(userId)
//van applicationUser table users
public string UserId { get; set; }
[ForeignKey("UserId")]
public virtual ApplicationUser users { get; set; }
}
}
https://i.stack.imgur.com/Qaq67.png
you have a bug here, fix it
public async Task<ActionResult> Edit(ProductsModel productsModel)
{
}
you have 2 choices
1.Change ProductsModel to ProductCreatEditViewModel
or
Return ProductCreatEditViewModel as model
var viewmodel = new productCreatEditViewModel()
{
Products = productsModel,
productCategories = productCategories,
users = users
};
return View(viewModel);

ASP.NET MVC Viewmodel Edit returns empty fields (item is not identified by id)

I previously reveived some help here but some problems with the "Edit" arouse now. Currently stuck at HttpGet, HttpPost is yet to come.
In short, whenever I click on an existing item to edit, instead of the expected values, it returns an empty form, as seen here. (You might think that the reason for this is because I use the same exact view for Create and Edit but unfortunately this isn't, even when I used to separate ones, had the same result.)
Town.cs
using System.Collections.Generic;
namespace City.Models
{
public class Town
{
public Town()
{
Streets = new List<Street>();
}
public int TownId { get; set; }
public string TownName { get; set; }
public virtual ICollection<Street> Streets { get; set; }
}
}
Street.cs
using System.Collections.Generic;
namespace City.Models
{
public class Street
{
public Street()
{
Houses = new List<House>();
}
public int StreetId { get; set; }
public string StreetName { get; set; }
public virtual ICollection<House> Houses { get; set; }
}
}
House.cs
using System.Collections.Generic;
namespace City.Models
{
public class House
{
public House()
{
Floors = new List<Floor>();
}
public int HouseId { get; set; }
public string HouseName { get; set; }
public ICollection<Floor> Floors { get; set; }
}
}
Floor.cs
using System.Collections.Generic;
namespace City.Models
{
public class Floor
{
public Floor()
{
FireExtinguishers = new List<FireExtinguisher>();
}
public int FloorId { get; set; }
public int FloorNumber { get; set; }
public virtual ICollection<FireExtinguisher> FireExtinguishers { get; set; }
}
}
FireExtinguisher.cs
using System.ComponentModel;
namespace City.Models
{
public class FireExtinguisher
{
public int FireExtinguisherId { get; set; }
[DisplayName("Fire Extinguisher")]
public string FireExtinguisherName { get; set; }
public int FloorId { get; set; }
public int HouseId { get; set; }
public int StreetId { get; set; }
public int TownId { get; set; }
public Floor Floor { get; set; }
public House House { get; set; }
public Street Street { get; set; }
public Town Town { get; set; }
}
}
MyViewModel.cs
using System.Collections.Generic;
namespace City.Models
{
public class MyViewModel
{
public IEnumerable<Town> TownId { get; set; }
public IEnumerable<Street> StreetId { get; set; }
public IEnumerable<House> HouseId { get; set; }
public IEnumerable<Floor> FloorId { get; set; }
public FireExtinguisher FireExtinguisher { get; set; }
public string FireExtinguisherName { get; set; }
}
}
Create/Edit view
#model City.Models.MyViewModel
<h2>Add new or edit existing FE</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="col-md-12">
<div class="form-group">
#Html.LabelFor(model => model.TownId, "Town", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.TownId, new SelectList(Model.TownId, "TownId", "TownName"), "Choose Town", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.TownId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.StreetId, "Street", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.StreetId, new SelectList(Model.StreetId, "StreetId", "StreetName"), "Choose Street", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.StreetId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.HouseId, "House", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.HouseId, new SelectList(Model.HouseId, "HouseId", "HouseName"), "Choose House", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.HouseId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.FloorId, "Floor", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.FloorId, new SelectList(Model.FloorId, "FloorId", "FloorNumber"), "Choose Floor", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.FloorId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.FireExtinguisherName, "Fire Extinguisher", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.FireExtinguisherName, new { #class = "form-control", Value = "" })
#Html.ValidationMessageFor(model => model.FireExtinguisherName, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div>
<div>
<input type="submit" value="Do it" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Homecontroller.cs
using System;
using System.Linq;
using System.Net;
using System.Web.Mvc;
using City.Models;
namespace City.Controllers
{
public class HomeController : Controller
{
private ApplicationDbContext db;
private MyViewModel viewModel;
public HomeController()
{
db = new ApplicationDbContext();
var town = db.Towns.ToList();
var street = db.Streets.ToList();
var house = db.Houses.ToList();
var floor = db.Floors.ToList();
viewModel = new MyViewModel()
{
TownId = town,
StreetId = street,
HouseId = house,
FloorId = floor
};
}
public ActionResult Index()
{
return View(db.FireExtinguishers.ToList());
}
[HttpGet]
public ActionResult Create()
{
return View(viewModel);
}
[HttpPost]
public ActionResult Create(FireExtinguisher fe)
{
if (ModelState.IsValid)
{
db.FireExtinguishers.Add(fe);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(viewModel);
}
[HttpGet]
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var fe = db.FireExtinguishers.Find(id);
var town = db.Towns.ToList();
var street = db.Streets.ToList();
var house = db.Houses.ToList();
var floor = db.Floors.ToList();
viewModel = new MyViewModel()
{
FireExtinguisher = fe,
TownId = town,
StreetId = street,
HouseId = house,
FloorId = floor
};
return View("Create", viewModel);
}
[HttpPost]
public ActionResult Edit()
{
throw new NotImplementedException();
}
}
}
Any help would be appreciated, thank you
Personally I like to use View- and Post-Models for that as a clear statement, what is send to the View (ViewModel) and what is send back via Post. As a naming convention the name of the model class Takes the controller name and the action name.
I start with the PostModel, which would be in your case
public class HomeEditPostModel
{
[Required]
[MaxLength(50)]
public string Name { get; set; }
[Required]
public int TownId { get; set; }
[Required]
public int StreetId { get; set; }
[Required]
public int HouseId { get; set; }
[Required]
public int FloorId { get; set; }
}
Now for the view we need some collections for the dropdown fields, where we can select from
public class SelectionItem<TKey>
{
public TKey Key { get; set; }
public string DisplayName { get; set; }
}
public class HomeEditViewModel : HomeEditPostModel
{
public IEnumerable<SelectionItem<int>> Town { get; set; }
public IEnumerable<SelectionItem<int>> Street { get; set; }
public IEnumerable<SelectionItem<int>> House { get; set; }
public IEnumerable<SelectionItem<int>> Floor { get; set; }
}
Now the view
#model WebApplication6.Models.HomeEditViewModel
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>HomeEditSubmitModel</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.TownId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.TownId, new SelectList(Model.Town, "Key", "DisplayName"), "Choose Town", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.TownId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.StreetId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.StreetId, new SelectList(Model.Street, "Key", "DisplayName"), "Choose Street", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.StreetId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.HouseId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.HouseId, new SelectList(Model.House, "Key", "DisplayName"), "Choose House", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.HouseId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.FloorId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.FloorId, new SelectList(Model.Floor, "Key", "DisplayName"), "Choose Floor", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.FloorId, "", new { #class = "text-danger" })
</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>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
and finally the controller (only relevant parts)
public class HomeController : Controller
{
[HttpGet]
public ActionResult Edit(int id)
{
// just some fake data for demonstration
var model = new HomeEditViewModel
{
Name = "Name",
Floor = Enumerable.Range(1, 10).Select(e => new SelectionItem<int> { Key = e, DisplayName = $"Floor {e}" }),
Street = Enumerable.Range(1, 10).Select(e => new SelectionItem<int> { Key = e, DisplayName = $"Street {e}" }),
House = Enumerable.Range(1, 10).Select(e => new SelectionItem<int> { Key = e, DisplayName = $"House {e}" }),
Town = Enumerable.Range(1, 10).Select(e => new SelectionItem<int> { Key = e, DisplayName = $"Town {e}" }),
FloorId = 3,
StreetId = 4,
HouseId = 5,
TownId = 6,
};
return View(model);
}
[HttpPost]
public ActionResult Edit(int id, HomeEditPostModel model)
{
// needs to save the data here
return RedirectToAction(nameof(Index));
}
}

CheckBox lists in ASP.NET MVC

I am working on small ticket system for operations maintenance services comp.
Customer open a ticket and gives general information like location,
category description etc.
After technician fix the problem he must give sum details of the
problem from predefined set, use later for statistical reports.
My problem the repair method unable to post updated values to database plus nothing added to Defects_List table.
Note: I used this tutorial as guide
Models:
public partial class Tickets
{
public Tickets()
{
this.DefectsList = new HashSet<Defects_List>();
}
[Key]
[Display(Name = "Ticket Id")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Ticket_Id { get; set; }
[Display(Name = "Project")]
public int Project_Id { get; set; }
[Display(Name = "Issue Date")]
[DataType(DataType.Date)]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
public DateTime Issue_Date { get; set; }
[Display(Name = "Category")]
[DisplayFormat(NullDisplayText = "[Not set]")]
public int Category_Id { get; set; }
//Other Properties Removed for clarity
public virtual Business_Category businessCategories { get; set; }
public virtual ICollection<Defects_List> DefectsList { get; set; }
}
public partial class Business_Category
{
public Business_Categories()
{
this.Tickets = new HashSet<Tickets>();
this.Malfunctions = new HashSet<Malfunctions>();
}
[Key]
[Display(Name="Category Id")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Category_Id { get; set; }
[Display(Name = "Name (eng)")]
[StringLength(60, ErrorMessage = "Name cannot be longer than 60 characters.")]
public string Category_Name { get; set; }
public virtual ICollection<Tickets> Tickets { get; set; }
public virtual ICollection<Manufacturers> Manufacturers { get; set; }
public virtual ICollection<Malfunctions> Malfunctions { get; set; }
}
public partial class Defects_List
{
[Key, Column(Order = 0)]
[Display(Name = "Ticket Id")]
public int Ticket_Id { get; set; }
[Key, Column(Order = 1)]
[Display(Name = "Malfunction Id")]
public int Malfunction_Id { get; set; }
[StringLength(125, ErrorMessage = "Other cannot be longer than 125 characters.")]
public string Other { get; set; }
public virtual ICollection<Tickets> Tickets { get; set; }
public virtual Malfunctions Malfunctions { get; set; }
}
Controller:
// GET: /Tickets/Edit/5
public ActionResult Repair(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var tickets = db.nt_Tickets
.Include(t => t.DefectsList).Where(t => t.Ticket_Id == id)
.Single();
if (tickets == null)
{
return HttpNotFound();
}
PopulateSelectedDefects(tickets);
//Other codes Removed for clarity
return View(tickets);
}
private void PopulateSelectedDefects(nt_Tickets Tickets)
{
int categoryId;
if (Tickets.Category_Id == 2)
categoryId = 1;
else categoryId = Tickets.Category_Id;
var allDefects = (from m in db.sys_Malfunctions
where m.Category_Id == categoryId
select m).ToList();
var ticketDefects = new HashSet<int>(Tickets.DefectsList.Select(t => t.Malfunction_Id));
var viewModel = new List<TicketDefectsViewModel>();
foreach (var defect in allDefects)
{
viewModel.Add(new TicketDefectsViewModel
{
Malfunction_Id = defect.Malfunction_Id,
Malfunction_Name_e = defect.Malfunction_Name_e,
IsSelected = ticketDefects.Contains(defect.Malfunction_Id)
});
}
ViewBag.Defects = viewModel;
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Repair(int? id, string[] selectedDefects)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var ticketToUpdate = db.nt_Tickets
.Include(i => i.DefectsList)
.Where(i => i.Ticket_Id == id)
.Single();
if (TryUpdateModel(ticketToUpdate, "",
new string[] { Ticket_Id,Project_Id,Category_Id,Subject,Workshop_Id,Requested_By,Requestor_Mobile, Requestor_eMail,Location_Id,Ticket_Status,Periority_Id,Assigned_To,Description,Issue_Date,Created_By,Created_Date,Updated_By,Updated_Date" }))
{
ticketToUpdate.Updated_By = User.Identity.Name;
ticketToUpdate.Updated_Date = DateTime.UtcNow;
db.Entry(ticketToUpdate).State = EntityState.Modified;
SetTicketDefects(selectedDefects, ticketToUpdate);
try
{
db.SaveChanges();
return RedirectToAction("Index");
}
catch (DbEntityValidationException dbEx)
{
Exception raise = dbEx;
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
string message = string.Format("{0}:{1}",
validationErrors.Entry.Entity.ToString(),
validationError.ErrorMessage);
if (!string.IsNullOrEmpty(message))
ViewBag.errorMessage = message;
}
return View("Error");
}
throw raise;
}
}
PopulateSelectedDefects(ticketToUpdate);
return View("Index");
}
private void SetTicketDefects(string[] selectedDefects, Tickets ticketToUpdate)
{
if (selectedDefects == null)
{
ticketToUpdate.DefectsList = new List<Defects_List>();
return;
}
var selectedDefectsHS = new HashSet<string>(selectedDefects);
var tcketDefects = new HashSet<int>
(ticketToUpdate.DefectsList.Select(c => c.Ticket_Id));
foreach (var defect in db.DefectsLists)
{
if (selectedDefectsHS.Contains(defect.Malfunction_Id.ToString()))
{
if (!tcketDefects.Contains(defect.Malfunction_Id))
{
ticketToUpdate.DefectsList.Add(defect);
}
}
else
{
if (tcketDefects.Contains(defect.Malfunction_Id))
{
ticketToUpdate.DefectsList.Remove(defect);
}
}
}
}
db.DefectsLists here [foreach (var defect in db.DefectsLists)] always empty.
Repair View:
#using (Html.BeginForm("Repair", "Tickets", FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
#*#using (Html.BeginForm())*#
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.Ticket_Id)
#Html.HiddenFor(model => model.Issue_Date)
#Html.HiddenFor(model => model.Created_By)
#Html.HiddenFor(model => model.Project_Id)
#Html.HiddenFor(model => model.Created_Date)
<div class="form-group">
#Html.Label("Ticket_Id", new { #class = "control-label col-md-2" })
<div class="col-md-1 " style="margin-top:7px">
#Html.DisplayFor(model => model.Ticket_Id)
</div>
#Html.LabelFor(model => model.Issue_Date, new { #class = "control-label col-md-2" })
<div class="col-md-2" style="margin-top:7px">
#Html.DisplayFor(model => model.Issue_Date, new { #class = "form-control" })
</div>
#Html.LabelFor(model => model.Category_Id, new { #class = "control-label col-md-2" })
<div class="col-md-3" style="margin-top:7px">
#Html.DropDownList("Category_Id", null, new { #class = "form-control", #disabled = "disabled" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Subject, new { #class = "control-label col-md-2" })
<div class="col-md-10" style="margin-top:7px">
#Html.DisplayFor(model => model.Subject, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Description, new { #class = "control-label col-md-2" })
<div class="col-md-10" style="margin-top:7px">
#Html.DisplayFor(model => model.Description, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Requested_By, new { #class = "control-label col-md-2" })
<div class="col-md-5" style="margin-top:7px">
#Html.DisplayFor(model => model.Requested_By, new { #class = "form-control" })
</div>
#Html.LabelFor(model => model.Requestor_Mobile, new { #class = "control-label col-md-2" })
<div class="col-md-3" style="margin-top:7px">
#Html.DisplayFor(model => model.Requestor_Mobile, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Requestor_eMail, new { #class = "control-label col-md-2" })
<div class="col-md-10" style="margin-top:7px">
#Html.DisplayFor(model => model.Requestor_eMail, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Ticket_Status, new { #class = "control-label col-md-2" })
<div class="col-md-4" style="margin-top:7px">
#Html.EnumDropDownListFor(model => model.Ticket_Status, new { #class = "form-control" })
</div>
#Html.LabelFor(model => model.Periority_Id, new { #class = "control-label col-md-2" })
<div class="col-md-4" style="margin-top:7px">
#Html.EnumDropDownListFor(model => model.Periority_Id, new { #class = "form-control", #disabled = "disabled" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Location_Id, new { #class = "control-label col-md-2" })
<div class="col-md-4">
#Html.DropDownList("Location_Id", null, new { #class = "form-control", #disabled = "disabled" })
#Html.ValidationMessageFor(model => model.Location_Id)
</div>
#Html.LabelFor(model => model.Assigned_To, new { #class = "control-label col-md-2" })
<div class="col-md-4">
#Html.DropDownList("Assigned_To", null, new { #class = "form-control", #disabled = "disabled" })
</div>
</div>
<div class="form-group">
<div class="col-md-10">
#Html.Label("Defects")
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<table class="table table-hover">
<tr>
#{
int cnt = 0;
List<MaintCare.ViewModels.TicketDefectsViewModel> defects = ViewBag.Defects;
foreach (var defect in defects)
{
if (cnt++ % 3 == 0)
{
#:</tr><tr>
}
#:<td>
<input type="checkbox"
name="selectedDefects"
value="#defect.Malfunction_Id"
#(Html.Raw(defect.IsSelected ? "checked=\"checked\"" : "")) />
#defect.Malfunction_Name_e
#:</td>
}
#:</tr>
}
</table>
</div>
</div>
<br />
<div class="form-group">
<div class="col-md-2">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>

C# visual studio asp.net adding an item to a list attribute NullAttribute Exception: [duplicate]

I am currently working on a project to model a bikestore. In my 'Order' object, I have a lis object for the Bike items on the order. How would I add bikes to this list? I.E I want to display a list of availiable bikes in the Create view, an add one or more of them to the order.
My Controller:
public ActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "OrderNumber,CustomerName,OrderDate,PickupDate,TotalCost,PaymentMethod")] Order order)
{
if (ModelState.IsValid)
{
db.Orders.Add(order);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(order);
}
My Inventory model
public class Inventory
{
public int Id { get; set; }
public string SerialNumber { get; set; }
public virtual Store Store { get; set; }
public int? StoreId { get; set; }
public string Model { get; set; }
public string Description { get; set; }
public Decimal InventoryCost { get; set; }
public Decimal RecSalePrice { get; set; }
public Decimal SalePrice { get; set; }
public string PaymentMethod { get; set; }
public virtual BikeCategory Category { get; set; }
public int? CategoryId { get; set; }
}
My Order model:
namespace BikeStore.Models
{
public class Order
{
public Order()
{
OrderedItems = new List<Inventory>();
}
public string CustomerName { get; set; } //FROM CONTROLLER User.Identity.Name
public virtual List<Inventory> OrderedItems { get; set; }
[Key, DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public int OrderNumber { get; set; }
In the create view for orders:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Order</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.CustomerName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.CustomerName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CustomerName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.OrderDate, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.OrderDate, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.OrderDate, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.PickupDate, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.PickupDate, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.PickupDate, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.TotalCost, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.TotalCost, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.TotalCost, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.PaymentMethod, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.PaymentMethod, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.PaymentMethod, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Start by creating view models to represent what you want to display/edit in the view (add display and validation attributes as appropriate)
public class InventoryVM
{
public int ID { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
public class OrderVM
{
public string PaymentMethod { get; set; }
public List<InventoryVM> Inventory { get; set; }
}
Note that CustomerName, OrderDate and Total are not appropriate (you don't want a user editing these - they should be set in the POST method immediately before saving the order). Not sure what PickupDate represents but if its the actual date then that's not appropriate either (it would be set separately when the order is collected). I would also suggest that PaymentMethod be an enum or collection of PaymentType's and that you and use a dropdownlist in the view for selection.
Then the GET method would be
public ActionResult Create()
{
// Get all available bikes, for example
var inventory = db.Inventory;
OrderVM model = new OrderVM
{
Inventory = inventory.Select(i => new
{
ID = i.ID,
Name = i.Model // modify this to suit what you want to display in the view
}).ToList()
};
return View(model);
}
And in the view
#model yourAssembly.OrderVM
#using (Html.BeginForm())
{
for(int i = 0; i < Model.Inventory.Count; i++)
{
#Html.HiddenFor(m => m.Inventory[i].ID)
#Html.CheckBoxFor(m => m.Inventory[i].IsSelected)
#Html.LabelFor(m => m.Inventory[i].IsSelected, Model.Inventory[i].Name)
}
#Html.TextBoxFor(m => m.PayentMethod)
<input type="submit" value="Create" />
}
And the POST method would be
public ActionResult Create(OrderVM model)
{
// Initialize a new Order and map properties from view model
var order = new Order
{
CustomerName = User.Identity.Name,
OrderDate = DateTime.Now,
....
PaymentMethod = model.PaymentMethod
}
// Save the order so that you now have its `ID`
IEnumerable<int> selectedItems = model.Inventory.Where(i => i.IsSelected).Select(i => i.ID);
foreach(var item in selectedItems)
{
// You have not shown the model for this so just guessing
var orderItem = new OrderItem{ OrderID = order.Id, InventoryId = item };
db.OrderItems.Add(orderItem);
}
db.SaveChanges();
}
Side notes:
If you want to be able to allow users to select more that one of any
item, the you could change bool IsSelected to say int Quantity
If you you want to display additional information about the items,
say Description and Cost you can include additional properties
in the InventoryVM view model and display them with
#Html.DisplayFor(m => m.Inventory[i].Description)
If you want to display a total cost for all selected items in the
view, you will need to use javascript/jquery
If ModelState could be invalid, you will need to repopulate the
properties of InventoryVM before you return the view (as shown
only the ID and IsSelected properties post back) or include
hidden inputs for the other properties in the view

There is no ViewData item of type 'IEnumerable<SelectListItem>' that has the key 'Practice' - MVC5

I am very new to MVC and have just added a cascading drop down to my create page so when a Practice is selected the Optician drop down is populated with the names of opticians that work at that practice.
Model:
public class Booking
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid BookingId { get; set; }
[ForeignKey("Patient")]
public Guid PatientId { get; set; }
public virtual Patient Patient { get; set; }
public IEnumerable<SelectListItem> PatientList { get; set; }
[ForeignKey("Practice")]
public Guid PracticeId { get; set; }
public virtual Practice Practice { get; set; }
public IEnumerable<SelectListItem> PracticeList { get; set; }
[ForeignKey("Optician")]
public Guid OpticianId { get; set; }
public virtual Optician Optician { get; set; }
public IEnumerable<SelectListItem> OpticiansList { get; set; }
[Display(Name = "Date")]
[DataType(DataType.Date)]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
public DateTime Date { get; set; }
[ForeignKey("Time")]
public Guid? TimeId { get; set; }
public virtual Time Time { get; set; }
public IEnumerable<SelectListItem> TimeList { get; set; }
public bool isAvail { get; set; }
}
My Controller:
// GET: Bookings1/Create
public ActionResult Create()
{
var practices = new SelectList(db.Practices, "PracticeId", "PracticeName");
ViewData["Practice"] = practices;
Booking booking = new Booking();
ConfigureCreateModel(booking);
return View(booking);
}
public void ConfigureCreateModel(Booking booking)
{
booking.PatientList = db.Patients.Select(p => new SelectListItem()
{
Value = p.PatientId.ToString(),
Text = p.User.FirstName
});
booking.TimeList = db.Times.Select(t => new SelectListItem()
{
Value = t.TimeId.ToString(),
Text = t.AppointmentTime
});
}
// POST: Bookings1/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Booking booking)
{
// to ensure date is in the future
if (ModelState.IsValidField("Date") && DateTime.Now > booking.Date)
{
ModelState.AddModelError("Date", "Please enter a date in the future");
}
// Sets isAvail to false
booking.isAvail = false;
//Checks if model state is not valid
if (!ModelState.IsValid)
{
ConfigureCreateModel(booking);
return View(booking); // returns user to booking page
}
else // if model state is Valid
{
// Generates a new booking Id
booking.BookingId = Guid.NewGuid();
// Adds booking to database
db.Bookings.Add(booking);
// Saves changes to Database
db.SaveChanges();
// Redirects User to Booking Index
return RedirectToAction("Index");
}
}
My View:
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script>
$(document).ready(function () {
$("#Optician").prop("disabled", true);
$("#Practice").change(function () {
$.ajax({
url : "#Url.Action("Opticians","Bookings")",
type : "POST",
data : {Id : $(this).val() }
}).done(function (opticianList) {
$("#Optician").empty();
for (var i = 0; i < opticianList.length; i++) {
$("#Optician").append("<option>" + opticianList[i] + "</option>");
}
$("#Optician").prop("disabled", false);
});
});
});
</script>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Booking</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.PatientId, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.PatientId, Model.PatientList, "-Please select-", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.PatientId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.PracticeId, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("Practice", ViewData["Practice"] as SelectList,"-Please Select-", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.PracticeId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.Label("Select Optician :", new { #class = "col-md-2 control-label" })
<div class="col-md-10">
<select id="Optician"></select>
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Date, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Date, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Date, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.TimeId, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.TimeId, Model.TimeList, "-Please select-", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.TimeId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
The cascading drop down works as it should how ever when I click the button to create the appointment the following exception is thrown:
Exception:
An exception of type 'System.InvalidOperationException' occurred in System.Web.Mvc.dll but was not handled in user code
Additional information: There is no ViewData item of type 'IEnumerable' that has the key 'Practice'.
Any help would be greatly appreciated.
Thanks
You model already contains a property for the collection of practices
public IEnumerable<SelectListItem> PracticeList { get; set; }
although it should not contain
public virtual Practice Practice { get; set; }
In the GET method, you create a new SelectList for practices, but instead of assigning it to the model property, you add it to ViewData using
ViewData["Practice"] = practices;
and then in the view use
#Html.DropDownList("Practice", ViewData["Practice"] as SelectList, ..)
which is not even binding to a property in your model and would never post back to anything. Then when you return the view in the POST method (because your mode will always be invalid), you do not assign a value to ViewData["Practice"] so its null, hence the error.
Instead, in your ConfigureCreateModel() method, populate the PracticeList property (as your doing for PatientList) and remove the use of ViewData, and in the view use
#Html.DropDownListFor(model => model.PracticeId, Model.PracticeList, ...)
so your strongly binding to your model and when your submit the form, the value of PracticeId will be the value of the selected practice.
Side note: You will need to change your script to $("#PracticeId").change(function () { ...

Categories

Resources