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.
Related
I'm currently developing with ASP.NET MVC and after I have created the an error of converting a local class to an Identity one cannot implicitly convert "ListUsers" to "Identity.IdentityUser"(I simplified the error a bit) anyway, here are some bits of code:
ListUsersController.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ServiceReferenceAuthentication;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using SoftSMS.Data.Data;
using SoftSMS.Data.Entities;
using SoftSMS.Data.Entity;
using SoftSMS.Data.Interfaces;
using SoftSMS.MVC.ViewModels;
namespace SoftSMS.MVC.Controllers
{
public class ListUsersController : Controller
{
private readonly DataContext _context;
private readonly RoleManager<IdentityRole> roleManager;
private readonly UserManager<ListUsers> userManager;
private readonly IConfiguration configuration;
private readonly IUnitOfWork<ListUsers> _users;
//private readonly DataContext _context;
public ListUsersController(IUnitOfWork<ListUsers> Users/*DataContext context*/)
{
_users = Users;
//_context = context;
}
// GET: ListUsers
public IActionResult Index()
{
return View(_users.Entity.GetAll());
}
// GET: ListUsers/Details/5
public async Task<IActionResult> DetailsAsync(Guid? id)
{
if (id == null)
{
return NotFound();
}
...
if (listUsers == null)
{
return NotFound();
}
return View(listUsers);
}
// GET: ListUsers/Create
public IActionResult Create()
{
return View();
}
// POST: ListUsers/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,UserName,FirstName,LastName,Status,Profil")] ListUsers listUsers)
{
try
{
TPFAuthenticationSoapClient.EndpointConfiguration endpointConfiguration = new TPFAuthenticationSoapClient.EndpointConfiguration();
TPFAuthenticationSoapClient servAuth = new TPFAuthenticationSoapClient(endpointConfiguration);
if ((Boolean)TempData["isValid"] == false)
{
IdentityUser u = await userManager.FindByNameAsync(listUsers.UserName);
if (u != null)
{
ViewBag.IsValid = false;
ModelState.AddModelError(string.Empty, "Ce Compte existe déja");
}
else
{
...;
if (x != null)
{
...
return View(listUsers);
}
else
{
...
}
}
}
else
{
...
return RedirectToAction(nameof(Index));
}
return View(listUsers);
}
catch (Exception ex)
{
...
return RedirectToAction(nameof(Create));
}
}
the error is in IdentityUser u = await userManager.FindByNameAsync(listUsers.UserName);
I don't want to create an operator override as it's suggested (nor explicit conversion),
ListUsers.cs:
using Microsoft.AspNet.Identity.EntityFramework;
using SoftSMS.Data.Entities;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
namespace SoftSMS.Data.Entity
{ public enum StatusType
{ Banned,Registered,Normal,Warned}
public enum ProfilType
{ Admin,Manager,Agent}
public class ListUsers : IdentityUser
{
[DataType(DataType.Text)]
public string FirstName { get; set; }
[DataType(DataType.Text)]
public string LastName { get; set; }
[DataType(DataType.Text)]
[Required]
public StatusType Status { get; set; }
public ProfilType Profil { get; set; }
public virtual ICollection<MembershipAssociations> MembershipAssociation { get; set; }
public virtual ICollection<Template> TemplateNavigation { get; set; }
public virtual ICollection<SentMsg> SentmsgNavigation { get; set; }
}
}
UsersViewModel.cs:
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace SoftSMS.MVC.ViewModels
{ public enum StatusType
{ Banned,Registered,Normal,Warned}
public enum ProfilType
{ Admin,Manager,Agent}
public class UsersViewModel : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public StatusType Status { get; set; }
public ProfilType Profil { get; set; }
public List<MembershipAssociationViewModel> MembershipAssociation { get; set; }
public List<SentMsgViewModel> Sentmsg { get; set; }
}
}
and Create.cshtml:
#model SoftSMS.MVC.ViewModels.UsersViewModel
#{ ViewData["Title"] = "Create"; }
#{TempData["isValid"] = ViewBag.isValid;}
<br />
<h3>Nouvel Utilisateur</h3>
<hr />
#*#ViewBag.Result1*#
<br />
<h4 style="color: red;">#TempData["errorMessage"]</h4>
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
#if ((Boolean)ViewBag.isValid == false)
{
<div class="form-group">
<label asp-for="UserName" class="control-label">Login</label>
<input asp-for="UserName" class="form-control" />
<span asp-validation-for="UserName" class="text-danger"></span>
</div> }
#if ((Boolean)ViewBag.isValid == true)
{
<input type="hidden" asp-for="Id" />
<div class="form-group">
<label asp-for="UserName" class="control-label">Login</label>
<input asp-for="UserName" class="form-control" disabled="disabled" />
#Html.HiddenFor(model => model.UserName)
</div>
<div class="form-group">
<label asp-for="FirstName" class="control-label">Prenom</label>
<input asp-for="FirstName" class="form-control" disabled="disabled" />
#Html.HiddenFor(model => model.FirstName)
</div>
<div class="form-group">
<label asp-for="LastName" class="control-label">Nom</label>
<input asp-for="LastName" class="form-control" disabled="disabled" />
#Html.HiddenFor(model => model.LastName)
</div>
<div class="form-group">
<label asp-for="Email" class="control-label">Email</label>
<input asp-for="Email" class="form-control" disabled="disabled" />
#Html.HiddenFor(model => model.Email)
</div>
<div class="form-group">
<label asp-for="EstActif" class="control-label">Actif</label>
<select id="Select1" asp-for="EstActif" class="form-control">
<option value="True"> <text>OUI</text></option>
<option value="False"> <text>NON</text></option>
</select>
</div> }
<div class="form-group">
<input type="submit" value="Creer" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Liste des utilisateurs</a>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
as you could have seen, I had to make the UI in French (even though I don't like it) because I am developing this platform for French people,
if you need another resource, I could share it, but not the entire app, the ...is for a confidential code,
thank you
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);
}
I am having problems to return a model using a form.
The problem is when I submit the form, the values are null even though I've specified that returns a model
This is my controller
And this is my View that returns null.
#model MyEnglishDictionary.Models.Dictionary
#{
ViewData["Title"] = "Create";
}
<h2>Create</h2>
<form method="post" asp-action="Create">
<div class="p-4 border rounded">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group row">
<div class="col-2">
<label asp-for="Word"></label>
</div>
<div class="col-5">
<input asp-for="Word" class="form-control" />
</div>
<span asp-validation-for="Word" class="text-danger"></span>
</div>
<div class="form-group row">
<div class="col-2">
<label asp-for="Meaning"></label>
</div>
<div class="col-5">
<input asp-for="Meaning" class="form-control" />
</div>
<span asp-validation-for="Meaning" class="text-danger"></span>
</div>
<div class="form-group row">
<div class="col-2">
<label asp-for="Pronunciation"></label>
</div>
<div class="col-5">
<input asp-for="Pronunciation" class="form-control" />
</div>
<span asp-validation-for="Pronunciation" class="text-danger"></span>
</div>
<br />
<div class="form-group">
<input type="submit" class="btn btn-primary" value="Create" />
<a asp-action="Index" class="btn btn-success">Back To List</a>
</div>
</div>
</form>
#section Scripts{
#{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}
EDIT
This is my Dictionary controller.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MyEnglishDictionary.Data;
using MyEnglishDictionary.Models;
namespace MyEnglishDictionary.Controllers
{
public class DictionaryController : Controller
{
private readonly ApplicationDbContext _db;
public DictionaryController(ApplicationDbContext db)
{
_db = db;
}
public IActionResult Index()
{
return View(_db.Dictionaries.ToList());
}
public IActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(Models.Dictionary word)
{
if(!ModelState.IsValid)
{
return View(word);
}
_db.Add(word);
await _db.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
}
}
And this is my Dictionary model
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MyEnglishDictionary.Models
{
public class Dictionary
{
[Key]
public int Id { get; set; }
[Required]
public string Word { get; set; }
[Required]
public string Meaning { get; set; }
[Required]
public string Pronunciation { get; set; }
public string Link { get; set; }
public DateTime Date { get; set; }
}
}
I am using Net Core 2.1, but I have some few projects that I use the same way to pass the form model from View to controller and they work.
You need to pay attention to the name of the parameter and fields.
For your issue, it is caused by that you defined a field which is Word and the parameter is word which caused the binding failed.
Try to change the public async Task<IActionResult> Create(Models.Dictionary word) to public async Task<IActionResult> Create(Models.Dictionary dictionary).
change the word parameter name to something else like _word, it seems like the compiler doesn't accept it as a parameter name in c#.
public async Task<IActionResult> Create(Models.Dictionary _word)
{
if(!ModelState.IsValid)
{
return View(_word);
}
_db.Add(_word);
await _db.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
btw, I didn't see it in the reserved keywords list
You have to bind the properties to your model. In your case:
public async Task<IActionResult> Create([Bind("Word, Meaning, Pronounciation")] Dictionary word)
Further reading:
Model Binding
This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 4 years ago.
I'm getting the following error each Time I try to enter a new Course.
Object reference not set to an instance of an object.
AspNetCore._Views_Admin_Manage_cshtml+<b__23_12>d.MoveNext()
in Manage.cshtml, line 34
Here is my Controller:
using ASP_Project.Data;
using ASP_Project.Models;
using ASP_Project.Services.Interfaces;
using ASP_Project.ViewModels;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
namespace ASP_Project.Controllers
{
public class AdminController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SchoolContext _schoolContext;
private readonly IAdminRepository _adminRepos;
private readonly ITeacherRepository _teacherRepository;
public AdminController(UserManager<ApplicationUser> userManager,
SchoolContext schoolContext,
IAdminRepository adminRepos,
ITeacherRepository teacherRepository
)
{
_userManager = userManager;
_schoolContext = schoolContext;
_adminRepos = adminRepos;
_teacherRepository = teacherRepository;
}
[HttpGet]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> Index()
{
ClaimsPrincipal currentUser = User;
var user = await _userManager.GetUserAsync(currentUser);
var admin = _adminRepos.GetAdminByUser(user);
return View(new AdminViewModel()
{
FirstName = admin.FirstName,
LastName = admin.LastName,
MiddleName = admin.MiddleName
});
}
[HttpGet]
public IActionResult Manage()
{
IEnumerable<string> teachers = _teacherRepository.TeacherNames();
return View(new CourseViewModel()
{
Teachers = teachers
});
}
[HttpPost]
public async Task<IActionResult> Manage(CourseViewModel courseViewModel)
{
var teacher = _schoolContext.Teacher.Single(t => t.FirstName == courseViewModel.TeacherName);
Course course = new Course()
{
CodeID = courseViewModel.CodeID,
Name = courseViewModel.Name,
NumOfCredits = courseViewModel.NumOfCredits,
TeacherID = teacher.TeacherID
};
await _schoolContext.Course.AddAsync(course);
if (await _schoolContext.SaveChangesAsync() == 0)
return RedirectToAction("Index", "Admin");
return View(courseViewModel);
}
}
}
Here is my View:
#model ASP_Project.ViewModels.CourseViewModel
#{
ViewData["Title"] = "Manage";
}
<h2>Manage</h2>
<div class="row">
<div class="col-md-4">
<form asp-controller="Admin" asp-action="Manage" method="post" class="form-horizontal" role="form">
<h4>Create a new Course.</h4>
<hr />
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="CodeID"></label>
<input asp-for="CodeID" class="form-control" />
<span asp-validation-for="CodeID" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Name"></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="NumOfCredits"></label>
<input asp-for="NumOfCredits" class="form-control" />
<span asp-validation-for="NumOfCredits" class="text-danger"></span>
</div>
<div>
<label asp-for="TeacherName" class="col-md-2 control-label"></label>
<div class="col-md-10">
<select asp-for="TeacherName" class="form-control" required>
<option value="" disabled selected>Select Teacher</option>
#foreach (var teach in Model.Teachers)
{
<option value="#teach"> #teach </option>
}
</select>
<span asp-validation-for="TeacherName" class="text-danger"></span>
</div>
</div>
<button type="submit" class="btn btn-default">Add</button>
</form>
</div>
</div>
#section Scripts {
#await Html.PartialAsync("_ValidationScriptsPartial")
}
My CourseViewModel:
using ASP_Project.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace ASP_Project.ViewModels
{
public class CourseViewModel
{
[Required]
public string CodeID { get; set; }
[Required]
public int NumOfCredits { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string TeacherName { get; set; }
public IEnumerable<string> Teachers { get; set; }
}
}
And Finally the Function used to retrieve the names of the teachers:
public IEnumerable<string> TeacherNames() => _schoolContext.Teacher.Select(t => t.FirstName);
What I understood from the exception is that there is either a part of the foreach that needs an await or that one of the Objects is not being defined.
Take note that the operation is doing its job successfully nonetheless and the data is being added to the database, its just that this strange exception keeps showing up.
Edit: Even though #NoCodeFound answer Pointed out that I should debug (and that's what I did to find the answer) yet I was planning on doing that anyway, and I happened to discover the real cause anyway.
Turns out I messed up when I return from the Manage action after POST, since I used:
if (await _schoolContext.SaveChangesAsync() == 0)
return RedirectToAction("Index", "Admin");
return View(courseViewModel);
which was making me go through the courseViewModel again rather than being redirected to the page I needed.
So the fix would simply be:
if (await _schoolContext.SaveChangesAsync() == 0)
return View(courseViewModel);
return RedirectToAction("Index", "Admin");
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