How to get My Purchase Class to map to a Specific ApplicationUser - asp.net-mvc-4

Here is the Idea. When an Admin is logged on they can pull up a list of all of the users.It will give the options for edit, details, delete like normal but I have added a link to Purchases like so:
#model IEnumerable<IdentitySample.Models.ApplicationUser>
#{
ViewBag.Title = "Index";
}
<div class="col-12 backgroundImg">
<div class="navbarSpace">
<div class="col-12 formBackground">
<h2 class="formHeader">List of Users</h2>
<h4 class="formText">
#Html.ActionLink("Create New ", "Create")
</h4>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Email)
</th>
<th>
#Html.DisplayNameFor(model => model.UserName)
</th>
<th>
#Html.DisplayNameFor(model => model.FavStrain)
</th>
<th>
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Email)
</td>
<td>
#Html.DisplayFor(modelItem => item.UserName)
</td>
<td>
#Html.DisplayFor(modelItem => item.FavStrain)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.Id
}) |
#Html.ActionLink("Details", "Details", new { id =
item.Id }) |
#Html.ActionLink("Delete", "Delete", new { id =
item.Id }) |
#Html.ActionLink("Purchases", "PurchaseIndex", new {
id = item.Id})
</td>
</tr>
}
</table>
</div>
</div>
</div>enter code here
When you click the Purchases link it takes you to the PurchaseIndex page which looks like this:
Purchase List
#model IEnumerable<IdentitySample.Models.Purchases>
#{
ViewBag.Title = "Index";
}
<div class="col-12 backgroundImg navbarSpace">
<div class="col-12 formBackground">
<h2 class="formHeader">Index</h2>
<hr />
<div class="formHeaderSmall">
Total Points <br />
#Model.Sum(i => i.Points) </div>
<p class="formText">
#Html.ActionLink("Create New", "CreatePurchase")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Points)
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Points)
</td>
<td></td>
</tr>
}
</table>
<p class="formText">
#Html.ActionLink("Back to List", "Index")
</p>
</div>
</div>
It gives a list of Purchases and gives the total points that is why i didnt include a details page. Everything works right EXCEPT for the fact that the Purchases do not map to a specific user. If I create a new user and click Purchases it brings up a list of all of the purchases, not just the purchases specific for that user. How do I get a Purchase to map to a Specific User?
I have created a Purchases class that looks like this:
public class Purchases
{
[Key]
public int PurchaseId { get; set; }
[Required]
[Display(Name = "Product Name")]
[DataType(DataType.Text)]
public string Name { get; set; }
[Required]
[Range(0,5)]
[Display(Name = "Points")]
[DataType(DataType.Text)]
public int Points { get; set; }
public string ApplicationUserId { get; set; }
public virtual ApplicationUser Users { get; set; }
}
My ApplicationUser Class looks like this:
public class ApplicationUser : IdentityUser
{
[Display(Name ="Favorite Strain")]
[DataType(DataType.Text)]
public string FavStrain { get; set; }
public virtual List<Purchases> Purchase { get; set; }
Now up to this point the Database is registering the Foreign Key of the Purchases Class to the ApplicationUser class like it is supposed to.
I can create a new purchase and display them to a list and all of the Crud Operations work just fine.
The problem is when I create a new Purchase it doesn't include the ApplicationUserId in the Database it returns a Null.
Null Database
I am pretty sure that the problem is in my Controller. I have tried just about everything so I don't want to include the failed try's so here is the Controllers as they are now and working.
There is no need for me to include the edit or details because I am not going to give the users that access.
public ActionResult CreatePurchase()
{
return View();
}
private ApplicationDbContext db = new ApplicationDbContext();
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreatePurchase([Bind(Include = "PurchaseId,Name,Points,Id")] Purchases purchases)
{
if (ModelState.IsValid)
{
db.Purchases.Add(purchases);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(purchases);
}
// GET: Purchases/Edit/5
public ActionResult PurchaseIndex()
{
var userDetails = db.Purchases.Include(u => u.Users);
return View(db.Purchases.ToList());
}
This is my first Question on Stack Overflow so forgive me if something isn't right.
**************************************Update************************************
This is my PurchaseIndexController. Now this returns only the user associated with the purchase. However it is always 0 because there is no UserID. If I try using an int? type or Guid? it gives an error. Cannot implicitly convert type int to string.
public ActionResult PurchaseIndex(string ID)
{
//this gets all purchases for a certain individual
ApplicationDbContext db = new ApplicationDbContext();
var userDetails = db.Purchases.Where(x => x.ApplicationUserId ==
ID).ToList();
return View(userDetails);
}
Here is the CreatePurchase View
#model IdentitySample.Models.Purchases
#{
ViewBag.Title = "Create";
}
<div class="col-12 backgroundImg navbarSpace">
<div class="col-12 formBackground">
<h2 class="formHeader">Add a New Purchase</h2>
<hr />
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#*#Html.Hidden("id", (string)ViewBag.UserID)*#
#Html.HiddenFor(model => model.ApplicationUserId)
<div class="form-horizontal">
<div class="col-12">
#Html.LabelFor(model => model.Name, htmlAttributes: new {
#class = "formText col-12" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new {
htmlAttributes = new { #class = "col-12" } })
#Html.ValidationMessageFor(model => model.Name, "", new
{ #class = "text-danger" })
</div>
</div>
<div class="col-12">
#Html.LabelFor(model => model.Points, htmlAttributes: new {
#class = "formText col-12" })
<div class="col-md-10">
#Html.EditorFor(model => model.Points, new {
htmlAttributes = new { #class = "col-12" } })
#Html.ValidationMessageFor(model => model.Points, "",
new { #class = "text-danger" })
</div>
</div>
<div class="col-12">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-
default" />
</div>
</div>
</div>
}
<div class="formText">
#Html.ActionLink("Back to List", "Index")
</div>
</div>
</div>
I also have link in the Manage section for the users to check thier points and purchases but I don't know how to create an ActionLink for this to just get purchases associated with the user.
Here is the controller
public ActionResult WeedPoints(string ID)
{
ApplicationDbContext db = new ApplicationDbContext();
var userDetails = db.Purchases.Where(x => x.ApplicationUserId ==
ID).ToList();
return View(userDetails);
}
Here is the Action Link now.
<div class="col-12 formHeaderSmall">#Html.ActionLink("My
Purchases/Points", "WeedPoints", "Manage")</div>
*********************************Update****************************************
Here is the Controllers with the View Bag reference. The Create Purchase View has the ViewBag I just Uncommented it out.
[Authorize(Roles =
"Admin,DispensaryManager,DispensaryEmployee,DispensaryEastEmployee")]
public ActionResult CreatePurchase(string Id)
{
ViewBag.UserID = Id;
//ApplicationDbContext db = new ApplicationDbContext();
//var userDetails = db.Purchases.Where(x => x.ApplicationUserId == Id;
return View();
}
private ApplicationDbContext db = new ApplicationDbContext();
//POST: Purchases/Create
[HttpPost]
[Authorize(Roles =
"Admin,DispensaryManager,DispensaryEmployee,DispensaryEastEmployee")]
[ValidateAntiForgeryToken]
public ActionResult CreatePurchase([Bind(Include =
"PurchaseId,Name,Points,ApplicationUserId")] Purchases
purchases,string id)
{
if (ModelState.IsValid)
{
db.Purchases.Add(purchases);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(purchases);
}
[Authorize(Roles =
"Admin,DispensaryManager,DispensaryEmployee,DispensaryEastEmployee")]
public ActionResult PurchaseIndex(string Id)
{
//this gets all purchases for a certain individual
ApplicationDbContext db = new ApplicationDbContext();
var userDetails = db.Purchases.Where(x => x.ApplicationUserId ==
Id).ToList();
ViewBag.UserID = Id;
return View(userDetails);
}
***************************Total Refactor*********************************8
Here is the new controller in its entirety.
public class PurchasesController : Controller
{
private ApplicationDbContext db = new ApplicationDbContext();
// GET: Purchases
public ActionResult Index()
{
var purchases = db.Purchases.Include(p => p.Users);
return View(purchases.ToList());
}
// GET: Purchases/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Purchases purchases = db.Purchases.Find(id);
if (purchases == null)
{
return HttpNotFound();
}
return View(purchases);
}
// GET: Purchases/Create
public ActionResult Create()
{
ViewBag.Users = new SelectList(db.Users, "Id", "UserName");
List<SelectListItem> selectListItems = new List<SelectListItem>();
foreach (ApplicationUser user in db.Users)
{
SelectListItem selectListItem = new SelectListItem
{
Text = user.UserName,
Value = user.Id.ToString()
};
selectListItems.Add(selectListItem);
}
//ViewBag.ApplicationUserId = new SelectList(db.Users, "Id",
"UserName");
return View();
}
// POST: Purchases/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 =
"PurchaseId,Name,Points,TotalPoints,ApplicationUserId")] Purchases
purchases)
{
if (ModelState.IsValid)
{
var totalPoints = db.Purchases.Sum(x => x.Points);
purchases.TotalPoints = totalPoints;
db.Purchases.Add(purchases);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.ApplicationUserId = new SelectList(db.Users, "Id",
"UserName", purchases.ApplicationUserId);
return View(purchases);
}
// GET: Purchases/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Purchases purchases = db.Purchases.Find(id);
if (purchases == null)
{
return HttpNotFound();
}
ViewBag.ApplicationUserId = new SelectList(db.Users, "Id",
"UserName", purchases.ApplicationUserId);
return View(purchases);
}
// POST: Purchases/Edit/5
// 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 Edit([Bind(Include =
"PurchaseId,Name,Points,TotalPoints,ApplicationUserId")] Purchases
purchases)
{
if (ModelState.IsValid)
{
db.Entry(purchases).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.ApplicationUserId = new SelectList(db.Users, "Id",
"UserName", purchases.ApplicationUserId);
return View(purchases);
}
// GET: Purchases/Delete/5
public ActionResult Delete(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Purchases purchases = db.Purchases.Find(id);
if (purchases == null)
{
return HttpNotFound();
}
return View(purchases);
}
// POST: Purchases/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
Purchases purchases = db.Purchases.Find(id);
db.Purchases.Remove(purchases);
db.SaveChanges();
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
}
}
Now there is a dropdown list of users to choose from when you create a new purchase. Here is the Create View.
<div class="col-12 backgroundImg navbarSpace scrollBar">
<div class="formBackground col-12">
<h2 class="formHeader">Edit Puchase</h2>
<hr />
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger"
})
#Html.HiddenFor(model => model.PurchaseId)
#Html.HiddenFor(model => model.TotalPoints)
<div class="col-12">
#Html.LabelFor(model => model.Name, htmlAttributes: new {
#class = "formText col-12" })
<div class="col-12">
#Html.EditorFor(model => model.Name, new {
htmlAttributes = new { #class = "col-12" } })
#Html.ValidationMessageFor(model => model.Name, "", new
{ #class = "text-danger" })
</div>
</div>
<div class="col-12">
#Html.LabelFor(model => model.Points, htmlAttributes: new {
#class = "formText col-12" })
<div class="col-12">
#Html.EditorFor(model => model.Points, new {
htmlAttributes = new { #class = "col-12" } })
#Html.ValidationMessageFor(model => model.Points, "",
new { #class = "text-danger" })
</div>
</div>
#*<div class="col-12">
#Html.LabelFor(model => model.TotalPoints,
htmlAttributes: new { #class = "formText col-12" })
<div class="col-12">
#Html.EditorFor(model => model.TotalPoints, new {
htmlAttributes = new { #class = "col-12" } })
#Html.ValidationMessageFor(model =>
model.TotalPoints, "", new { #class = "text-danger" })
</div>
</div>*#
<div class="col-12">#Html.LabelFor(model => model.ApplicationUserId,
"Users", htmlAttributes: new { #class = "formText col-12" })
<div class="col-12"> #Html.DropDownList("Users", null, htmlAttributes:
new { #class = "col-12" })
#Html.ValidationMessageFor(model => model.ApplicationUserId, "", new {
#class = "text-danger" })
</div>
</div>
<div class="col-12">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div class="formText"> #Html.ActionLink("Back to List", "Index")
</div>
</div>
</div>
This creates a drop down list of users displaying their User Name. When I select a user and hit save I get an error saying that
There is no ViewData item of type 'IEnumerable' that has the key 'Id'.

Is the 'Id' being passed to this method null?
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreatePurchase([Bind(Include = "PurchaseId,Name,Points,Id")] Purchases purchases)
{
if (ModelState.IsValid)
{
db.Purchases.Add(purchases);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(purchases);
}
If it is null, the userID should be included (as a hidden field) in the form you are posting. Then (once the userID is populated in the DB) you should be able to get only the purchase associated with the userID, doing something like this:
var userDetails = db.Purchases.Where(x=>x.ApplicationUserId == ID).ToList();

The problem you are having is that the 'Create new purchase' action is not passing a user id, it is currently:
#Html.ActionLink("Create New", "CreatePurchase")
Whereas it needs to be this to pass an id:
#Html.ActionLink("Create New", "CreatePurchase", new {
id = Model.Id})
However this assumes that an id has been passed to the purchase index view in the model for that page, which is likely not the case but I can't tell as I can't see your purchase index action. The simplest way to pass it for you is through a viewbag, however I do not recommend using this for your site if you intend to use it seriously. The correct way to handle data across your views would be using viewmodels. There are a lot of tutorials available, e.g. https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/mvc-music-store/mvc-music-store-part-3
Using the CRUD implementation you have you can just pass the id to the page using a weakly type viewbag. Your purchase index action should look something like this:
public ActionResult Index(string id)
{
//this checks to see if an id has been passed to the action
if (id != null){
//this gets all purchases for a certain individual
var purchases = db.purchases.Where(i => i.ApplicationUserId == id).ToList()
//this gets the user id passed to the action and sticks it in a viewbag you can retrieve later on the page
ViewBag.UserID == id;
//returns the view with the list above
return View(purchases);
}
else{
//no id was passed to the action so the list is for all purchases
var purchases = db.purchases.ToList();
return View(purchases);
}
}
Now in your view you need to amend the create new purchase action to include the viewbag item:
#Html.ActionLink("Create New", "CreatePurchase", new {
id = ViewBag.UserID})
Change your create purchase action to accept the user id you are passing:
public ActionResult CreatePurchase(string id)
{
//puts the id in a viewbag to again be used by the view
ViewBag.UserID == id;
return View();
}
Then on your create purchase view you need to pass the viewbag item into the model, you do this by having a hidden field somewhere inside the form:
#Html.Hidden("id", (string)ViewBag.UserID)
I'm converting the viewbag into a string because assuming your are using ASP NET identity the user id is a string and ViewBag is a dynamic object, so needs to be turned into a string before you can put it into the model.id space effectively. This will then pass the user ID to the post action and a purchase will be created specific to the id.
Bear in mind, this is a terrible way to be doing this, the default CRUD stuff whilst handy isn't really that great for production because you are accessing models directly and you will need to use weakly typed ViewBags to transfer data. It's error prone and insecure.

Related

How fix not working ActionResult Update in NET Core?

I'm learning about .NET Core and I'm using code from this tutorial. But my update sql is not working.
Here is the index view code:
public ActionResult Index(int? id)
{
ViewBag.Operation = id;
ViewBag.Name = db.Chars.ToList();
Chars Chars = db.Chars.Find(id);
return View(Chars);
}
As for now it work I see results from sql and here is the updated part:
public ActionResult Update(Chars Chars)
{
if (ModelState.IsValid)
{
db.Entry(Chars).State = EntityState.Modified;
db.SaveChanges();
}
return RedirectToAction("Index", new { id = 0 });
}
Here is index.cshtml part:
#using (Html.BeginForm()
{
#foreach (var item in (IEnumerable<MVC__test_2.Chars>)ViewBag.Name)
{
<div class="form-group">
<div class="col-md-10">
#Html.EditorFor(modelItem => item.CharName, new { htmlAttributes = new { #class = "form-control" } })
#Html.HiddenFor(modelItem => item.CharID, new { id = item.CharID })
</div>
</div>
#Html.ActionLink("Index", "Index", new { id = item.CharID })
<input type="submit" value="Update" name="Update"
style=#((ViewBag.Operation != null && Convert.ToInt32(ViewBag.Operation) > 0) ? "display:block" : "display:none") />
}
}
According to the tutorial you provided , I made a demo to test and it updated the data well. The following is the working example , you could refer to and make the modification as per your need .
Model
public class Description
{
public int Id { get; set; }
public string Display { get; set; }
}
Controller
public IActionResult Index(int? id)
{
ViewBag.Operation = id;
ViewBag.Name = _context.Description.ToList();
Description description= _context.Description.Find(id);
return View(description);
}
public ActionResult Update(Description description)
{
if (ModelState.IsValid)
{
_context.Entry(description).State = EntityState.Modified;
_context.SaveChanges();
}
return RedirectToAction("Index", new { id = 0 });
}
Index.cshtml , you should hide the id of the modified data in the modification section.
#model WebApplication1.Models.Description
#using (Html.BeginForm("Update", "Home", FormMethod.Post))
{
#foreach (var item in (IEnumerable<WebApplication1.Models.Description >)ViewBag.Name)
{
<div class="form-group">
<div class="col-md-10">
#Html.EditorFor(modelItem => item.Display, new { htmlAttributes = new { #class = "form-control" } })
#Html.HiddenFor(modelItem => item.Id, new { id = item.Id })
</div>
</div>
#Html.ActionLink("Edit", "Index", new { id = item.Id })
}
// Create or Update data
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true)
<fieldset>
<legend> <b>Entry Screen</b></legend>
<div class="form-group">
#Html.LabelFor(model => model.Display, new { #class = "control-label col-md-2" })
#Html.HiddenFor(model => model.Id)
<div class="col-md-10">
#Html.EditorFor(model => model.Display)
#Html.ValidationMessageFor(model => model.Display)
</div>
</div>
<div class="form-group">
<p>
<input type="submit" value="Create" name="Create"
style=#((ViewBag.Operation != null && Convert.ToInt32(ViewBag.Operation) > 0) ? "display:none" : "display:block") />
<input type="submit" value="Update" name="Update"
style=#((ViewBag.Operation != null && Convert.ToInt32(ViewBag.Operation) > 0) ? "display:block" : "display:none") />
</p>
</div>
</fieldset>
</div>
}

Asp.net Core EF 3DropDown in series on Edit form, Save will delete fields in navigation properties tabels

I have 4 entities :
Vehicle, VehicleBrand, VehicleModel, VehicleVersion
in one-to-many relationship, code-first generated by ef.
Database look like that:
Edit form use jQuery, first dropdown is feeded by a ViewBag, and the other 2 by Json.
The Edit Form is that:
And when I save the DropDowns retrn ONLY id ! dooh !
But my Bind expect entity
As you can see vbName came back null and after SaveChanges.. in all entities have name field null
Here is the Controller code for Edit:
[HttpGet]
public async Task<IActionResult> YourEditNewCar(int? id)
{
if (id == null)
{
return NotFound();
}
var brands = await _context.VehicleBrands.OrderBy(b => b.vbName).Select(x => new { Id = x.id, Value = x.vbName }).ToListAsync();
var models = await _context.VehicleModels.OrderBy(m => m.vmName).ToListAsync();
var versions= await _context.VehicleVersions.OrderBy(v =>v.vvName).ToListAsync();
var model = new Vehicle();
ViewBag.BrandList = new SelectList(brands, "Id", "Value");
model = await _context.Vehicles.SingleOrDefaultAsync(m => m.id == id);
if (model == null)
{
return NotFound();
}
return View("~/Views/Manage/YourEditNewCar.cshtml", model);
}
public JsonResult getVehicleModelById(int id)
{
List<VehicleModel> vehicleModelList = new List<VehicleModel>();
vehicleModelList = _context.VehicleModels.Where(m => m.VehicleBrand.id == id).OrderBy(m => m.vmName).ToList(); //.Select(y => new { Id = y.id, Value = y.vmName })
vehicleModelList.Insert(0, new VehicleModel { id = 0, vmName = "Car Model" });
return Json(vehicleModelList);
}
public JsonResult getVehicleVersionById(int id)
{
List<VehicleVersion> vehicleVersionList = new List<VehicleVersion>();
vehicleVersionList = _context.VehicleVersions.Where(m => m.VehicleModel.id == id).OrderBy(m => m.vvName).ToList(); //.Select(y => new { Id = y.id, Value = y.vmName })
vehicleVersionList.Insert(0, new VehicleVersion { id = 0, vvName = "Car Version" });
return Json(vehicleVersionList);
}
and the post:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> YourEditNewCar(int id, [Bind("id,DoorsNr,isAvailable,isDamaged,isDeleted,FabricationDate,FuelTankCapacity,TrunckCapacity,OnBoardKm,SeatNr," +
"LicencePlate, VehicleBrand, VehicleModel, VehicleVersion")] Vehicle vehicle)
{
var user = await _userManager.GetUserAsync(User);
if (ModelState.IsValid)
{
try
{
_context.Update(vehicle);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!VehicleExists(vehicle.id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction("YourCar", "Manage");
}
return View(vehicle);
}
in the Edit form dropdowns look like this:
<hr>
<div class="col-md-12 text-center"><h4> Please select :</h4></div>
<div class="row row-list">
<div class="form-group col-xs-4">
<label asp-for="VehicleBrand" class="control-label hidden" value=""></label>
#Html.DropDownListFor(model => model.VehicleBrand.id, (SelectList)ViewBag.BrandList, "Car Brand", new { style = "width: 140px;", #class = "form-control" })
</div>
<div class="form-group col-xs-4">
<label asp-for="VehicleModel" class="control-label hidden" value=""></label>
#Html.DropDownListFor(model => model.VehicleModel.id, new SelectList(string.Empty), "Car Model", new { style = "width: 120px;", #class = "form-control" })
</div>
<div class="form-group col-xs-4">
<label asp-for="VehicleVersion" class="control-label hidden" value=""></label>
#Html.DropDownListFor(model => model.VehicleVersion.id, new SelectList(string.Empty), "Car Version", new { style = "width: 110px;", #class = "form-control" })
</div>
</div>
and the scrip from edit form, to feed dropdowns, is like that:
<script>
$(function () {
$("#VehicleBrand_id").change(function () {
//alert("Vehicle Brand dd changed !");
var url = '#Url.Content("~/Manage/getVehicleModelById")';
var ddlsource = "#VehicleBrand_id";
$.getJSON(url, { id: $(ddlsource).val() }, function (data) {
var items = '';
$("#VehicleModel_id").empty();
$.each(data, function (i, row) {
items += "<option value='" + row.id + "'>" + row.vmName + "</option>";
});
$("#VehicleModel_id").html(items);
})
});
});
</script>
All dropdowns works perfect, the return is just id.
And when I SaveChanges, even if I want to save Only in Vehicle table, it update in other entities too, by navigation properties.
_context.Update(vehicle);
await _context.SaveChangesAsync();
I tried this to not Update VehicleBrands, but doesn't have effect :
and this need a new object:
I feel that I'm missing something simple, or is a wrong approach.
The problem Was in Model! I used Just: public VehicleBrand VehicleBrand { get; set; }
Without declaring field as ForeignKey
I added in model:
[ForeignKey("VehicleVersionid")]
public int VehicleVersionid { get; set; }
Now I added (with Bold) in Model:
[ForeignKey("VehicleBrandid")]
public int VehicleBrandid { get; set; }
public VehicleBrand VehicleBrand { get; set; }
[ForeignKey("VehicleModelid")]
public int VehicleModelid { get; set; }
public VehicleModel VehicleModel { get; set; }
[ForeignKey("VehicleVersionid")]
public int VehicleVersionid { get; set; }
public VehicleVersion VehicleVersion { get; set; }
And edited in :
[HttpPost]
[ValidateAntiForgeryToken]
public async Task YourEditNewCar(int id,[Bind("id,DoorsNr,isAvailable,isDamaged,isDeleted,FabricationDate,FuelTankCapacity,TrunckCapacity,OnBoardKm,SeatNr," +
"LicencePlate, VehicleBrandid, VehicleModelid, VehicleVersionid")] Vehicle vehicle)
Thanks for your patience!:)
Works like a charm ! :)
You should be using
#Html.DropDownListFor(model => model.VehicleBrandid, (SelectList)ViewBag.BrandList, "Car Brand", new { style = "width: 140px;", #class = "form-control" })
not
#Html.DropDownListFor(model => model.VehicleBrand.id, (SelectList)ViewBag.BrandList, "Car Brand", new { style = "width: 140px;", #class = "form-control" })
Bind your dropdown list to the id field in vehicle entity, not to the id field in the related entity.
Or if you wanted to start using taghelpers in asp.net core you could use this reference: https://learn.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms#the-select-tag-helper
The issue with the code is, mvc modelstate validation processing with respected to the model used in your posted view. So in this your model is vehicles, so with respect vehicle we need to give values for model. vehicleBrand, VehicleModel, VehicleVersion are different model and the value in that field not have any dependencies on Vehicles model at posting time. so in view you need to use like this
<div class="row row-list">
<div class="form-group col-xs-4">
<label asp-for="VehicleBrand" class="control-label hidden" value=""></label>
#Html.DropDownListFor(model => model.VehicleBrandid, (SelectList)ViewBag.BrandList, "Car Brand", new { style = "width: 140px;", #class = "form-control" })
</div>
<div class="form-group col-xs-4">
<label asp-for="VehicleModel" class="control-label hidden" value=""></label>
#Html.DropDownListFor(model => model.VehicleModelid, new SelectList(string.Empty), "Car Model", new { style = "width: 120px;", #class = "form-control" })
</div>
<div class="form-group col-xs-4">
<label asp-for="VehicleVersion" class="control-label hidden" value=""></label>
#Html.DropDownListFor(model => model.VehicleVersionid, new SelectList(string.Empty), "Car Version", new { style = "width: 110px;", #class = "form-control" })
</div>
</div>
and once one model used in view it is will we changed the whole property as entity then it be difficult to manage and check entitystate.unchanged property

ASP.Net MVC: When form post then one view model property getting null

i have simple form where one dropdown and one submit button. i have two index function one for get and one for form post. when i select a product from dropdown and click on submit button then my index action getting invoke but there i notice my products property getting null. see my code please and tell me where i made the mistake.
view code
#model AuthTest.Models.SampleViewModel
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>DateValTest</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Products, htmlAttributes: new { #class = "control-label col-md-2", style = "padding-top:0px;" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.SelectedProductId, new SelectList(Model.Products, "ID", "Name"), "-- Select Product--")
#Html.ValidationMessageFor(model => model.SelectedProductId, "", 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-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
model code
public class Product
{
public int ID { set; get; }
public string Name { set; get; }
}
public class SampleViewModel
{
[Display(Name = "Products")]
public List<Product> Products { set; get; }
[Required(ErrorMessage = "Select any one")]
public int SelectedProductId { set; get; }
}
controller code
public class TestValController : Controller
{
// GET: TestVal
public ActionResult Index()
{
var SampleVM = new SampleViewModel();
SampleVM.Products = new List<Product>
{
new Product{ ID=1, Name="IPhone" },
new Product{ ID=2, Name="MacBook Pro" },
new Product{ ID=3, Name="iPod" }
};
return View(SampleVM);
}
[HttpPost]
public ActionResult Index(SampleViewModel vm)
{
var SampleVM = new SampleViewModel();
SampleVM.Products = new List<Product>
{
new Product{ ID=1, Name="IPhone" },
new Product{ ID=2, Name="MacBook Pro" },
new Product{ ID=3, Name="iPod" }
};
if (ModelState.IsValid)
return View(vm);
else
return View(SampleVM);
}
}
when i debug second action then i saw vm products property getting null
please tell me where i made the mistake?
thanks
You are not making any mistake, You are not getting the list of products back because you are not including them in the HTML input form.
If you want to include the list of products you can add the following inside the input form
#for (int i = 0; i < Model.Products.Count(); i++)
{
<div>
#Html.HiddenFor(model => Model.Products[i].Name)
#Html.HiddenFor(model => Model.Products[i].ID)
</div>
}
#Mou,
Please modify your razor view and try this.
In Razor View nowhere you have specified the Http verb(Get,Post).
#using (Html.BeginForm("Index", "TestVal", FormMethod.Post)

why delete failed to removed the record?

i have the following action for the delete operation:
[HttpPost]
public ActionResult Delete(int id, EmployeeDeleteViewModel collection)
{
try
{
if (ModelState.IsValid)
{
Employee e = new Employee
{
EmpId = collection.EmpID,
FirstName = collection.FirstName,
LastName = collection.LastName,
DepartmentId = collection.DepartmentID
};
db.Employees.Remove(e);
db.SaveChanges();
return RedirectToAction("Index", new { id = id });
}
// TODO: Add update logic here
return View(collection);
}
catch
{
return View();
}
}
the delete view is:
#model VirtualCampus2.Models.EmployeeDeleteViewModel
#{
ViewBag.Title = "Delete";
}
<h2>Delete</h2>
<h3>Are you sure you want to delete this?</h3>
<fieldset>
<legend>EmployeeDeleteViewModel</legend>
<div class="display-label">
#Html.DisplayNameFor(model => model.EmpID)
</div>
<div class="display-field">
#Html.DisplayFor(model => model.EmpID)
</div>
<div class="display-label">
#Html.DisplayNameFor(model => model.FirstName)
</div>
<div class="display-field">
#Html.DisplayFor(model => model.FirstName)
</div>
<div class="display-label">
#Html.DisplayNameFor(model => model.LastName)
</div>
<div class="display-field">
#Html.DisplayFor(model => model.LastName)
</div>
<div class="display-label">
#Html.DisplayNameFor(model => model.DepartmentName)
</div>
<div class="display-field">
#Html.DisplayFor(model => model.DepartmentName)
</div>
<div class="display-label">
#Html.DisplayNameFor(model => model.DepartmentID)
</div>
<div class="display-field">
#Html.DisplayFor(model => model.DepartmentID)
</div>
</fieldset>
#using (Html.BeginForm())
{
<p>
<input type="submit" value="Delete" /> |
#Html.ActionLink("Back to List", "Index")
</p>
}
When i click delete on the delete view the following error occurs:
to action method's second parameter "collection", it sends a collection with zeros and null in their properties
does not delete the record
Here is video that shows the problem
why this happens and how do i fix this?
To Steve:
I have made the changes by creating a separate view model and a delete action:
ViewModel:
public class EmpDeleteCommitViewModel
{
public int EmpID { get; set; }
}
the actions Delete methods:
[HttpGet]//this works fine, gets the record to show on view
public ActionResult Delete(int id)
{
var empList = db.Employees.ToList();
var employee = empList.Where(e => e.EmpId == id).Select(e => new EmployeeDeleteViewModel
{
EmpID=e.EmpId,
FirstName= e.FirstName,
LastName=e.LastName,
DepartmentID=e.DepartmentId,
DepartmentName=e.Department.Name
}).FirstOrDefault();
return View(employee);
}
[HttpPost] //BUT THIS DOES NOT WORK!, evm EmpID does not contain id value
public ActionResult Delete(EmpDeleteCommitViewModel evm)
{
try
{
var employee = db.Employees.Where(e => e.EmpId == evm.EmpID).FirstOrDefault();
db.Employees.Remove(employee);
db.SaveChanges();
return RedirectToAction("Index", new { id = evm.EmpID });
}
catch
{
return View();
}
}
You have no form controls (<input>) between form tags so there is nothing to post back when you submit the form. All you doing is generating some text to display the property values.
There is no need to include the EmployeeDeleteViewModel collection parameter in you method. Your int id parameter will be bound with the id of the employee assuming your using the correct routing, so all you need to to get the original data model from the database based on the id and then delete it.

Using Automapper to Edit selected fields of a model MVC4

I am trying to update selected fields using a viewmodel(OApplyIDViewModel) of the original model(OApply). When I run changes are not effected. I will appreciate help from anyone who is experienced with this. I do not get any error. The form submits and redirects.
I have this at global.asax
AutoMapper.Mapper.CreateMap<OApplyIDViewModel, OApply>();
This is ViewModel
public class OApplyIDViewModel
{
[Key]
public int OAId { get; set; }
[Display(Name = "Identification")]
[Required(ErrorMessage = "Identification Required")]
public int IdId { get; set; }
[Display(Name = "Identification Number")][Required(ErrorMessage="ID Number Required")]
public string AIdentificationNo { get; set; }
[Display(Name = "Licence Version(5b)")]
[RequiredIf("IdId", Comparison.IsEqualTo, 1, ErrorMessage = "Version(5b) Required")]
public string ALicenceVersion { get; set; }
public int CountryId { get; set; }
[RequiredIf("IdId",Comparison.IsNotEqualTo,1, ErrorMessage="Country Required")]
[Display(Name = "Your Electronic Signature Date Seal")]
[Required(ErrorMessage="Date Signature Seal Required")]
public DateTime SigDate { get; set; }
[ScaffoldColumn(false)]
[Display(Name = "Last Updated on")]
public DateTime UDate { get; set; }
[ScaffoldColumn(false)]
[Display(Name = "Last Updated by")]
public String UDateBy { get; set; }
}
This is at Controller
//GET
public ActionResult ClientEditID(int id)
{
var model = new OApplyIDViewModel();
OApply oapply = db.OApply.Find(id);
if (model == null )
{
return HttpNotFound();
}
ViewBag.CountryId = new SelectList(db.Countries, "CountryId", "CountryName", model.CountryId);
ViewBag.IdId = new SelectList(db.PhotoIds, "IdId", "IdName", model.IdId);
return View();
}
[HttpPost]
public ActionResult ClientEditId(OApplyIDViewModel oapply, int Id)
{
if (!ModelState.IsValid)
{
return View(oapply);
}
var onlineid = db.OApply.Where(x => x.OAId == Id).FirstOrDefault();
Mapper.Map<OApplyIDViewModel,OApply>(oapply);
oapply.UDateBy = Membership.GetUser().ProviderUserKey.ToString();
oapply.UDate = DateTime.Now;
db.Entry(onlineid).State= EntityState.Modified;
db.SaveChanges();
ViewBag.CountryId = new SelectList(db.Countries, "CountryId", "CountryName", oapply.CountryId);
ViewBag.IdId = new SelectList(db.PhotoIds, "IdId", "IdName", oapply.IdId);
return RedirectToAction("HomeAccount");
}
This is the View
#using (Html.BeginForm("ClientEditId", "OApply", FormMethod.Post, new { enctype = "multipart/form-data", #class = "stdform" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>OnlineApplication</legend>
#Html.HiddenFor(model => model.OAId)
<div class="related">
<div class="editor-label">
#Html.LabelFor(model => model.IdId, "IdType")
</div>
<div class="editor-field">
#Html.DropDownList("IdId", String.Empty)
#Html.ValidationMessageFor(model => model.IdId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.AIdentificationNo)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.AIdentificationNo)
#Html.ValidationMessageFor(model => model.AIdentificationNo)
</div>
<div class="requiredfields">
<div class="editor-label">
#Html.LabelFor(model => model.ALicenceVersion)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ALicenceVersion)
#Html.ValidationMessageFor(model => model.ALicenceVersion)
</div>
</div>
<div class="country">
<div class="editor-label">
#Html.LabelFor(model => model.CountryId)
</div>
<div class="editor-field">
#Html.DropDownList("CountryId")
#Html.ValidationMessageFor(model => model.CountryId)
</div>
</div></div>
<div class="editor-label">
#Html.LabelFor(model => model.SigDate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.SigDate)
#Html.ValidationMessageFor(model => model.SigDate)
</div>
<p>
<input type="submit" value="Save" />
</p>
There is something wrong here:
var onlineid = db.OApply.Where(x => x.OAId == Id).FirstOrDefault();
-> Mapper.Map<OApplyIDViewModel,OApply>(oapply);
oapply.UDateBy = Membership.GetUser().ProviderUserKey.ToString();
oapply.UDate = DateTime.Now;
db.Entry(onlineid).State= EntityState.Modified;
The line with the arrow returns a new OApply instance. It does not update onlineid. In fact, it has no idea about onlineid. Try the following.
Mapper.Map<OApplyIDViewModel,OApply>(oapply, onlineid);
Now it will modify onlineid instead of returning one. However, you should ignore the mapping for the primary key if it is an identity (auto-incrementing) one.
AutoMapper.Mapper.CreateMap<OApplyIDViewModel, OApply>()
.ForMember(dest => dest.OAId, opt => opt.Ignore());
I am not sure if OAId is your primary key or not. You are not following naming conventions and probably some other conventions too, at all.
I have made corrections in your code :
public ActionResult ClientEditID(int id)
{
OApply oapply = db.OApply.Find(id);
->if (oapply == null )
{
return HttpNotFound();
}
->var model = Mapper.Map<OApply, OApplyIDViewModel>(oapply);
ViewBag.CountryId = new SelectList(db.Countries, "CountryId", "CountryName", model.CountryId);
ViewBag.IdId = new SelectList(db.PhotoIds, "IdId", "IdName", model.IdId);
->return View(model);
}
Your HttpPost is mostly valid, except that you put data into ViewBag before you use RedirectToAction(). That data will be lost. Instead, use TempData dictionary. Check msdn.