DataBinding: 'System.String' does not contain a property with the name 'Name' - c#

i'm working with custom authentication with asp.net mvc , i'm using Role Provider. i have many to many relation User , Role , UserInRoles .
the error in binding roles values in checkboxs in view
i tried debugging controller create method it return all data in Role table then continue to view in foreach
error DataBinding: 'System.String' does not contain a property with the name 'Name'.
User Model
public class User
{
[Key]
public string UserId { get; set; }
public string Name { get; set; }
public virtual ICollection<UserInRoles> UserInRoles { get; set; }
}
Role Model
public class Role
{
public int RoleId { get; set; }
public string Name { get; set; }
public virtual ICollection<UserInRoles> UserInRoles { get; set; }
}
UserInRole Model
public class UserInRoles
{
[Key, Column(Order = 0)]
public string UserId { get; set; }
[Key, Column(Order = 1)]
public int RoleId { get; set; }
public virtual User User { get; set; }
public virtual Role Role { get; set; }
}
Controller
public ActionResult Create()
{
ViewBag.RoleId = new SelectList(Roles.GetAllRoles().ToList(), "Name", "Name");
return View();
}
View
#model AramexOneKnowledge.Models.RegisterViewModel
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>User</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.UserId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.UserId, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.UserId, "", new { #class = "text-danger" })
</div>
</div>
<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">
<label class="col-md-2 control-label">
Select User Role
</label>
<div class="col-md-10">
#foreach (var item in (SelectList)ViewBag.RoleId)
{
<input type="checkbox" name="SelectedRoles"
value="#item.Value" class="checkbox-inline" />
#Html.Label(item.Value, new { #class = "control-label" })
}
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</div>
expected result display roles in checkbox

this exception because Roles.GetAllRoles() returns string[] and you try to bind property Name inside it which it not exists.
so to overcome this issue you can populate you checkbox list as following
ViewBag.RoleId = Roles.GetAllRoles().Select(r => new SelectListItem{Text = r, Value = r});
in this case you SelectListItem Value and Text it will be the role name for both of them

Related

ASP.NET MVC POST won't send back second ocurrence of variable

The class Unidade:
public class Unidade
{
public int UnidadeId { get; set; }
public string Apelido { get; set; }
public string Descricao { get; set; }
}
Is used twice in the class Insumo, as Unidade and UnidadeConsumo
public class Insumo
{
public int InsumoId { get; set; }
public string Apelido { get; set; }
public string Descricao { get; set; }
public int UnidadeId { get; set; }
public Unidade Unidade { get; set; }
public int UnidadeConsumoId { get; set; }
public Unidade UnidadeConsumo { get; set; }
}
To edit Insumo there are two actions EDIT in the controller:
public ActionResult Edit(int? id)
{
Insumo insumo = db.Insumos.Find(id);
if (insumo == null) return HttpNotFound();
ViewBag.UnddId = new SelectList(db.Unidades, "UnidadeId", "Apelido", insumo.UnidadeId);
ViewBag.UndConsId = new SelectList(db.Unidades, "UnidadeId", "Apelido", insumo.UnidadeConsumoId);
return View(insumo);
}
And the POST EDIT:
[HttpPost]
public ActionResult Edit([Bind(Include = "InsumoId,Apelido,Descricao,UnidadeId,UnidadeConsumoId")] Insumo insumo)
{
if (ModelState.IsValid)
{
db.Entry(insumo).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.UnddId = new SelectList(db.Unidades, "UnidadeId", "Apelido", insumo.UnidadeId);
ViewBag.UndConsId = new SelectList(db.Unidades, "UnidadeId", "Apelido", insumo.UnidadeConsumoId);
return View(insumo);
}
The view to display the fields to edition, comprised of two dropdown lists to select both units is:
#model Gestor.Models.Insumo
#{
ViewBag.Title = "Alterar";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Alterar</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Insumo</h4>
<hr />
#Html.Partial("CopyEdit")
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Gravar" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Retornar a lista", "Index")
</div>
And the partial view CopyEdit in the center:
#model Gestor.Models.Insumo
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.InsumoId)
<div class="form-group">
#Html.LabelFor(model => model.Apelido, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Apelido, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Apelido, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Descricao, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Descricao, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Descricao, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.UnidadeId, "Unidade", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("UnddId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.UnidadeId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.UnidadeConsumoId, "Unidade de Consumo", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("UndConsId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.UnidadeConsumoId, "", new { #class = "text-danger" })
</div>
</div>
The problem is thta whrn returned to the POST Edit action, all fields are ok, but UnidadeConsumoId is always 0, what doesn't even exists in the database?
Can someone tell me why is it not returning the expected value, i.e. the selected value in the dropdown list reflecting the id of if?
Because your current code is rendering a SELECT element with name attribute value UndConsId.
<select class="form-control" name="UndConsId">
<!-- options -->
</select>
For model binding to work, the input element name attribute value should match with the parameter/property name used in the http post action method. Your parameter name is UnidadeConsumoId, not UndConsId
To fix this issue, pass UnidadeConsumoId as the first parameter of the DropDownList method call so that it will render the SELECT element with name UnidadeConsumoId. You can pass the ViewBag.UndConsId as the second parameter to explicitly specify the collection to be used to build the SELECT element.
This should work
#Html.DropDownList("UnidadeConsumoId", ViewBag.UndConsId as SelectList,
new { #class = "form-control" })

How do I use two dropdown menu instances from the same model?

I am having an issue where I think I have set things up correctly, however the results are alway nil.
Here's the three models:
public class FamilyMember
{
[Key]
public int id { get; set; }
[Required]
[Display(Name = "First Name")]
public string firstName { get; set; }
[Required]
[Display(Name = "Surname")]
public string surname { get; set; }
[Required]
[Display(Name = "Date of Birth")]
public DateTime dob { get; set; }
public virtual FamilyRelationship FamilyRelationship { get; set; }
[Display(Name = "Full Name")]
public string fullName
{
get
{
return string.Format("{0} {1}", firstName, surname);
}
}
}
public class RelationshipType
{
[Key]
public int id { get; set; }
[Required]
[Display(Name="Relationship Type")]
public string relationshipType { get; set; }
public virtual FamilyRelationship FamilyRelationship { get; set; }
}
public class FamilyRelationship
{
[Key]
public int id { get; set; }
[ForeignKey("FamilyMembers")]
[Display(Name = "First Family Member")]
public int familyMemberPrimary { get; set; }
[ForeignKey("FamilyMembers")]
[Display(Name = "Second Family Member")]
public int familyMemberSecondary { get; set; }
[Display(Name = "Relationship Type")]
public int relationshipType { get; set; }
public virtual ICollection<FamilyMember> FamilyMembers { get; set; }
public virtual ICollection<RelationshipType> RelationshipTypes { get; set; }
}
So, I have successfully added data to FamilyMember and RelationshipType and the CRUD is working perfectly.
The problem is found in the Create Controller/View of FamilyRelationship. The dropdown works perfectly and shows the family members in the two associated menus and the relationship also shows on the relationType dropdown. However, when I click create all values are set to null.
Create Controller:
// GET: FamilyRelationships/Create
public ActionResult Create()
{
ViewBag.familyMember = new SelectList(db.FamilyMembers, "id", "fullName");
ViewBag.relationship = new SelectList(db.RelationshipTypes, "id", "relationshipType");
return View();
}
// POST: FamilyRelationships/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "id,familyMemberPrimary,familyMemberSecondary,relationshipType")] FamilyRelationship familyRelationship)
{
if (ModelState.IsValid)
{
db.FamilyRelationships.Add(familyRelationship);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(familyRelationship);
}
Create View:
#model FamilyTree.Models.FamilyRelationship
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>FamilyRelationship</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.familyMemberPrimary, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#*#Html.EditorFor(model => model.familyMemberPrimary, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.familyMemberPrimary, "", new { #class = "text-danger" })*#
#Html.DropDownList("familyMember", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.familyMemberPrimary, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.familyMemberSecondary, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("familyMember", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.familyMemberPrimary, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.relationshipType, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("relationship", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.relationshipType, "", 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>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Please let me know where am I going wrong and if possible provide an example to make this work.
#Html.DropDownList("familyMember"
you need to use the actual property names most likely
#Html.DropDownList("familyMemberPrimary"
you'd also have to rename the viewbag property to match for the items to show up.. or use dropdownlistfor
#Html.DropDownListFor(a => a.familyMemberPrimary, (SelectList)ViewBag.familyMember , new { #class = "form-control" })
you also need to add a dropdownlist for familyMemberSecondary
#Html.DropDownListFor(a => a.familyMemberSecondary, (SelectList)ViewBag.familyMember , new { #class = "form-control" })
This should get you pretty close..
#model FamilyTree.Models.FamilyRelationship
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>FamilyRelationship</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.familyMemberPrimary, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(a => a.familyMemberPrimary, (SelectList)ViewBag.familyMember, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.familyMemberPrimary, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.familyMemberSecondary, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(a => a.familyMemberSecondary, (SelectList)ViewBag.familyMember, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.familyMemberSecondary, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.relationshipType, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(a => a.relationshipType, (SelectList)ViewBag.relationship, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.relationshipType, "", 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>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
make sure you re set your ViewBag properties after a failed POST
DotNetFiddle Example
This is the expected behaviour. Remember Http is stateless. So you need to reload your dropdown data before returning to the view
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "id,familyMemberPrimary,familyMemberSecondary,
relationshipType")] FamilyRelationship familyRelationship)
{
if (ModelState.IsValid)
{
db.FamilyRelationships.Add(familyRelationship);
db.SaveChanges();
return RedirectToAction("Index");
}
//Let's reload the data for dropdown.
ViewBag.familyMember = new SelectList(db.FamilyMembers, "id", "fullName");
ViewBag.relationship = new SelectList(db.RelationshipTypes, "id", "relationshipType");
return View(familyRelationship);
}
EDIT: As per comment
The values within the FamilyRelationship so familyMemberPrimary,
familyMemberSecondary and relationshipType have values of 0, where I
was expecting the id's of each of these would be passed over.
Because you are using EditorFor helper method for familyMemberPrimary property in your view. So if you are not filling a value in that input field, it is going to have default value(0 for int type)
If you want that property to be filled with your dropdown selection(of family members), you should give the dropdown name value as familyMemberPrimary so that when you post the form, model binding will set the selected option value to familyMemberPrimary property.
#Html.DropDownList("familyMemberPrimary",
ViewBag.familyMember as IEnumerable<SelectListItem>,
htmlAttributes: new { #class = "form-control" })

The INSERT statement conflicted with the FOREIGN KEY constraint dropdownlist [duplicate]

This question already has answers here:
What Causes The INSERT statement conflicted with the FOREIGN KEY constraint?
(2 answers)
Closed 6 years ago.
I'm working in ASP.NET MVC Web Application, so I want to insert values from another table (I get it with dropdownlist), but when I try to post, I get this:
The INSERT statement conflicted with the FOREIGN KEY constraint
"FK_dbo.Products_dbo.Subcategories_SubcategoryId". The conflict
occurred in database "ProyectName", table "dbo.Subcategories", column
'SubcategoryId'. The statement has been terminated.
Products model:
public class Product
{
public int ProductId { get; set; }
public int SubcategoryId { get; set; }
public virtual Subcategory Subcategory { get; set; }
public string Name { get; set; }
public string Presentation { get; set; }
public string Image { get; set; }
public string Alt { get; set; }
public bool IsDeleted { get; set; }
Product ViewModel
public class ProductViewModel
{
public string Name { get; set; }
public string Presentation { get; set; }
public string Image { get; set; }
public string Alt { get; set; }
public int SelectedSubcategory { get; set; }
public IEnumerable <SelectListItem> Subcategory { get; set; }
}
Subcategory Model:
public class Subcategory
{
public int SubcategoryId { get; set; }
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Image { get; set; }
public string Alt { get; set; }
public string Pdf { get; set; }
public bool IsDeleted { get; set; }
public bool IsInstalled { get; set; }
}
Get Controller
public ActionResult Create()
{
var subcategoryList = new ProductViewModel
{
Subcategory = new SelectList(db.SubcategoriesList, "SubcategoryId", "Name")
};
return View(subcategoryList);
}
Post Controller(Service method):
public class ProductService : IProductService
{
private EfDatabase db = new EfDatabase();
public async Task<string> CreateProduct(ProductViewModel model)
{
var product = new Product
{
Name = model.Name,
Presentation = model.Presentation,
Image = model.Image,
Alt = model.Alt,
SubcategoryId = model.SelectedSubcategory,
IsDeleted = false
};
db.ProductsList.Add(product);
await db.SaveChangesAsync();
return "Product " + model.Name + "has been created";
View:
#model Proyect.Models.ViewModels.ProductViewModel
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Product</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.SubcategoryId, "SubcategoryId", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(m => m.SelectedSubcategory, Model.Subcategory, "-Selecciona una opcion-", new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.SelectedSubcategory)
</div>
</div>
<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.Presentation, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Presentation, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Presentation, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Image, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Image, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Image, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Alt, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Alt, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Alt, "", 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>
So I don't found any error on my code, what's wrong with that? can any one help me to solve this problem?
My question is unique because model.SelectedSubcategory don't get Id value from another table, It always get 0 so in the other questions no make any comparision with that
Thankyou in advance!
MY CREATE VIEW NOW
#model myPROYECT.Models.ViewModels.ProductViewModel
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Product</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
<div class="col-md-10">
#Html.DropDownListFor(m => m.SelectedSubcategory, Model.Subcategory, "-Selecciona una opcion-", new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.SelectedSubcategory)
</div>
</div>
<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.Presentation, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Presentation, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Presentation, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Image, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Image, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Image, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Alt, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Alt, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Alt, "", 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>
There's nothing that requires a Subcategory to be selected. You're likely to get this error when a selection hasn't been made for subcategory. You should make subcategory required or make null a value that's possible to get back.
public class Product
{
public int ProductId { get; set; }
public int? SubcategoryId { get; set; }
public virtual Subcategory Subcategory { get; set; }
public string Name { get; set; }
public string Presentation { get; set; }
public string Image { get; set; }
public string Alt { get; set; }
public bool IsDeleted { get; set; }
}
using System.ComponentModel.DataAnnotations;
public class ProductViewModel
{
public int? SubcategoryId { get; set; }
public string Name { get; set; }
public string Presentation { get; set; }
public string Image { get; set; }
public string Alt { get; set; }
[Required]
public int? SelectedSubcategory { get; set; }
public IEnumerable Subcategory { get; set; }
}
Post Controller(Service method):
public class ProductService : IProductService
{
private EfDatabase db = new EfDatabase();
public async Task<string> CreateProduct(ProductViewModel model)
{
if (!ModelState.IsValid)
return View("Create");
var product = new Product
{
Name = model.Name,
Presentation = model.Presentation,
Image = model.Image,
Alt = model.Alt,
SubcategoryId = model.SelectedSubcategory,
IsDeleted = false
};
db.ProductsList.Add(product);
await db.SaveChangesAsync();
return "Product " + model.Name + "has been created";
}
}

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 () { ...

ASP.NET MVC5 Entity Framework Group Fields (Country, State) for Account

I have a Company, Country and State classes.
State is related to Country
State and Country are foreign key to Company
The problem Company View shows both state and Country as separate dropdowns.
But I expect when I select State, corresponding Country should change or vice versa. Is this possible in MVC5 EF Model.
Company Class
public class Company
{
public int CompanyID { get; set; }
[Required]
[StringLength(100,MinimumLength=3)]
public string Name { get; set; }
[StringLength(200)]
public string Address { get; set; }
public int? StateID { get; set; }
public virtual State State { get; set; }
public int? CountryID { get; set;}
public virtual Country Country { get; set; }
public int CompanyTypeID { get; set; }
public virtual CompanyType CompanyType { get; set; }
}
Country Class
public class Country
{
public int CountryID { get; set; }
[Required]
[StringLength(100)]
public string Name { get; set; }
public virtual ICollection<State> States { get; set; }
public virtual ICollection<Company> Companies { get; set; }
}
State Class
public class State
{
public int StateID { get; set; }
[Required]
[StringLength(100)]
public string Name { get; set; }
public int CountryID { get; set; }
public virtual Country Country { get; set; }
public virtual ICollection<Company> Companies { get; set; }
}
I have scaffolded and Generated the view and Company View for reference
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Company</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.Address, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Address, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Address, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.StateID, "StateID", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("StateID", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.StateID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CountryID, "CountryID", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("CountryID", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.CountryID, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CompanyTypeID, "CompanyTypeID", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("CompanyTypeID", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.CompanyTypeID, "", 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")
#section Scripts { #Scripts.Render("~/bundles/jqueryval") }
Any help would be appreciated
What you need are Cascading Dropdown Lists. Here is an example from CodeProject.
http://www.codeproject.com/Articles/258172/Simple-Implementation-of-MVC-Cascading-Ajax-Drop-D
Is this possible in MVC5 EF Model.
Yes. But you'll need to do the work (or get (buy) controls that do it for you).
The limitation is not MVC, but that the default HTML controls have no concept of being dependent on another. Either write code to change the content of the State drop down when the Country drop down changes (this could include filtering the list of states by the selected country), or get thirdy party controls that do so (it is a common requirement so lots of controls out there1).
1 But Stack Overflow does not do recommendations of tools.

Categories

Resources