I am building an e-store web app using ASP.NET Core 6 MVC. I am trying to do CRUD operations with the help of some tutorial, everything went smoothly, but when I try to edit a product, it makes a copy of that edited item, instead of just replacing it.
Also, it asks me to upload a new image even though I want to set it not to ask for a new one, and there is a default (noimage.png) set if there is no image uploaded. Here is the edit method, please tell me where am going wrong.
public async Task<IActionResult> Edit(int id, Product product)
{
ViewBag.CategoryId = new SelectList(_context.Category.OrderBy(x => x.Sorting),
"Id", "Name", product.CategoryId);
// if (ModelState.IsValid)
// {
product.Slug = product.Name.ToLower().Replace(" ", "-");
var slug = await _context.Product
.Where(x => x.Id != id)
.FirstOrDefaultAsync(x => x.Slug == product.Slug);
if (slug != null)
{
ModelState.AddModelError("", "The product already exists.");
return View(product);
}
// Not mandatory to upload an image when editing
if (product.ImageUpload != null)
{
string uploadsDir = Path.Combine(webHost.WebRootPath, "media/products");
// If the image is not noimage.png then remove the existing image and upload a new one
if (!string.Equals(product.Image, "noimage.png"))
{
string oldImagePath = Path.Combine(uploadsDir, product.Image);
if (System.IO.File.Exists(oldImagePath))
{
System.IO.File.Delete(oldImagePath);
}
}
// Upload new image
string imageName = Guid.NewGuid().ToString() + "_" + product.ImageUpload.FileName;
string filePath = Path.Combine(uploadsDir, imageName);
FileStream fs = new FileStream(filePath, FileMode.Create);
await product.ImageUpload.CopyToAsync(fs);
fs.Close();
product.Image = imageName;
}
_context.Update(product);
await _context.SaveChangesAsync();
TempData["Success"] = "The product has been edited!";
return RedirectToAction("Index");
// }
// return View(product);
}
Product class
public class Product
{
public int Id { get; set; }
[Required, MinLength(2, ErrorMessage = "Minimum length 2")]
public string? Name { get; set; }
public string? Slug { get; set; }
[Column(TypeName = "decimal (18,2)")]
public decimal Price { get; set; }
[Display(Name = "Category")]
[Range(1, int.MaxValue, ErrorMessage = "You must choose a category")] //specific validation for category
public int? CategoryId { get; set; }
[Required, MinLength(2, ErrorMessage = "Minimum length 4")]
public string? Description { get; set; }
public string? Image { get; set; }
//To make the connection
[ForeignKey("CategoryId")]
public virtual Category? Category { get; set; }
[NotMapped] //to not show in the DB
[ImageExtention]
public IFormFile? ImageUpload { get; set; }
}
Edit view
#model Product
#{
ViewData["Title"] = "Edit Product";
}
<h1>Edit Product</h1>
<div class="row">
<div class="col-md-10">
<form asp-action="Edit" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
#*<input type="hidden" asp-for="Image" />*#
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Description" class="control-label"></label>
<textarea asp-for="Description" class="form-control"></textarea>
<span asp-validation-for="Description" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Price" class="control-label"></label>
<input asp-for="Price" class="form-control" />
<span asp-validation-for="Price" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CategoryId" class="control-label"></label>
<select asp-for="CategoryId" asp-items="#ViewBag.CategoryId" class="form-control">
<option value="0">Choose a category</option>
</select>
<span asp-validation-for="CategoryId" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Image" class="control-label">Current Image</label>
<img src="~/media/products/#Model.Image" width="200" alt="" />
</div>
<div class="form-group">
<label asp-for="Image" class="control-label">New Product Image</label>
<input asp-for="ImageUpload" class="form-control" />
<img src="" id="imgpreview" class="pt-2" alt="" />
<span asp-validation-for="ImageUpload" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Edit" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
<script>
$("#ImageUpload").change(function () {
readURL(this);
});
</script>
}
You need to make Id as Primary Key in Product
public class Product
{
[Key]
public int Id { get; set; }
//ommitted
}
You are not posting Id to Edit Method while Edit button clicked. So every time It is sending value of Id as 0. EntityFramework is considering it as new object.
Please add below code to cshtml view inside form. It will send correct value of existing product Id.
<input type="hidden" asp-for="Id" />
Related
On the "Create" view I am trying to enumerate through some secondary/external model data. Instead, the page returns a NullReferenceError and I can't figure out why the Model is null. If I filter for IsNull on the enumerating fields that are null, the page will load.
Below is the Create functions inside the Controller:
public IActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("SOFT_ID,SOFT_NAME,DEPT_ID,IT_CONTACT,SOFT_EXP_DATE,SOFT_SUP_PERIOD,SOFT_OUT_OF_SERVICE,VEND_ID,SUPP_ID,SOFT_IS_RENEWED")] Software software)
{
if (ModelState.IsValid)
{
_context.Add(software);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View();
}
Next are the Models. Below is the Software model, which is the main model, and the Department model which is the secondary models. There are two other models I want to load data from and they are both set up exactly like the Department Model.
Software Model:
public class Software
{
[Key]
public int SOFT_ID { get; set; }
[Required]
[StringLength(50, ErrorMessage = "Software Name cannont be longer than 50 characters.")]
[Display(Name = "Software Name")]
public string SOFT_NAME { get; set; }
[Required]
public virtual Department DEPT_ID { get; set; }
[Required]
[Display(Name = "IT Contact")]
public string IT_CONTACT { get; set; }
[Required]
[Display(Name = "Expiration Date")]
[DataType(DataType.Date)]
public DateTime SOFT_EXP_DATE { get; set; }
[Required]
[Range(1,5)]
[Display(Name = "Support Period")]
public int SOFT_SUP_PERIOD { get; set; }
[Display(Name = "No Longer Used")]
public bool SOFT_OUT_OF_SERVICE { get; set; }
[Required]
public virtual Vendor VEND_ID { get; set; }
[Required]
public virtual Supplier SUPP_ID { get; set; }
[Display(Name = "Renewed?")]
public bool SOFT_IS_RENEWED { get; set; }
public IEnumerable<Department> Departments { get; set; }
public IEnumerable<Supplier> Suppliers { get; set; }
public IEnumerable<Vendor> Vendors { get; set; }
}
Department Model:
public class Department
{
[Key]
public int DEPT_ID { get; set; }
[Required]
[Display(Name = "Department Name")]
public string DEPT_NAME { get; set; }
[Required]
[Display(Name = "Department Contact Name")]
public string DEPT_CONTACT { get; set; }
[Required]
[Display(Name = "Email")]
public string DEPT_EMAIL { get; set; }
[Required]
[Display(Name = "Phone")]
public string DEPT_PHONE { get; set; }
}
And finally there is the View. The sections that are throwing the NullReferenceError are the #foreach loops for the departments, suppliers, and vendors. These are supposed to be populating drop down menus.
#model SoftwareManager_SSO.Models.Software
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Software</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="SOFT_NAME" class="control-label"></label>
<input asp-for="SOFT_NAME" class="form-control" />
<span asp-validation-for="SOFT_NAME" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="DEPT_ID.DEPT_NAME" class="control-label"></label>
<select class="form-control">
<option value="" disabled selected>Select A Dept</option>
#foreach (var item in Model.Departments)
{
<option value="#item.DEPT_ID">#item.DEPT_NAME</option>
}
</select>
<span asp-validation-for="DEPT_ID.DEPT_NAME"></span>
</div>
<div hidden="hidden" class="form-group">
<label asp-for="DEPT_ID" class="control-label"></label>
<input asp-for="DEPT_ID" class="form-control" disabled="disabled" />
<span asp-validation-for="DEPT_ID" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="DEPT_ID.DEPT_CONTACT" class="control-label"></label>
<input id="dept_contact" asp-for="DEPT_ID.DEPT_CONTACT" disabled="disabled" class="form-control" />
<span asp-validation-for="DEPT_ID.DEPT_CONTACT" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="IT_CONTACT" class="control-label"></label>
<select class="form-control">
<option value="" selected>Select IT Person</option>
<option value="uciltas#tohowater.com">Umit Ciltas</option>
<option value="sslaven#tohowater.com">Stephen Slaven</option>
<option value="imoreno#tohowater.com">Ismael Moreno</option>
<option value="dkearney#tohowater.com">David Kearney</option>
</select>
<span asp-validation-for="IT_CONTACT" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="SOFT_EXP_DATE" class="control-label"></label>
<input asp-for="SOFT_EXP_DATE" class="form-control" />
<span asp-validation-for="SOFT_EXP_DATE" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="SOFT_SUP_PERIOD" class="control-label"></label>
<select class="form-control">
<option value="" selected>Select A Support Period</option>
<option value="1">1 Year</option>
<option value="2">2 Years</option>
<option value="3">3 Years</option>
<option value="4">4 Years</option>
<option value="5">5 Years</option>
</select>
<span asp-validation-for="SOFT_SUP_PERIOD" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="VEND_ID.VEND_NAME" class="control-label"></label>
<select class="form-control">
<option value="" disabled selected>Select a Vendor</option>
#foreach (var item in Model.Vendors)
{
<option value="#item.VEND_ID">#item.VEND_NAME</option>
}
</select>
<span asp-validation-for="VEND_ID.VEND_NAME"></span>
</div>
<div class="form-group">
<label asp-for="SUPP_ID.SUPP_NAME" class="control-label"></label>
<select class="form-control">
<option value="" disabled selected>Select a Vendor</option>
#foreach (var item in Model.Suppliers)
{
<option value="#item.SUPP_ID">#item.SUPP_NAME</option>
}
</select>
<span asp-validation-for="SUPP_ID.SUPP_NAME"></span>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="SOFT_IS_RENEWED" /> #Html.DisplayNameFor(model => model.SOFT_IS_RENEWED)
</label>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="SOFT_OUT_OF_SERVICE" /> #Html.DisplayNameFor(model => model.SOFT_OUT_OF_SERVICE)
</label>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
I am quite honestly at a loss and can't figure this out. I've read through numerous other posts on this site and I can't figure out what is different between my code and similar code that I've seen posted. I will continue to search though posts, but any help is appreciated.
You need to pass the information about those departments into the view. This is typically done by creating a viewmodel and passing it to the view.
In your code, your view is expecting a "software" model, so you could create one (and be sure to populate "departments") and pass that into your view.
Once you have the model, you could use a Html helper to generate the drop-down, rather than manually building it using a loop.
For example:
#Html.DropDownListFor(model => model.departments )
Or maybe
#Html.DropDownListFor(model => model.departments as selectlist)
Sorry if some syntax not quite right, on my mobile so can't check very well. But that should give you the right idea.
EDIT
You need to build the view model in your controller and pass it to the view.
var viewModel = new Software();
viewModel.Departments = new List<Department>() {
new Department() { DEPT_NAME = "Some department name"}
};
return View(viewModel);
Or if pulling dynamically from database (for example via entity framework):
var viewModel = new Software();
viewModel.Departments = _db.Departments.ToList()
return View(viewModel);
I have been trying to create an application with the CodeFirst approach with MVC Core and using the Entity Framework and a ViewModel.
The issue is that nothing populates in the branch dropdown.
For the life of me I cannot figure out what I am doing wrong, but I suspect that the problem in my controller.
Please help.
My Models
public class Intermediary
{
public int IntermediaryID {get; set; }
public string RegisteredName {get; set; }
public string TradingName {get; set; }
public DateTime CreatedDate{get; set; }
public stringCreatedBy {get; set; }
}
public class Branch
{
public int BranchID {get; set; }
public string Name {get; set; }
public DateTime CreatedDate{get; set; }
public stringCreatedBy {get; set; }
}
My ViewModel
public class IntermediaryIndexData
{
public IEnumerable<Intermediary> Intermediaries { get; set; }
public IEnumerable<Branch> Branches { get; set; }
}
My Controller
namespace BizDevHub.Controllers
{
public class IntermediariesController : Controller
{
private readonly BizDevHubContext _context;
public IntermediariesController(BizDevHubContext context)
{
_context = context;
}
// GET: Intermediaries
public async Task<IActionResult> Index(int? id, int? intermediaryID)
{
var viewModel = new IntermediaryIndexData();
viewModel.Intermediaries = await _context.Intermediaries
.Include(i => i.Branch)
.AsNoTracking()
.OrderBy(i => i.RegisteredName)
.ToListAsync();
//if (id != null)
//{
// ViewData["IntermdiaryID"] = id.Value;
// Instructor instructor = viewModel.Instructors.Where(
// i => i.ID == id.Value).Single();
// viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
//}
//if (courseID != null)
//{
// ViewData["CourseID"] = courseID.Value;
// viewModel.Enrollments = viewModel.Courses.Where(
// x => x.CourseID == courseID).Single().Enrollments;
//}
return View(viewModel);
}
// GET: Intermediaries/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var intermediary = await _context.Intermediaries
.Include(c => c.Branch)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.IntermediaryID == id);
if (intermediary == null)
{
return NotFound();
}
return View(intermediary);
}
// GET: Intermediaries/Create
public IActionResult Create()
{
PopulateBranchesDropDownList();
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("IntermediaryID,RegisteredName,TradingName,Registration,VATNumber,FSPNumber,CreatedDate,CreatedBy,BranchID,AgreementID")] Intermediary intermediary)
{
if (ModelState.IsValid)
{
_context.Add(intermediary);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
PopulateBranchesDropDownList(intermediary.BranchID);
return View(intermediary);
}
// GET: Intermediaries/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var intermediary = await _context.Intermediaries
.AsNoTracking()
.FirstOrDefaultAsync(m => m.IntermediaryID == id);
if (intermediary == null)
{
return NotFound();
}
PopulateBranchesDropDownList(intermediary.BranchID);
return View(intermediary);
}
[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditPost(int? id)
{
if (id == null)
{
return NotFound();
}
var intermediaryToUpdate = await _context.Intermediaries
.FirstOrDefaultAsync(c => c.IntermediaryID == id);
if (await TryUpdateModelAsync<Intermediary>(intermediaryToUpdate,
"",
c => c.RegisteredName,
c => c.TradingName,
c => c.Registration,
c => c.VATNumber,
c => c.FSPNumber,
c => c.CreatedBy,
c => c.CreatedDate,
c => c.BranchID,
c => c.AgreementID
))
{
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateException /* ex */)
{
//Log the error (uncomment ex variable name and write a log.)
ModelState.AddModelError("", "Unable to save changes. " + "Try again, and if the problem persists, " + "see your system administrator.");
}
return RedirectToAction(nameof(Index));
}
PopulateBranchesDropDownList(intermediaryToUpdate.BranchID);
ViewData["BranchID"] = new SelectList(_context.Branches, "BranchID", "Name", intermediaryToUpdate.BranchID);
return View(intermediaryToUpdate);
}
// GET: Intermediaries/Delete/5
public async Task<IActionResult> Delete(int? id)
{
if (id == null)
{
return NotFound();
}
var intermediary = await _context.Intermediaries
.Include(c => c.Branch)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.IntermediaryID == id);
if (intermediary == null)
{
return NotFound();
}
return View(intermediary);
}
// POST: Intermediaries/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
var intermediary = await _context.Intermediaries.FindAsync(id);
_context.Intermediaries.Remove(intermediary);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
private bool IntermediaryExists(int id)
{
return _context.Intermediaries.Any(e => e.IntermediaryID == id);
}
private void PopulateBranchesDropDownList(object selectedBranch = null)
{
var branchesQuery = from d in _context.Branches
orderby d.Name
select d;
ViewBag.DepartmentID = new SelectList(branchesQuery.AsNoTracking(), "BranchID", "Name", selectedBranch);
}
}
}
My Views
I added the views using scaffolding
Index
#model BizDevHub.Models.ViewModels.IntermediaryIndexData
#{
ViewData["Title"] = "Intermediaries";
}
<h2>Intermediary</h2>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>Registered Name</th>
<th>Trading As</th>
<th>Created Date</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.Intermediaries)
{
string selectedRow = "";
if (item.IntermediaryID == (int?)ViewData["IntermediaryID"])
{
selectedRow = "success";
}
<tr class="#selectedRow">
<td>
#Html.DisplayFor(modelItem => item.RegisteredName)
</td>
<td>
#Html.DisplayFor(modelItem => item.TradingName)
</td>
<td>
#Html.DisplayFor(modelItem => item.FSPNumber)
</td>
<td>
#Html.DisplayFor(modelItem => item.Registration)
</td>
<td>
#Html.DisplayFor(modelItem => item.VATNumber)
</td>
<th>
#Html.DisplayNameFor(model => item.Branch)
</th>
<td>
<a asp-action="Edit" asp-route-id="#item.IntermediaryID">Edit</a> |
<a asp-action="Details" asp-route-id="#item.IntermediaryID">Details</a> |
<a asp-action="Delete" asp-route-id="#item.IntermediaryID">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Edit
#model BizDevHub.Models.Intermediary
#{
ViewData["Title"] = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Edit</h2>
<h4>Intermediary</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="IntermediaryID" />
<div class="form-group">
<label asp-for="RegisteredName" class="control-label"></label>
<input asp-for="RegisteredName" class="form-control" />
<span asp-validation-for="RegisteredName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="TradingName" class="control-label"></label>
<input asp-for="TradingName" class="form-control" />
<span asp-validation-for="TradingName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Registration" class="control-label"></label>
<input asp-for="Registration" class="form-control" />
<span asp-validation-for="Registration" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="VATNumber" class="control-label"></label>
<input asp-for="VATNumber" class="form-control" />
<span asp-validation-for="VATNumber" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="FSPNumber" class="control-label"></label>
<input asp-for="FSPNumber" class="form-control" />
<span asp-validation-for="FSPNumber" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CreatedDate" class="control-label"></label>
<input asp-for="CreatedDate" class="form-control" />
<span asp-validation-for="CreatedDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CreatedBy" class="control-label"></label>
<input asp-for="CreatedBy" class="form-control" />
<span asp-validation-for="CreatedBy" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Branch" class="control-label"></label>
<select asp-for="BranchID" class="form-control" asp-items="ViewBag.BranchID">
<option value=""></option>
</select>
<span asp-validation-for="BranchID" class="text-danger" />
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
#model BizDevHub.Models.Intermediary
Create
#model BizDevHub.Models.Intermediary
#{
ViewData["Title"] = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Create</h2>
<h4>Intermediary</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="RegisteredName" class="control-label"></label>
<input asp-for="RegisteredName" class="form-control" />
<span asp-validation-for="RegisteredName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="TradingName" class="control-label"></label>
<input asp-for="TradingName" class="form-control" />
<span asp-validation-for="TradingName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Registration" class="control-label"></label>
<input asp-for="Registration" class="form-control" />
<span asp-validation-for="Registration" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="VATNumber" class="control-label"></label>
<input asp-for="VATNumber" class="form-control" />
<span asp-validation-for="VATNumber" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="FSPNumber" class="control-label"></label>
<input asp-for="FSPNumber" class="form-control" />
<span asp-validation-for="FSPNumber" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CreatedBy" class="control-label"></label>
<input asp-for="CreatedBy" class="form-control" />
<span asp-validation-for="CreatedBy" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Branches" class="control-label"></label>
<select asp-for="BranchID" class="form-control" asp-items="ViewBag.BranchID">
<option value="">-- Select Branch --</option>
</select>
<span asp-validation-for="BranchID" class="text-danger" />
</div>
#*<div class="form-group">
<label asp-for="AgreementID" class="control-label"></label>
<input asp-for="AgreementID" class="form-control" />
<span asp-validation-for="AgreementID" class="text-danger"></span>
</div>*#
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
For the life of me I cannot figure out what I am doing wrong, but I suspect that the problem in my controller.
Please help.
It seems that there is a one-to-many relationship between Branch and Intermediary , so your model design should be like below:
public class Intermediary
{
public int IntermediaryID { get; set; }
public string RegisteredName { get; set; }
public string TradingName { get; set; }
public DateTime CreatedDate { get; set; }
public string CreatedBy {get; set; }
public int BranchID { get; set; }
public Branch Branch { get; set; }
}
public class Branch
{
public int BranchID { get; set; }
public string Name { get; set; }
public DateTime CreatedDate { get; set; }
public string CreatedBy {get; set; }
public List<Intermediary> Intermediaries { get; set; }
}
->>The issue is that nothing populates in the branch dropdown.
The key-name of ViewBagb in the view and the controller are inconsistent, you should change PopulateBranchesDropDownList method
private void PopulateBranchesDropDownList(object selectedBranch = null)
{
var branchesQuery = from d in _context.Branches
orderby d.Name
select d;
ViewBag.BranchID= new SelectList(branchesQuery.AsNoTracking(), "BranchID", "Name", selectedBranch);
}
You could refer to here for more details about relationships in EF Core.
I notice that remote validation in ASP.NET core always fails because the server always receives a null IFormFile in the controller method. Is there a way to make it work?. Some code to reproduce the problem:
The model class ("not mapped" was included so Entity Framework doesn't interfere, but it also didn't work in another project without Entity Framework.
public class Movie
{
public int ID { get; set; }
[Sacred(sacredWord: "sonda")]
public string Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
[Remote(action: "VerifyRating", controller: "Movies")]
public string Rating { get; set; }
[NotMapped]
[Remote(action: "VerifyFile", controller: "Movies"),Required]
public IFormFile File { get; set; }
}
The controller
public class MoviesController : Controller
{
private readonly WebAppMVCContext _context;
public MoviesController(WebAppMVCContext context)
{
_context = context;
}
// GET: Movies/Create
public IActionResult Create()
{
return View();
}
[AcceptVerbs("Get", "Post")]
public IActionResult VerifyFile(IFormFile File)
{
if(File == null)
{
return Json("The file is null");
}
else
{
return Json("The file is not null");
}
}
// POST: Movies/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 async Task<IActionResult> Create([Bind("ID,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
if (ModelState.IsValid)
{
_context.Add(movie);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(movie);
}
[AcceptVerbs("Get", "Post")]
public IActionResult VerifyRating( int rating)
{
if(rating>0 && rating < 10)
{
return Json(true);
}
else
{
return Json($"The rating is invalid");
}
}
and the View
#model WebAppMVC.Models.Movie
#{
ViewData["Title"] = "Create";
}
<h2>Create</h2>
<h4>Movie</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Title" class="control-label"></label>
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ReleaseDate" class="control-label"></label>
<input asp-for="ReleaseDate" class="form-control" />
<span asp-validation-for="ReleaseDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Genre" class="control-label"></label>
<input asp-for="Genre" class="form-control" />
<span asp-validation-for="Genre" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Price" class="control-label"></label>
<input asp-for="Price" class="form-control" />
<span asp-validation-for="Price" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Rating" class="control-label"></label>
<input asp-for="Rating" class="form-control" />
<span asp-validation-for="Rating" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="File" class="control-label"></label>
<input asp-for="File" />
<span asp-validation-for="File" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts{
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script type="text/javascript">
$.validator.addMethod('sacred',
function (value, element, params) {
var title = $(params[0]).val(),
sacredword = params[1];
if (title!=null && title == sacredword) {
return true;
}
else {
return false;
}
}
);
$.validator.unobtrusive.adapters.add('sacred',
['sacredword'],
function (options) {
var element = $(options.form).find('input#Title')[0];
options.rules['sacred'] = [element, options.params['sacredword']];
options.messages['sacred'] = options.message;
}
);
</script>
}
Notice that all the other validations work (including the remote validation "VerifyRating").
I want to upload an image file and add it to SQLite. I'm new to ASP .Net Core MVC and i'm following the tutorials from https://learn.microsoft.com/en-us/aspnet/core/tutorials/first-mvc-app-mac/?view=aspnetcore-2.0.
I have added an input field to upload files in the View. However, I'm not sure how to go ahead with the Controller and Model. Tried searching everywhere.
One approach is to convert the image to byte. I'm not sure how can I do that in my case.
Any help appreciated :)
Model:
public class Movie
{
public int ID { get; set; }
[StringLength(60, MinimumLength = 3)]
[Required]
public string Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
// use only letters (white space, numbers and special characters are not allowed).
[RegularExpression(#"^[A-Z]+[a-zA-Z""'\s-]*$")]
[Required]
[StringLength(30)]
public string Genre { get; set; }
[Range(1, 100)]
[DataType(DataType.Currency)]
public decimal Price { get; set; }
[RegularExpression(#"^[A-Z]+[a-zA-Z""'\s-]*$")]
[StringLength(5)]
[Required]
public string Rating { get; set; }
[DataType(DataType.Upload)]
[Display(Name = "Upload File")]
// [Required(ErrorMessage = "Please choose file to upload.")]
public string Poster { get; set; }
}
Controller:
public async Task<IActionResult> Edit(int id, [Bind("ID,Title,ReleaseDate,Genre,Price,Rating,Poster")] Movie movie)
{
if (id != movie.ID)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(movie);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(movie.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(movie);
}
View:
<form asp-action="Create" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Title" class="control-label"></label>
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ReleaseDate" class="control-label"></label>
<input asp-for="ReleaseDate" class="form-control" />
<span asp-validation-for="ReleaseDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Genre" class="control-label"></label>
<input asp-for="Genre" class="form-control" />
<span asp-validation-for="Genre" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Price" class="control-label"></label>
<input asp-for="Price" class="form-control" />
<span asp-validation-for="Price" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Rating" class="control-label"></label>
<input asp-for="Rating" class="form-control" />
<span asp-validation-for="Rating" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Poster" class="control-label"></label>
<input type="file" asp-for="Poster" class="form-control" />
<span asp-validation-for="Poster" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
I have not seen your code but for me what i do on button click convert image to base64 using JavaScript like
var dataURL;
$("#uploadFileID").change(function() {
var fileInput = $("#uploadFileID");
var file = document.querySelector('#uploadFileID').files[0];
var reader = new FileReader();
reader.onloadend = function (f) {
var canvas = document.createElement('CANVAS');
var ctx = canvas.getContext('2d');
ctx.drawImage(this, 0, 0);
dataURL = canvas.toDataURL();
});
}
});
then send it to method using ajax post and save it to Database
This is the code to upload file https://github.com/Sagardip/studentapp
public string Upload(IFormFile file)
{
var uploadDirecotroy = "uploads/";
var uploadPath = Path.Combine(env.WebRootPath, uploadDirecotroy);
if (!Directory.Exists(uploadPath))
Directory.CreateDirectory(uploadPath);
var fileName = Guid.NewGuid() + Path.GetExtension(file.FileName);
var filePath = Path.Combine(uploadPath, fileName);
using (var strem = File.Create(filePath))
{
file.CopyTo(strem);
}
return fileName;
}
Before asp.net core I would invoke a web api from a button by writing some jquery from behind the button click.
I would then handle the data returned from that api call.
Currently I have a view. this view contains a pop up window. In that window I have 3 div sections.
Login
Registration
ForgottenPassword
Focusing on Registration div I allow user to enter email/password/confirm password and let them press a button that calls my web api to validate this process.
So, this is my parent view snippet:
#model InformedWorker.Services.Models.Account
<div id="divLogForm" class="modal-dialog" style="display:none;position:absolute; ">
<div style="background-color: #fefefe;margin: auto;padding: 20px;border: 1px solid #888;">
<div class="modal-header">
<button id="btnClose" type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button>
<h4 class="modal-title" id="hdrTitle">LogIn</h4>
</div>
<div class="modal-body">
#{Html.RenderPartial("~/Views/Segments/_Registration.cshtml", Model.Registration);}
#{Html.RenderPartial("~/Views/Segments/_LogIn.cshtml", Model.LogIn);}
#{Html.RenderPartial("~/Views/Segments/_ForgottenRegistration.cshtml", Model.ForgottenloginDetails);}
</div>
</div>
</div>
My registration partial view is this:
#model InformedWorker.Services.Models.Registration
#*
*#
<div class="row" style="display:none" id="divRegistration">
<div class="col-xs-6">
<div class="well">
<div class="form-group">
<label asp-for="EmailAddress" class="control-label"></label>
<input type="text" class="form-control" id="EmailAddress" name="EmailAddress" value="#Model.EmailAddress" required="" title="Please enter you username" placeholder="example#gmail.com">
<span class="help-block"></span>
</div>
<div class="form-group">
<label asp-for="Password" class="control-label">Password</label>
<input type="password" class="form-control" id="Password" name="Password" value="" required="" title="Please enter your password">
<span class="help-block"></span>
</div>
<div class="form-group">
<label asp-for="ConfirmPassword" class="control-label"></label>
<input type="password" class="form-control" id="ConfirmPassword" name="ConfirmPassword" value="" required="" title="Please confirm your password">
</div>
<div id="registerErrorMsg" class="alert alert-error hide">Invalid registration</div>
<a asp-area="" asp-controller="api/Registration" asp-action="Register" class="btn btn-success btn-block">Register</a>
</div>
</div>
<div class="col-xs-6">
<p class="lead">Already Registered?</p>
<p><a onclick="showLogInPage();" class="btn btn-info btn-block">LogIn</a></p>
</div>
</div>
My account Model is this:
public class Account
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long AccountId { get; set; }
public string AccountRef { get; set; }
[Display(Name = "Company Name")]
public string CompanyName { get; set; }
[Display(Name = "Email Address")]
[Required]
[RegularExpression(#"\w+([-+.']\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*", ErrorMessage = "Email address not found")]
[StringLength(60, MinimumLength = 3)]
public string EmailAddress { get; set; }
public string Salt { get; set; }
public string Hash { get; set; }
public bool Disabled { get; set; }
[Display(Name = "Date of Entry")]
public DateTime DOE { get; set; }
public ICollection<Client> Clients { get; set; }
public bool Activated { get; set; }
[NotMapped]
public Registration Registration { get; set; }
[NotMapped]
public LogIn LogIn { get; set; }
[NotMapped]
public ForgottenRegistration ForgottenRegistration { get; set; }
}
My homeController is this:
public IActionResult Index()
{
//var home = new Services.Models.Home();
//home.MyTest = _localizer["ActivationEmailSent"];
//return View(home);
var test = new Services.Models.Account();
test.Registration = new Services.Models.Registration();
test.Registration.EmailAddress = "aaa";
return View(test);
//return View(new Services.Models.Account());
}
My Registration Web api is this:
[Route("api/[controller]")]
public class Registration : Controller
{
// GET api/values/5
[HttpGet("{id}")]
public string Get(Registration Account)//int id)
{
return "Error in reg";
}
}
so my question is that when api is called the whole page page is replaced with 'value'.
how would I parse the data and populate the form fields with the result and preserve the original view?
Should i revert back to jquery and make api call that way?
Have I got this fundamentally wrong?
Thanks