I'm creating a simple MVC project for my ASP.NET classes at the Uni.
It consists of one model class (BikeAds), Controller (BikeAdsController) and Views (Create, Delete, Details, Edit, Index) and uses mdf file as a database.
Controller and views were generated automatically (I chose "MVC controller with views, using Entity Framework").
I encountered the problem when trying to create a new entry. When I fill the "Create" form and click "Submit" button, it clears all data from the input fields and the form is not submitted - validation doesn't allow for empty fields. When I removed [Required] validation, I got a SQL exception (null is not allowed in the database).
I do not understand where the cause of the issue my lie.
Controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using bikes_ads.Data;
using bikes_ads.Models;
namespace bikes_ads.Controllers
{
public class BikeAdsController : Controller
{
private readonly BikesAdvertsDbContext _context;
public BikeAdsController(BikesAdvertsDbContext context)
{
_context = context;
}
// GET: BikeAds
public async Task<IActionResult> Index()
{
return View(await _context.Adverts.ToListAsync());
}
// GET: BikeAds/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var bikeAd = await _context.Adverts
.FirstOrDefaultAsync(m => m.Id == id);
if (bikeAd == null)
{
return NotFound();
}
return View(bikeAd);
}
// GET: BikeAds/Create
public IActionResult Create()
{
return View();
}
**// POST: BikeAds/Create
// To protect from overposting attacks, 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")] BikeAd bikeAd)
{
if (ModelState.IsValid)
{
_context.Add(bikeAd);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(bikeAd);
}**
// GET: BikeAds/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var bikeAd = await _context.Adverts.FindAsync(id);
if (bikeAd == null)
{
return NotFound();
}
return View(bikeAd);
}
// POST: BikeAds/Edit/5
// To protect from overposting attacks, 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> Edit(int id, [Bind("Id")] BikeAd bikeAd)
{
if (id != bikeAd.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(bikeAd);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!BikeAdExists(bikeAd.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(bikeAd);
}
// GET: BikeAds/Delete/5
public async Task<IActionResult> Delete(int? id)
{
if (id == null)
{
return NotFound();
}
var bikeAd = await _context.Adverts
.FirstOrDefaultAsync(m => m.Id == id);
if (bikeAd == null)
{
return NotFound();
}
return View(bikeAd);
}
// POST: BikeAds/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
var bikeAd = await _context.Adverts.FindAsync(id);
_context.Adverts.Remove(bikeAd);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
private bool BikeAdExists(int id)
{
return _context.Adverts.Any(e => e.Id == id);
}
}
}
Create form:
#model bikes_ads.Models.BikeAd
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>BikeAd</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="Category" class="control-label"></label>
<input asp-for="Category" class="form-control" />
<span asp-validation-for="Category" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ShortDescription" class="control-label"></label>
<input asp-for="ShortDescription" class="form-control" />
<span asp-validation-for="ShortDescription" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="LongDescription" class="control-label"></label>
<input asp-for="LongDescription" class="form-control" />
<span asp-validation-for="LongDescription" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="SellerName" class="control-label"></label>
<input asp-for="SellerName" class="form-control" />
<span asp-validation-for="SellerName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="SellerPhoneNumber" class="control-label"></label>
<input asp-for="SellerPhoneNumber" class="form-control" />
<span asp-validation-for="SellerPhoneNumber" 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">
<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");}
}
Model class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
namespace bikes_ads.Models
{
public class BikeAd
{
[Key]
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string Title { get; set; }
[Required]
public string Category { get; set; }
[Required]
[MaxLength(100)]
public string ShortDescription { get; set; }
[Required]
[MaxLength(500)]
public string LongDescription { get; set; }
[Required]
public string SellerName { get; set; }
[Required]
public string SellerPhoneNumber { get; set; }
[Required]
public double Price { get; set; }
public BikeAd(int id, string title, string category, string shortDescription, string longDescription, string sellerName, string sellerPhoneNumber, double price)
{
Id = id;
Title = title;
Category = category;
ShortDescription = shortDescription;
LongDescription = longDescription;
SellerName = sellerName;
SellerPhoneNumber = sellerPhoneNumber;
Price = price;
}
public BikeAd()
{
}
}
}
In your HTTPPost Create method, you're only binding the Id property;
public async Task<IActionResult> Create([Bind("Id")] BikeAd bikeAd)
{
}
Looking at your create form, you have other properties besides Id.
1) Shouldn't you be binding the all the other properties?
and
2) Shouldn't Id be automatically generated?
Change your Create method to this;
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Title,Category,Description,ShortDescription,LongDescription,SellerName,SellerPhoneNumber,Price")] BikeAd bikeAd)
{
if (ModelState.IsValid)
{
_context.Add(bikeAd);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(bikeAd);
}
Related
This razor Page Create new Continent and if an ID is passed in OnGetAsync method
This EditContinent.cshtml.cs will just edit the given id continent.
Below is the Model, in its OnPost method is showing error only when i try to Add new continent.
It is updating just fine which is in the else statement.
This error is showing
{The id field is required.}
I am using sqlite database
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using World.Data;
using World.Models;
using Microsoft.EntityFrameworkCore;
namespace World.Pages
{
public class EditContinentModel : PageModel
{
private readonly ContinentContext _context;
public EditContinentModel(ContinentContext context)
{
_context = context;
}
[BindProperty]
public Continent Continent { get; set; }
public async Task<IActionResult> OnGetAsync(string id)
{
if (id == null)
{
Continent = new Continent();
}
else
{
Continent = await _context.Continents.FindAsync(id);
if (Continent == null)
{
return NotFound();
}
}
return Page();
}
public async Task<IActionResult> OnPostAsync(string id)
{
if (!ModelState.IsValid)
{
System.Diagnostics.Debug.WriteLine("ModelState is not Valid");
System.Diagnostics.Debug.WriteLine(ModelState.Values.SelectMany(v=>v.Errors));
return Page();
}
if (id == null)
{
await _context.Continents.AddAsync(Continent);
}
else
{
_context.Attach(Continent).State = EntityState.Modified;
}
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
}
This the razor page for the model above
#page "{id?}"
#model World.Pages.EditContinentModel
#{
ViewData["Title"] = "Edit Continent";
}
<div class="parentDiv">
<div class="header1">
<h2>#ViewData["Title"]</h2>
</div>
<form method="post" class="formEdit">
<div class="flex_row">
<label asp-for="Continent.ID">ID</label>
<input asp-for="Continent.ID" type="text" required/>
</div>
<div class="flex_row">
<label asp-for="Continent.Name">Name</label>
<input asp-for="Continent.Name" type="text" required/>
</div>
<div class="savebutton">
<input type="submit" value="Save" />
</div>
</form>
</div>
This below is my model for the DbSet
namespace World.Models
{
public class Continent
{
public string ID { get; set; }
public string Name { get; set; }
}
}
changed public async Task<IActionResult> OnGetAsync(string id) to
public async Task<IActionResult> OnGetAsync(string? id)
Same goes in post and it worked.
I am trying to make a simple bank application in ASP.NET Core and I have two problems:
How to make one to many relationship between User and Transactions (every user has many transactions). I tried that what is below but it doesn't work and now I'm stuck.
How to set the current user in form (user can't change that) when transaction will be created.
TransactionsController
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using Aplikacja_Bankowa.Data;
using Aplikacja_Bankowa.Models;
namespace Aplikacja_Bankowa.Controllers
{
public class TransactionsController : Controller
{
private readonly ApplicationDbContext _context;
public TransactionsController(ApplicationDbContext context)
{
_context = context;
}
// GET: Transactions
public async Task<IActionResult> Index()
{
return View(await _context.Transactions.ToListAsync());
}
// GET: Transactions/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var transactions = await _context.Transactions
.FirstOrDefaultAsync(m => m.Id == id);
if (transactions == null)
{
return NotFound();
}
return View(transactions);
}
// GET: Transactions/Create
public IActionResult Create()
{
return View();
}
// POST: Transactions/Create
// To protect from overposting attacks, 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,Date,Sender,Beneficient,Amount")] Transactions transactions)
{
if (ModelState.IsValid)
{
_context.Add(transactions);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(transactions);
}
// GET: Transactions/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var transactions = await _context.Transactions.FindAsync(id);
if (transactions == null)
{
return NotFound();
}
return View(transactions);
}
// POST: Transactions/Edit/5
// To protect from overposting attacks, 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> Edit(int id, [Bind("Id,Date,Sender,Beneficient,Amount")] Transactions transactions)
{
if (id != transactions.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(transactions);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!TransactionsExists(transactions.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(transactions);
}
// GET: Transactions/Delete/5
public async Task<IActionResult> Delete(int? id)
{
if (id == null)
{
return NotFound();
}
var transactions = await _context.Transactions
.FirstOrDefaultAsync(m => m.Id == id);
if (transactions == null)
{
return NotFound();
}
return View(transactions);
}
// POST: Transactions/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
var transactions = await _context.Transactions.FindAsync(id);
_context.Transactions.Remove(transactions);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
private bool TransactionsExists(int id)
{
return _context.Transactions.Any(e => e.Id == id);
}
}
}
Models/Transactions
using Microsoft.AspNetCore.Identity;
using System.ComponentModel.DataAnnotations;
namespace Aplikacja_Bankowa.Models
{
public class ApplicationUser : IdentityUser
{
public ICollection<Transactions> UserTransactions { get; set; }
}
//public class UserTransaction
//{
// public string UserId { get; set; }
// public ApplicationUser ApplicationUser { get; set; }
// public int TransactionsId { get; set; }
// public Transactions Transactions { get; set; }
//}
public class Transactions
{
[Key]
public int Id { get; set; }
[Display(Name = "Data transakcji")]
[DataType(DataType.Date)]
public DateTime Date { get; set; } = DateTime.Now;
public string? Sender { get; set; }
[Display(Name = "Odbiorca")]
public string? Beneficient { get; set; }
[Display(Name = "Kwota")]
public decimal Amount { get; set; }
public Transactions Transaction { get; set; }
}
}
Views/Transactions/Create.cs
#model Aplikacja_Bankowa.Models.Transactions
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Transactions</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="Sender" class="control-label"></label>
<input asp-for="Sender" class="form-control" />
<span asp-validation-for="Sender" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Beneficient" class="control-label"></label>
<input asp-for="Beneficient" class="form-control" />
<span asp-validation-for="Beneficient" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Amount" class="control-label"></label>
<input asp-for="Amount" class="form-control" />
<span asp-validation-for="Amount" class="text-danger"></span>
</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 new with MVC Core and trying to add an image to my SQL Server-database through Entity Framework. I have accomplished the database itself and the view. What I cannot really get working is the controller. Can someone help me get the controller in place. Please!
Here is the model:
public class Product
{
[Key]
public int ProductID { get; set; }
[Required(ErrorMessage = "Please enter an product name")]
public string Name { get; set; }
[Required(ErrorMessage = "Please specify a category")]
public string Category { get; set; }
public string SubCategory { get; set; }
[Required(ErrorMessage = "Please enter a description")]
public string Description { get; set; }
[Required(ErrorMessage = "Please enter a positive price")]
public decimal Price { get; set; }
public byte[] Image { get; set; }
public string ImageSourceFileName { get; set; }
public string ImageContentType { get; set; }
}
Here is the database:
Product ID int False
Category nvarchar(MAX) False
Description nvarchar(MAX) False
Name nvarchar(MAX) False
Price decimal(18,2) False
SubCategory nvarchar(MAX) True
Image varbinary(MAX) True
ImageContentType nvarchar(MAX) True
ImageSourceFileName nvarchar(MAX) True
Here is the view:
<div class="col-md-4">
<form asp-action="Create" method="post" enctype="multipart/
form-data" asp-controller="Products">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Image" class="control-label">File
input</label>
<input asp-for="Image" type="file"
aria-describedby="fileHelp" class="form-control-file" />
<span asp-validation-for="Image" class="text-danger"></span>
<small id="fileHelp" class="form-text text-muted">This is
some placeholder block-level help text for the above input. It's a bit
lighter and easily wraps to a new line.</small>
</div>
<div class="form-group">
<label asp-for="ImageSourceFileName"
class= "control-label"></label>
<input asp-for="ImageSourceFileName" class="form-control" />
<span asp-validation-for="ImageSourceFileName"
class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ImageContentType" class="control-label"></label>
<input asp-for="ImageContentType" class="form-control" />
<span asp-validation-for="ImageContentType"
class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
</div>
Here is the controller:
public class ProductsController : Controller
{
private readonly ApplicationDbContext _context;
public ProductsController(ApplicationDbContext context)
{
_context = context;
}
// GET: Products
public async Task<IActionResult> Index()
{
return View(await _context.Products.ToListAsync());
}
// GET: Products/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var product = await _context.Products
.SingleOrDefaultAsync(m => m.ProductID == id);
if (product == null)
{
return NotFound();
}
return View(product);
}
// GET: Products/Create
public IActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create
([Bind("ProductID,Name,Category,SubCategory,
Description,Price,Image,ImageSourceFileName,ImageContentType")]
Product product)
{
if (ModelState.IsValid)
{
_context.Add(product);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(product);
}
// GET: Products/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var product = await _context.Products.SingleOrDefaultAsync(m =>
m.ProductID == id);
if (product == null)
{
return NotFound();
}
return View(product);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult>
Edit(int id,
[Bind
("ProductID,Name,Category,SubCategory,Description,Price,
Image,ImageSourceFileName,ImageContentType")]
Product product)
{
if (id != product.ProductID)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(product);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ProductExists(product.ProductID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(product);
}
// GET: Products/Delete/5
public async Task<IActionResult> Delete(int? id)
{
if (id == null)
{
return NotFound();
}
var product = await _context.Products
.SingleOrDefaultAsync(m => m.ProductID == id);
if (product == null)
{
return NotFound();
}
return View(product);
}
// POST: Products/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
var product = await _context.Products.SingleOrDefaultAsync(m =>
m.ProductID == id);
_context.Products.Remove(product);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
private bool ProductExists(int id)
{
return _context.Products.Any(e => e.ProductID == id);
}
}
I like to store the image in the database. Now all text info goes into the database nice and neat, but no image/bytes...
The space is empty in the index-view where the image is supposed to be.
In asp.net core, to send a file from your browser to your action method, you should use the IFormFile type.
If you do not prefer to create a view model (I strongly advise you to create a view model and use that), you can add a new parameter to your httppost action method of IFormFile type and convert that to a byte array and store that in the Image property on your Product entity.
Also there is no need to have input elements for ImageContentType and ImageSourceFileName properties/columns. You can read this meta information from the uploaded file.
[HttpPost]
public IActionResult Create(Product model, IFormFile img)
{
if (img != null)
{
model.Image = GetByteArrayFromImage(img);
model.ImageSourceFileName = System.IO.Path.GetFileName(img.FileName);
model.ImageContentType = img.ContentType;
}
_context.Products.Add(model);
_context.SaveChanges();
return RedirectToAction("Index");
}
private byte[] GetByteArrayFromImage(IFormFile file)
{
using (var target = new MemoryStream())
{
file.CopyTo(target);
return target.ToArray();
}
}
Now make sure you are using a file input element with same name as the new method parameter we added (img) in your form.
<form asp-action="Create" method="post"
enctype="multipart/form-data" asp-controller="Product">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input asp-for="Name" />
<input asp-for="Category" />
<input asp-for="Price" />
<input type="file" name="img" />
<input type="submit" value="Create" class="btn" />
</form>
As i mentioned earlier, It is a good idea to use a view model for your create view and use that. This example uses a view model to transfer the uploaded file from the browser to the action method.
How to upload files in asp.net core?
At the view, you should assign the image input tag with name like following:
name="#Model.Image"
And at the Controller, add a parameter for image upload and use MemoryStream class to convert it into bytes:
public virtual ActionResult yourController(Product prod, HttpPostedFileBase imgUpload)
{
Product prod = new Product();
var imgT = new MemoryStream();
if(imgUpload!=null){
imgUpload.InputStream.CopyTo(imgT);
prod.Image = imgT.ToArray();
}
}
Hope it helps!
This way let you to save the file in a folder and to save the path the DB
following code for the Entity Dto
public string ImagePath { get; set; }
public IFormFile ImageFile { get; set; }
following code for the controller
var file = EntityDto.ImageFile;
if (file != null && file.Length > 0)
EntityDto.ImagePath = $"\\images\\folderName\\{EntityDto.Code}{Path.GetExtension(file.FileName)}";
if (AddAsync(EntityDto, $"{nameof(EntityDto)}."))
{
if (file != null && file.Length > 0)
{
var uploads = Path.Combine(_environment.WebRootPath, #"images\employees");
var filePath = Path.Combine(uploads, $"{EntityDto.Code}{Path.GetExtension(file.FileName)}");
using var fileStream = new FileStream(filePath, FileMode.Create);
await file.CopyToAsync(fileStream);
}
following code for the UI
<form asp-action="New" method="post" enctype="multipart/form-data">
<input asp-for="#Model.EntityDto.ImageFile" type="file" />
</form>
I'm trying to edit an element through the edit view. While debugging, I've noticed that the object I'm trying to update the database with has null or zeroed fields. The edit view seems to not return the data I'm entering.
Model:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Data.Entity;
using Microsoft.Data.Entity.Infrastructure;
namespace METALIMPEX.Models
{
public class Condition
{
[Key]
[Display(Name = "Condition ID")]
[Column("condition_id")]
[Required]
public int conditionID { get; set; }
[Display(Name = "Date and time")]
[Column("condition_date")]
[Timestamp]
[Required]
public DateTime dateTime { get; set; }
[Display(Name = "Component ID")]
[Column("component")]
[Required]
public int componentID { get; set; }
[Display(Name = "Operator ID")]
[Column("operator")]
[Required]
public int operatorID { get; set; }
[Display(Name = "Component status")]
[Column("condition")]
[Required]
public bool condition { get; set; }
[Display(Name = "Comments")]
[Column("remarks")]
[Required]
public string comments { get; set; }
}
public class ConditionDBContext : DbContext
{
public DbSet<Condition> Conditions { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Data Source=DELL-XPS\\SQLEXPRESS;Initial Catalog=METALIMPEX;Integrated Security=True");
}
}
}
Controller:
using System.Linq;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.Data.Entity;
using METALIMPEX.Models;
using System.Data.SqlClient;
using System.Collections.Generic;
using System.Web;
using System;
namespace METALIMPEX.Controllers
{
public class ConditionController : Controller
{
private ApplicationDbContext _context;
private ConditionDBContext db = new ConditionDBContext();
IList<Condition> conditionList = new List<Condition>();
public ConditionController(ApplicationDbContext context)
{
_context = context;
//DEPRECATED CODE, MANUAL QUERY, PROJECT NOW USES DBCONTEXT
/*string strConnection = "Data Source=DELL-XPS\\SQLEXPRESS;Initial Catalog=METALIMPEX;Integrated Security=True";
string sqlQuery = "SELECT REPLICATE('0',6-LEN(RTRIM(PartID))) + RTRIM(PartID) AS 'Part ID', REPLICATE('0', 6 - LEN(RTRIM(OperatorID))) + RTRIM(OperatorID) AS 'Operator ID', Date_Time AS 'Time', IsPartWorking AS 'Part Status', IsMachineWorking AS 'Machine Status' FROM Defect";
SqlConnection sqlConnection = new SqlConnection(strConnection);
SqlCommand command = new SqlCommand(sqlQuery, sqlConnection);
SqlDataReader Dr;
try {
sqlConnection.Open();
Dr = command.ExecuteReader();
while (Dr.Read())
{
conditionList.Add(new Defect()
{
conditionID = Dr["Part ID"].ToString(),
operatorID = Dr["Operator ID"].ToString(),
dateTime = Dr["Time"].ToString(),
partStatus = (bool)Dr["Part Status"],
machineStatus = (bool)Dr["Machine Status"]
});
};
Dr.Dispose();
}
catch (SqlException) {
throw new InvalidOperationException("An error has occured while connecting to the database");
//OR
//return View("Error");
}*/
}
// GET: Condition
public IActionResult Index()
{
//return View(conditionList);
return View(db.Conditions.ToList());
}
// GET: Condition/Details/5
public IActionResult Details()
{
return View(conditionList);
}
// GET: Condition/Create
public IActionResult Create()
{
return View();
}
// POST: Condition/Create
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(Condition condition)
{
if (ModelState.IsValid)
{
//_context.Condition.Add(condition);
//_context.SaveChanges();
return RedirectToAction("Index");
}
return View(condition);
}
// GET: Condition/Edit/5
public IActionResult Edit(int id)
{
if (conditionList == null)
{
return HttpNotFound();
}
Condition condition = db.Conditions.Single(m => m.conditionID == id);
return View(condition);
}
// POST: condition/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(Condition condition)
{
if (ModelState.IsValid)
{
db.Update(condition);
db.Entry(condition).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(condition);
}
// GET: condition/Delete/5
[ActionName("Delete")]
public IActionResult Delete(string id)
{
return View(conditionList);
}
// POST: condition/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public IActionResult DeleteConfirmed(string id)
{
/*Movie movie = db.Movies.Find(id);
if (movie == null)
{
return HttpNotFound();
}
db.Movies.Remove(movie);
db.SaveChanges();
return RedirectToAction("Index");*/
return RedirectToAction("Index");
}
}
}
View:
#model METALIMPEX.Models.Condition
#{
ViewData["Title"] = "Edit";
}
<h2>Edit</h2>
<form asp-action="Edit">
<div class="form-horizontal">
<h4>Condition</h4>
<hr />
<div asp-validation-summary="ValidationSummary.ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="conditionID" />
<div class="form-group">
<label asp-for="componentID" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="componentID" class="form-control" />
<span asp-validation-for="componentID" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="operatorID" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="operatorID" class="form-control" />
<span asp-validation-for="operatorID" class="text-danger" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<div class="checkbox">
<input asp-for="condition" />
<label asp-for="condition"></label>
</div>
</div>
</div>
<div class="form-group">
<label asp-for="dateTime" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="dateTime" class="form-control" />
<span asp-validation-for="dateTime" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="comments" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="comments" class="form-control" />
<span asp-validation-for="comments" class="text-danger" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
</form>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
}
I'm a beginner in .NET so I have no real idea what's wrong here. The edit view was generated by the framework so I assume it should be working.
Any ideas?
The issue is with the parameter name of the Edit Action method. Below is the URL encoded form-data posted from the view on clicking Save button.
conditionID=&dateTime=2013-12-01&componentID=1&operatorID=1&condition=true&comments=
The action parameter name is condition and there is a key-value pair condition=true in the incoming form-data. The MVC model binding process will try to map the parameter/property(in case of complex model) name to the corresponding Route/Query String/Request Body data.
Hence it's trying to map the value of condition in the form-data i.e. true to the action parameter and eventually fails to convert bool to object.
Change the parameter name to any name that doesn't corresponds to any key name in the form-data and it should work.
For whatever reason I'm unable to Create and Edit using the ViewModel called CreateEmployeeViewModel that I created. I can however Create and Edit fine without using the CreateEmployeeViewModel but was told it was bad practive to use the main Models for CRUD. I am however able to retrieve values to my 2 DropDownList tags fine using the CreateEmployeeViewModel, just not Create or Edit. Below are my current Models, ViewModels, Controllers and Views.
I just figure out why I cannot Create using the public IActionResult Create(Employee employee) Active Method.
Employee Model: (located in Models folder)
public class Employee
{
[Key]
public int EmpId { get; set; }
[Required]
public string EmpFirstName { get; set; }
[Required]
public string EmpLastName { get; set; }
public int DeptId { get; set; }
public Department Department { get; set; }
public int BldgId { get; set; }
public Building Building { get; set; }
}
EmployeeController: (located in Controllers folder)
public class EmployeeController : Controller
{
private DataEntryContext _context;
public EmployeeController(DataEntryContext context)
{
_context = context;
}
public IActionResult Index()
{
return View(_context.Employees.ToList());
}
// Populate Department values to DropDownList
private IEnumerable<SelectListItem> GetDeptList()
{
var dept = _context.Departments
.Select(s => new SelectListItem
{
Value = s.DeptId.ToString(),
Text = s.DeptTitle
})
.ToList();
return (dept);
}
// Populate Building values to DropDownList
private IEnumerable<SelectListItem> GetBldgList()
{
var bldg = _context.Buildings
.Select(b => new SelectListItem
{
Value = b.BldgId.ToString(),
Text = b.BldgName
})
.ToList();
return (bldg);
}
public IActionResult Create()
{
CreateEmployeeViewModel model = new CreateEmployeeViewModel();
model.DeptList = GetDeptList();
model.BldgList = GetBldgList();
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(Employee employee)
{
if (ModelState.IsValid)
{
_context.Employees.Add(employee);
_context.SaveChanges();
return RedirectToAction("Index");
}
return View(employee);
}
public IActionResult Edit(int? id)
{
if (id == null)
{
return View("Error");
//return NotFound();
}
var employee = _context.Employees
.Where(e => e.EmpId == id)
.Single();
if (employee == null)
{
return View("Error");
//return NotFound();
}
return View(employee);
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(Employee employee)
{
if (ModelState.IsValid)
{
_context.Employees.Update(employee);
_context.SaveChanges();
return RedirectToAction("Index");
}
return View(employee);
}
}
CreateEmployeeViewModel: (located in ViewModels Folder)
public class CreateEmployeeViewModel
{
public int EmpId { get; set; }
public string EmpFirstName { get; set; }
public string EmpLastName { get; set; }
public int DeptId { get; set; }
public IEnumerable<SelectListItem> DeptList { get; set; }
public int BldgId { get; set; }
public IEnumerable<SelectListItem> BldgList { get; set; }
}
Employee Create View:
<form asp-controller="employee" asp-action="Create" method="post" class="form-horizontal" role="form">
<div class="form-horizontal">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="EmpFirstName" class="col-md-2 control-label">First Name</label>
<div class="col-md-10">
<input asp-for="EmpFirstName" class="form-control" />
<span asp-validation-for="EmpFirstName" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="EmpLastName" class="col-md-2 control-label">Last Name</label>
<div class="col-md-10">
<input asp-for="EmpLastName" class="form-control" />
<span asp-validation-for="EmpLastName" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="DeptId" class="col-md-2 control-label">Department</label>
<div class="col-md-10">
<select asp-for="DeptId" asp-items="#Model.DeptList" class="form-control">
<option>Select Department</option>
</select>
<span asp-validation-for="DeptId" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="BldgId" class="col-md-2 control-label">Building Location</label>
<div class="col-md-10">
<select asp-for="BldgId" asp-items="#Model.BldgList" class="form-control">
<option>Select Building</option>
</select>
<span asp-validation-for="BldgId" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
In your Create method, you are sending to the view the CreateEmployeeViewModel but in your HttpPost Create method you are accepting back the Employee model instead of the CreateEmployeeViewModel. So once you change the post methods signature to accept the correct CreateEmployeeViewModel, you can simply map it back to the Employee model.
Get Action Method:
public IActionResult Create(Employee employee)
{
return View(employee);
}
Just change in your Post Action Method:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(CreateEmployeeViewModel vm)
{
if (ModelState.IsValid)
{
var model = new Employee{
//your logic here for example
employeename = vm.employeename,
employeepassword = vm.employeepassword
}
_context.Employees.Add(model);
_context.SaveChanges();
return RedirectToAction("Index");
}
return View(employee);
}
and donĀ“t forget to cal View Model in your .cshtml