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

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

Related

MVC save image to DB (with EF)

I am still new to MVC and am struggling with uploading an image to a DB in my web application. I've seen tons of articles on the subject, followed instructions but still I am stuck with the following error message:
The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.
Whenever I use [Bind(Exclude = "CompetitionPicture")], everything would work fine, except for, of course, the "CompetitionPicture" won't be included.
My ViewModel looks as follows:
public class PhotoCompetition
{
public int ID { get; set; }
public string UserID { get; set; }
public string FirstName { get; set; }
public string Email { get; set; }
public byte[] CompetitionPicture { get; set; }
[Required]
[Display(Name = "by checking this box I accept the Terms & Conditions")]
public bool TermsAndConditionsAccepted { get; set; }
public DateTime TimeStamp { get; set; }
}
Controller is as follows:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult UploadCompetitionPicture(/*[Bind(Exclude = "CompetitionPicture")]*/ PhotoCompetition model)
{
string test = Request.Form["CompetitionPicture"];
byte[] bt = Convert.FromBase64String(test.Split(',')[1]);
var participation = new PhotoCompetition
{
CompetitionPicture = bt
};
// var participation = new PhotoCompetition
// {
// UserID = User.Identity.GetUserId(),
// Email = User.Identity.GetUserName(),
// TermsAndConditionsAccepted = model.TermsAndConditionsAccepted,
// TimeStamp = DateTime.UtcNow.ToUniversalTime()
// };
// participation.CompetitionPicture = competitionPicture;
// DB.PhotoCompetition.Add(model);
// DB.SaveChanges();
// return RedirectToAction("Index");
//}
return View(model);
}
and the View:
<section id="photoCompetition" class="manageForm">
#using (Html.BeginForm("UploadCompetitionPicture", "errandom", FormMethod.Post, new { #id = "photoCompetitionForm", #class = "form-horizontal", #role = "form", #enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div id="photoCompetitionSection" class="manageSection">
<p id="photoCompetitionSectionTitle" class="manageSectionTitle">
Upload your picture and be selected as our model!
</p>
#Html.HiddenFor(m => m.UserID)
#Html.HiddenFor(m => m.Email)
#Html.HiddenFor(m => m.FirstName)
#Html.HiddenFor(m => m.TimeStamp)
<div id="photoCompetitionProfilePictureArea" class="manageArea row">
#Html.LabelFor(m => m.CompetitionPicture, new { #id = "photoCompetitionProfilePictureLabel", #class = "manageLabel col-xs-offset-1 col-xs-10 col-sm-offset-1 col-sm-10 col-md-offset-1 col-md-3 col-lg-offset-1 col-lg-4" })
<a id="photoCompetitionProfilePictureSelectionButton" class="manageField col-xs-offset-1 col-xs-10 col-sm-offset-1 col-sm-10 col-md-offset0 col-md-7 col-lg-offset-0 col-lg-6" href="#">
select a file...
</a>
#Html.TextBoxFor(m => m.CompetitionPicture, new { #id = "photoCompetitionProfilePictureField", #class = "manageField col-xs-offset-1 col-xs-10 col-sm-offset-1 col-sm-10 col-md-offset-0 col-md-7 col-lg-offset-0 col-lg-6", #name = "CompetitionPicture", #type = "file", #style = "display: none" })
</div>
<div id="photoCompetitionTermsAndConditionsArea" class="manageArea row">
#Html.CheckBoxFor(m => m.TermsAndConditionsAccepted, new { #id = "photoCompetitionTermsAndConditionsField", #class = "photoCompetitionTermsAndConditionsField" })
#Html.LabelFor(m => m.TermsAndConditionsAccepted, new { #id = "photoCompetitionTermsAndConditionsLabel", #class = "photoCompetitionTermsAndConditionsLabel" })
</div>
<script>
jQuery("#photoCompetitionProfilePictureSelectionButton").click(function () {
$("#photoCompetitionProfilePictureField").click();
});
</script>
<script>
$("#photoCompetitionProfilePictureField").change(function () {
var fullFileName = $("#photoCompetitionProfilePictureField").val()
$("#photoCompetitionProfilePictureSelectionButton").html(fullFileName.substr(fullFileName.lastIndexOf('\\') + 1));
});
</script>
<div id="photoCompetitionButtonArea" class="manageArea row">
<input id="photoCompetitionButtonUpload" class="manageButton col-xs-offset-1 col-xs-10 col-sm-offset-1 col-sm-10 col-md-offset-1 col-md-10 col-lg-offset-1 col-lg-10" type="submit" value="Save" />
</div>
</div>
}
</section>
Your support is highly appreciated!
I'm stuck in this issue for days before, i will put simple solving.
first: I save image path as string in DB not byte
and Image File as HttpPostedFileBase in model Like :`
public string ProductImage { get; set; }
[Required(ErrorMessage = "Image is required")]
public HttpPostedFileBase ImageFile { get; set; }
in view you will write :
<div class="form-group">
<div class="col-md-10">
<input type="file" name="ImageFile" required />
</div>
#Html.ValidationMessageFor(model => model.ImageFile, "", new { #class = "text-danger" })
</div>
name of input must be like the name in model and here I named it ImageFile
in controller will write
public void SaveImage(Product pro)
{
string fileName = Path.GetFileNameWithoutExtension(pro.ImageFile.FileName);
string exetention = Path.GetExtension(pro.ImageFile.FileName);
fileName = fileName + DateTime.Now.ToString("yymmssfff") + exetention;
pro.ProductImage = "~/ProductDropBox/" + fileName;
fileName = Path.Combine(Server.MapPath("~/ProductDropBox/"), fileName);
pro.ImageFile.SaveAs(fileName);
}
I use this function to save image in DB and in Folder created for images
finally in action result you will write :
public ActionResult AddProducts(Product pro)
{
if (ModelState.IsValid)
{
SetCategories();
SaveImage(pro);
pro.UPID = Guid.NewGuid();
using (db)
{
db.Products.Add(pro);
db.SaveChanges();
}
return RedirectToAction("Index");
}
else
{
return View("AddProducts",pro);
}
}
I hope this useful

ASP.NET MVC - Object reference not set to an instance of an object in DropDownList

I have a model Class
public partial class FEES
{
public FEES()
{
}
public long FEE_ID { get; set; }
public decimal AMOUNT { get; set; }
public int CURRENCY_ID { get; set; }
public string NAME { get; set; }
public virtual CURRENCIES CURRENCIES { get; set; }
}
ViewModel
public class FeesViewModel
{
public SelectList CurrenciesList { get; set; }
public FeesViewModelInput input { get; set; }
public class FeesViewModelInput
{
[HiddenInput]
public long FEE_ID { get; set; }
[Display(Name = "Amount")]
[Required(ErrorMessage = "Fee Amount Is Required!")]
[RegularExpression(#"^[0-9,.]+$", ErrorMessage = "Please enter proper currency format e.g. 2,500")]
public decimal AMOUNT { get; set; }
[Display(Name = "Currency")]
[Required(ErrorMessage = "Currency Is Required!")]
public int CURRENCY_ID { get; set; }
[Required(ErrorMessage = "Fee Name Is Required!")]
[Display(Name = "Fee Name")]
public string NAME { get; set; }
}
}
Small service for the ViewModel
public void createFees(FEES fee, FeesViewModel viewModel)
{
fee.FEE_ID = viewModel.input.FEE_ID;
fee.CURRENCY_ID = viewModel.input.CURRENCY_ID;
fee.NAME = viewModel.input.NAME.Trim();
}
I call the service and the ViewModel in my controller.
Controller
public ActionResult Create()
{
FeesViewModel fees = new FeesViewModel();
fees.CurrenciesList = new SelectList(_currenciesService.GetCurrencies().Where(c => c.ACTION_STATUS != 2), "CURRENCY_ID", "CURRENCY_NAME");
fees.FeeTypesList = new SelectList(_feetypesService.GetFeeTypes().Where(c => c.ACTION_STATUS != 2), "FEE_TYPE_ID", "FEE_TYPE_NAME");
return View();
}
[HttpPost]
public ActionResult Create(FeesViewModel fees)
{
try
{
if (ModelState.IsValid)
{
//check if values is duplicate
if (_feesService.GetFees().Where(c => c.ACTION_STATUS != 2).Any(c => c.NAME.ToLower().Trim() == fees.input.NAME.ToLower().Trim()))
{
this.AddNotification("Fee Name already exist.<br/> Kindly verify the data.", NotificationType.ERROR);
}
else
{
var fee = new BPP.CCSP.Admin.Web.BPPCCSPAdminFeesService.FEES();
var helper = new FeesService();
helper.createFees(fee, fees);
_feesService.AddFee(fee);
var notif = new UINotificationViewModel()
{
notif_message = "Record saved successfully",
notif_type = NotificationType.SUCCESS,
};
TempData["notif"] = notif;
return RedirectToAction("Index");
}
}
}
catch (Exception e)
{
this.AddNotification("Fees cannot be added.<br/> Kindly verify the data.", NotificationType.ERROR);
}
fees.CurrenciesList = new SelectList(_currenciesService.GetCurrencies().Where(c => c.ACTION_STATUS != 2), "CURRENCY_ID", "CURRENCY_NAME");
return View(fees);
}
And the View
#model BPP.CCSP.Admin.Web.ViewModels.FeesViewModel
#{
//ViewBag.Title = "Create";
}
<div class=" box box-body box-primary">
#using (Html.BeginForm("Create", "Fees", FormMethod.Post, new { #class = "form-horizontal", #enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, null, new { #class = "text-danger" })
#*#Html.HiddenFor(model => model.faculty_activation_date, new { #Value = System.DateTime.Now })*#
<div class="row .col">
<div style="margin-top:20px" class="mainbox col-md-12 col-md-offset-0 col-sm-8 col-sm-offset-2">
<div class="panel panel-info">
<div class="panel-heading">
<div class="panel-title">Create Fee</div>
</div>
<div class="panel-body">
<div class="col-md-6">
<div>
#Html.LabelFor(model => model.input.NAME, "Fee Name")
#Html.TextBoxFor(model => model.input.NAME, new { #style = "border-radius:3px;", #type = "text", #class = "form-control", #placeholder = Html.DisplayNameFor(m => m.input.NAME), #autocomplete = "on" })
#Html.ValidationMessageFor(model => model.input.NAME, null, new { #class = "text-danger" })
</div>
<div>
#Html.LabelFor(model => model.input.AMOUNT, "Amount")
#Html.TextBoxFor(model => model.input.AMOUNT, new { #style = "border-radius:3px;", #type = "text", #class = "form-control", #placeholder = Html.DisplayNameFor(m => m.input.AMOUNT), #autocomplete = "on" })
#Html.ValidationMessageFor(model => model.input.AMOUNT, null, new { #class = "text-danger" })
</div>
</div>
<div class="col-md-6">
<div>
#Html.LabelFor(model => model.input.CURRENCY_ID, "Currency")
#*#Html.DropDownList("CURRENCY_ID", (IEnumerable<SelectListItem>)ViewBag.name, "Please Select a Currency", new { #class = "form-control", #style = "border-radius:3px;" })*#
#Html.DropDownListFor(x => x.input.CURRENCY_ID, Model.CurrenciesList, "Please Select a Currency", new { #class = "form-control", #style = "border-radius:3px;" })
#Html.ValidationMessageFor(model => model.input.CURRENCY_ID, null, new { #class = "text-danger" })
</div>
<div>
#Html.LabelFor(model => model.input.FEE_TYPE_ID, "Fee Type")
#Html.DropDownListFor(model => model.input.FEE_TYPE_ID, Model.FeeTypesList, "Please Select a Fee Type", new { #class = "form-control", #style = "border-radius:3px;" })
#Html.ValidationMessageFor(model => model.input.FEE_TYPE_ID, null, new { #class = "text-danger" })
</div>
</div>
</div>
<div class="panel-footer">
<div class="panel-title">
<div class="form-actions no-color">
<input type="submit" value="Create" class="btn btn-success" />
</div>
</div>
</div>
</div>
</div>
</div>
</div>
}
</div>
}
When I clicked on the View (Create), I got this error
The CurrencyID is a DropDownList coming from CURRENCIES model class.
I have these questions:
Why am I getting this error and how do I resolve it.
How do I do ViewModel without mapping.?
Why am I getting this error and how do I resolve it.
Because the Model is not set in your view. It is null.
When the users visit the Create page, you need to make sure to present them with options in the dropdown. Therefore, you need to make sure you pass the model into the view during GET.
public ActionResult Create()
{
// your code and pass fees to your view.
return View(fees);
}
How do I do ViewModel without mapping. Any example please.
You can use AutoMapper NuGet package to do the mapping.

How to get My Purchase Class to map to a Specific ApplicationUser

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.

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)

i would like to populate one drop down list using another drop down list

i tried a code from a video and got an error :
This is the Error when i click my drop down to display the states from country
this is my controller code:
public ActionResult Submit()
{
List<Country> allCountry = new List<Country>();
List<State> allState = new List<State>();
using (DropDownTestEntities1 dc = new DropDownTestEntities1())
{
allCountry = dc.Countries.OrderBy(a => a.CountryName).ToList();
}
ViewBag.CountryID = new SelectList(allCountry, "CountryID", "CountryName");
ViewBag.StateID = new SelectList(allState, "StateID", "StateName");
return View();
}
[HttpPost]
[ValidateAntiForgeryToken] // this is for prevent CSRF Attack
public ActionResult Submit(Feedback fb)
{
List<Country> allCountry = new List<Country>();
List<State> allState = new List<State>();
using (DropDownTestEntities1 dc = new DropDownTestEntities1())
{
allCountry = dc.Countries.OrderBy(a => a.CountryName).ToList();
if (fb != null && fb.CountryID > 0)
{
allState = dc.States.Where(a => a.CountryID.Equals(fb.CountryID)).OrderBy(a => a.StateName).ToList();
}
}
ViewBag.CountryID = new SelectList(allCountry, "CountryID", "CountryName", fb.CountryID);
ViewBag.StateID = new SelectList(allState, "StateID", "StateName", fb.StateID);
if (ModelState.IsValid)
{
using (DropDownTestEntities1 dc = new DropDownTestEntities1())
{
dc.Feedbacks.Add(fb);
dc.SaveChanges();
ModelState.Clear();
fb = null;
ViewBag.Message = "Successfully submitted";
}
}
else
{
ViewBag.Message = "Failed! Please try again";
}
return View(fb);
}
[HttpGet]
public JsonResult GetStates(string countryID = "")
{
List<State> allState = new List<State>();
int ID = 0;
if (int.TryParse(countryID, out ID))
{
using (DropDownTestEntities1 dc = new DropDownTestEntities1())
{
allState = dc.States.Where(a => a.CountryID.Equals(ID)).OrderBy(a => a.StateName).ToList();
//allState = dc.States.Where(a => a.CountryID.Equals(ID)).OrderBy(a => a.StateName).ToList();
}
}
if (Request.IsAjaxRequest())
{
return new JsonResult
{
Data = allState,
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
else
{
return new JsonResult
{
Data = "Not valid request",
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
}
}
}
this is my model code :
public partial class Feedback
{
public int FeedbackID { get; set; }
[Display(Name = "Full Name")]
[Required(ErrorMessage = "Please provide your fullname", AllowEmptyStrings = false)]
public string FullName { get; set; }
[Display(Name = "Mobile No")]
public string MobileNo { get; set; }
[Display(Name = "Country")]
[Required(ErrorMessage = "Please select country", AllowEmptyStrings = false)]
public int CountryID { get; set; }
[Display(Name = "State")]
[Required(ErrorMessage = "Please select state", AllowEmptyStrings = false)]
public int StateID { get; set; }
}
and this is my view with my ajax code :
#using (Html.BeginForm("Submit", "Feedback", FormMethod.Post))
{
#Html.ValidationSummary(true)
#Html.AntiForgeryToken()
<fieldset>
<legend>Feedback</legend>
#if (ViewBag.Message != null)
{
<div style="border:solid 1px black">
#ViewBag.Message
</div>
}
<div class="editor-label">
#Html.LabelFor(model => model.FullName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.FullName)
#Html.ValidationMessageFor(model => model.FullName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.MobileNo)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.MobileNo)
#Html.ValidationMessageFor(model => model.MobileNo)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.CountryID)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.CountryID, #ViewBag.CountryID as SelectList, "Select Country")
#Html.ValidationMessageFor(model => model.CountryID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.StateID)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.StateID, #ViewBag.StateID as SelectList, "Select State")
#Html.ValidationMessageFor(model => model.StateID)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
<script language="javascript">
$(document).ready(function () {
$("#CountryID").change(function () {
// this will call when Country Dropdown select change
var countryID = parseInt($("#CountryID").val());
if (!isNaN(countryID)) {
var ddState = $("#StateID");
ddState.empty(); // this line is for clear all items from State dropdown
ddState.append($("<option></option").val("").html("Select State"));
// Here I will call Controller Action via Jquery to load State for selected Country
$.ajax({
url: "#Url.Action("GetStates","Feedback")",
type: "GET",
data: { countryID: countryID },
dataType: "json",
success: function (data) {
$.each(data, function (i, val) {
ddState.append(
$("<option></option>").val(val.StateID).html(val.StateName)
);
});
},
error: function () {
alert("Error!");
}
});
}
});
});
</script>
}
All i want is for my country selecteditem to populate my state with the link between the two. E.g. if i select South Africa, it must only display Gauteng, Cape Town etc.
Please can your'll help me with my error or provide guidance thanks.
Issue 1: Try using == instead of using .Equals() because if CountryIDis null, you'll throw an error.
Issue 2: Change
if (Request.IsAjaxRequest())
{
return new JsonResult
{
Data = allState,
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
to
return Json(allState, JsonRequestBehavior.AllowGet);
Possible Issue 3?
Try this as your success function:
success: function (data) {
$(data).each(function () {
ddState.append(
$("<option></option>").val(this.StateID).html(this.StateName)
);
});
},