ApplicationDbContext.Update() doesn't update but saves it as a new record - asp.net-core-mvc

I am playing in the new APS.NET 5 RC1 environment but the default edit action creates a new entity of the object. I am searched the whole day finding where it goes wrong but I can't find out why it goes wrong.
Controller:
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.Data.Entity;
using SSTv5.Models;
using SSTv5.Models.Organisations;
using SSTv5.Models.Views;
using System.Collections.Generic;
using SSTv5.Models.Global;
namespace SSTv5.Controllers
{
public class OrganisationTypesController : Controller
{
private ApplicationDbContext _context;
private List<Breadcrumb> _bcList = new List<Breadcrumb>();
public OrganisationTypesController(ApplicationDbContext context)
{
_context = context;
}
#region Edit
// GET: OrganisationTypes/Edit/5
public async Task<IActionResult> Edit(int? id)
{
_bcList.Add(new Breadcrumb("OrganisationTypes", true, "Index", "Index"));
_bcList.Add(new Breadcrumb("Edit", false));
ViewBag.BcList = _bcList;
if (id == null)
{
return HttpNotFound();
}
OrganisationType organisationType = await _context.OrganisationType.SingleAsync(m => m.OrganisationTypeId == id);
if (organisationType == null)
{
return HttpNotFound();
}
return View(organisationType);
}
// POST: OrganisationTypes/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(OrganisationType organisationType)
{
if (ModelState.IsValid)
{
_context.Update(organisationType);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(organisationType);
}
#endregion
}
}
Form:
#model SSTv5.Models.Organisations.OrganisationType
#{
ViewData["Title"] = "Edit";
}
<h2>Edit</h2>
<form asp-action="Edit">
<div class="form-horizontal">
<h4>OrganisationType</h4>
<hr />
<div asp-validation-summary="ValidationSummary.ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="OrganisationTypeName" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="OrganisationTypeName" class="form-control" />
<span asp-validation-for="OrganisationTypeName" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="ClassIcon" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="ClassIcon" id="classIcon" />
<span asp-validation-for="ClassIcon" class="text-danger" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Edit" 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>
}

For anyone else who has this problem the answer is quite simple.
The form doesn't send the ID of the object with it. The only thing you need to do is put the id in a hidden field in the form. Then it will work fine.

Related

Why does ASP.NET Core 7 MVC ModelValidator always set ModelState.IsValid to true when the Model is invalid

I'm converting an ASP.NET 4.8 MVC web site to ASP.NET Core 7 MVC. I'm running into a problem with ModelValidation where my ModelState is always valid, even when it is not. I'm using the pattern that was used in .Net 4x and worked fine. Microsoft Learn link
Controller
[HttpPost]
public async Task<IActionResult> Create(TcPhoneType tcPhoneType)
{
if (ModelState.IsValid)
{
await _phoneTypeService.CreatePhoneType(tcPhoneType);
return RedirectToAction("Index");
}
return View(tcPhoneType);
}
Model.cs
// <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated>
#nullable disable
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace App.Models;
public partial class TcPhoneType
{
public string PhoneTypeId { get; set; }
public string PhoneTypeDescription { get; set; }
public virtual ICollection<MemberPhone> MemberPhones { get; } = new List<MemberPhone>();
}
ModelMetadata.cs
using System.ComponentModel.DataAnnotations;
namespace App.Models;
[MetadataType(typeof(TcPhoneTypeMetadata))]
public partial class TcPhoneType
{
}
public class TcPhoneTypeMetadata
{
[Required(AllowEmptyStrings = false, ErrorMessage = "Item is required")]
[StringLength(1)]
public string? PhoneTypeId { get; set; }
[Required(AllowEmptyStrings = false)]
[StringLength(5)]
public string? PhoneTypeDescription { get; set; }
}
Create.cshtml
#model SeasonCourt7.Models.TcPhoneType
#{
ViewBag.Title = "Add Phone Type Code";
}
<h2>#(ViewBag.Title)</h2>
<br>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="row mb-3">
<div class="col-sm-2">
<label asp-for="PhoneTypeId" class="col-form-label">Code</label>
</div>
<div class="col-sm-10">
<input asp-for="PhoneTypeId" class="form-control" autofocus />
</div>
<span asp-validation-for="PhoneTypeId" class="text-danger"></span>
</div>
<div class="row mb-3">
<div class="col-sm-2">
<label asp-for="PhoneTypeDescription" class="col-form-label">Description</label>
</div>
<div class="col-sm-10">
<input asp-for="PhoneTypeDescription" class="form-control" />
</div>
<span asp-validation-for="PhoneTypeDescription" class="text-danger"></span>
</div>
<div class="row mb-3">
<div class="offset-sm-2 col-sm-10">
<input type="submit" value="Create" class="btn btn-primary" />
Back
</div>
</div>
</div>
}
#section Scripts {
<partial name="_ValidationScriptsPartial" />
}
I set a breakpoint on the if (ModelState.IsValid) line so I could inspect the model and ModelState. I can see the data that is bound to the model is as expected and the ModelState is always valid.
If I pass null data in the model, I should see the ModelState.IsValid = false, but it's always true.
The model.cs is generated by EF and could be regenerated in the future which makes it impractical to add the metadata attributes for validation, as the file could be regenerated in the future.
The only way I've been able to get the ModelValidation to work as expected, is to decorate the model directly with the validation attributes.

How can I get full info on update page

I have an ASP.NET Core MVC project without Entity Framework.
When I click on the "update" button, I want to update page and make it does not become empty.
This is my view:
<div class="form-group">
<form asp-action="OdemeTurGuncelle">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
ID
<input asp-for="odemetur_id" class="form-control" />
<span asp-validation-for="odemetur_id" class="text-danger"></span>
</div>
<div class="form-group">
Ödeme Türü
<input asp-for="odemetur_adi" class="form-control" />
<span asp-validation-for="odemetur_adi" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
And my controller :
[HttpGet]
public IActionResult OdemeTurGuncelle()
{
return View();
}
[HttpPost]
public IActionResult OdemeTurGuncelle(OdemeTur odemeTur)
{
string constr = Genel.conString;
using (NpgsqlConnection con = new NpgsqlConnection(constr))
{
string query = "UPDATE odemeturleri SET odemetur_adi=#odemetur_adi WHERE odemetur_id=#odemetur_id";
using (NpgsqlCommand cmd = new NpgsqlCommand(query))
{
cmd.Connection = con;
con.Open();
cmd.Parameters.AddWithValue("#odemetur_id", odemeTur.odemetur_id);
cmd.Parameters.AddWithValue("#odemetur_adi", odemeTur.odemetur_adi);
cmd.ExecuteNonQuery();
con.Close();
}
}
// return View(ders);
return RedirectToAction("Index");
}
I can do it with Entity Framework, but I want to do it without EF

How to show Validation Summary in a modal form with ajax post method?

I'm beginner in ASP.NET Core MVC. I have a problem in my website code.
My model is User that has some fields, and I wrote a view model based on these fields.
I've written the following code:
My view model: RegisterViewModel:
public class RegisterViewModel
{
[Display(Name = "Username")]
[Required(ErrorMessage = "Enter {0} value please.")]
[MaxLength(20, ErrorMessage = "{0} Shouldn't have more than {1} Characters")]
public string Username { get; set; }
[Display(Name = "Password")]
[Required(ErrorMessage = "Enter {0} value please.")]
[MaxLength(50, ErrorMessage = "{0} Shouldn't have more than {1} Characters")]
public string Password { get; set; }
}
My controller: AccountController:
public class AccountController : Controller
{
private IUser _iuser;
public AccountController(IUser iuser)
{
_iuser = iuser;
}
public IActionResult Register()
{
return View();
}
[HttpPost]
public IActionResult Register(RegisterViewModel register)
{
if (ModelState.IsValid)
{
if (_iuser.isUsernameExist(register.Username))
{
ModelState.AddModelError("UserName", "This User Exists!");
return PartialView(register);
}
else
{
User user = new User()
{
Username = register.Username,
Password = HashGenerators.EncodingPassWithMD5(register.Password),
RoleId = 2,
};
_iuser.AddUser(user);
string TabName = "UserTab";
return Json(new { redirectToUrl = Url.Action("Index", "Profile", new {TabName}) });
}
}
else
{
return PartialView(register);
}
}
}
Register action has a view that has displayed as a modal.
View Register.cshtml:
<div class="row">
<div class="col-md-8 col-md-offset-2">
<hr />
<form asp-action="Register">
<div asp-validation-summary="ModelOnly" class="text-danger text-right"></div>
<div class="form-group">
<input asp-for="Username" class="form-control" , placeholder="username" id="txtUsername"/>
<span asp-validation-for="Username" class="text-danger" id="ValidationSummery"></span>
</div>
<div class="form-group">
<input asp-for="Password" class="form-control" , placeholder="password" id="txtPassword"/>
<span asp-validation-for="Password" class="text-danger text-right" id="ValidationSummery"></span>
</div>
<div class="form-group">
<input type="button" value="Create" onclick='AddUser()' class="btn-red pull-left" />
<button href="#" type="button" onclick='ClearForm()' class="btn-red pull-right"> Clear Form </button>
</div>
</form>
</div>
</div>
Modal code (at the end of above code):
<div id="myModal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div id="bodyModal" class="modal-body">
</div>
</div>
</div>
</div>
and at the end of view, I have these Ajax scripts:
<script>
function ClearForm() {
$.ajax({
url: "/Account/Register/",
type: "Get",
data: {}
}).done(function (result) {
$('#myModal').modal('show');
$('#bodyModal').html(result);
});
$('#myModal').modal('dispose'); }
</script>
<script>
function AddUser() {
$.ajax({
url: "/Account/Register/",
type: "Post",
data: {
Username: $("#txtUsername").val(),
Password: $("#txtPassword").val(),
},
success: function (response) {
window.location.href = response.redirectToUrl;
}
}).done(function (result) {
$('#myModal').modal('show');
$('#bodyModal').html(result);
});}
</script>
When program is running, the ClearForm button works well, the Create button works well when modelstate is valid. But when modelstate is not valid, it goes to an error page (Hello world!), I see validation errors for 1 second but browser opens an wrong page and doesn't stay on modal for showing validation error.
NOTE: When I delete: ( window.location.href = response.redirectToUrl;) from ajax AddUser function at success part, modal stays open and validation errors displays. But if modelstate is valid, modal stays open but it was empty and destination page(Index/Profile#UserTab) doesn't display.
Please help me, how can I change above code to solve this problem?
I think you can have a new partial view to show the validation message.
Add a hidden input field to the partial view:
<input name="IsValid" type="hidden" value="#ViewData.ModelState.IsValid.ToString()" />
Then in ajax success function determine whether to show the modal or redirect by judging the value of IsValid
A simple demo based on your codes.
Register.cshtml:
#model RegisterViewModel
#{
ViewData["Title"] = "Register";
}
<h1>Register</h1>
<div class="row">
<div class="col-md-8 col-md-offset-2">
<hr />
<form asp-action="Register">
<div asp-validation-summary="ModelOnly" class="text-danger text-right"></div>
<div class="form-group">
<input asp-for="Username" class="form-control" , placeholder="username" id="txtUsername" />
<span asp-validation-for="Username" class="text-danger" id="ValidationSummery"></span>
</div>
<div class="form-group">
<input asp-for="Password" class="form-control" , placeholder="password" id="txtPassword" />
<span asp-validation-for="Password" class="text-danger text-right" id="ValidationSummery"></span>
</div>
<div class="form-group">
<input type="button" value="Create" onclick='AddUser()' class="btn-red pull-left" />
<button href="#" type="button" onclick='ClearForm()' class="btn-red pull-right"> Clear Form </button>
</div>
</form>
</div>
</div>
<div id="myModal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div id="bodyModal" class="modal-body">
</div>
</div>
</div>
</div>
#section scripts{
<script>
function AddUser() {
$.ajax({
url: "/Account/Register/",
type: "Post",
data: {
Username: $("#txtUsername").val(),
Password: $("#txtPassword").val(),
},
success: function (response) {
$('#bodyModal').html(response);
var isValid = $('body').find('[name="IsValid"]').val() == 'True';
if (!isValid) {
$('#myModal').modal('show');
} else {
window.location.href = "#Url.Action("Index", "Profile")";
}
}
});
}
</script>
}
_Register.cshtml(Partial View):
#model RegisterViewModel
<form asp-action="Register">
<input name="IsValid" type="hidden" value="#ViewData.ModelState.IsValid.ToString()" />
<div asp-validation-summary="ModelOnly" class="text-danger text-right"></div>
<div class="form-group">
<input asp-for="Username" class="form-control" , placeholder="username" id="txtUsername" readonly />
<span asp-validation-for="Username" class="text-danger" id="ValidationSummery"></span>
</div>
<div class="form-group">
<input asp-for="Password" class="form-control" , placeholder="password" id="txtPassword" readonly />
<span asp-validation-for="Password" class="text-danger text-right" id="ValidationSummery"></span>
</div>
</form>
Controller:
public class AccountController : Controller
{
public IActionResult Register()
{
return View();
}
[HttpPost]
public IActionResult Register(RegisterViewModel register)
{
if (ModelState.IsValid)
{
// do some stuff
}
return PartialView("_Register",register);
}
}
Result:
With inspiring help of https://stackoverflow.com/users/11965297/mj1313, I Changed my code like following and my problem is solved entirely...
In controller, I send a parameter(Valid) by ViewBag to my view, to specify ModelState validity status.
[HttpPost]
public IActionResult Register(RegisterViewModel register)
{
if (ModelState.IsValid)
{
if (_iuser.isUsernameExist(register.Username))
{
ViewBag.Valid= "1"; // It's part of my change
ModelState.AddModelError("UserName", "This User Exists!");
return PartialView(register);
}
else
{
User user = new User()
{
Username = register.Username,
Password = HashGenerators.EncodingPassWithMD5(register.Password),
RoleId = 2,
};
_iuser.AddUser(user);
ViewBag.Valid= "0"; // It's part of my change
string TabName = "UserTab";
return Json(new { redirectToUrl = Url.Action("Index", "Profile", new {TabName}) });
}
}
else
{
ViewBag.Valid= "1"; // It's part of my change
return PartialView(register);
}
}
And in My view, I use a hidden input for save ViewBag.
<form asp-action="Register">
<input name="IsValid" type="hidden" value="#ViewBag.Valid" /> #*It's part of my change*#
<div asp-validation-summary="ModelOnly" class="text-danger text-right"></div>
<div class="form-group">
<input asp-for="Username" class="form-control" , placeholder="username"
</div>
.....
</form>
At the end, I change Ajax function, success part:
function AddUser() {
$.ajax({
url: "/Account/Register/",
type: "Post",
data: {
Username: $("#txtUsername").val(),
Password: $("#txtPassword").val(),
},
success: function (response) {
$('#bodyModal').html(response);
var isValid = $('body').find('[name="IsValid"]').val();
if (isValid) {
$('#myModal').modal('show');
} else {
window.location.href = response.redirectToUrl;
}
}
}).done(function (result) {
$('#myModal').modal('show');
$('#bodyModal').html(result);
});}

Remote validation fails with IFormFile attribute

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").

send data from jsp to controller using model attribute

<body>
<jsp:useBean id="loginBean" class="com.ss.sms.bean.LoginBean"></jsp:useBean>
<div class="row">
<div class="col-xs-10 col-xs-offset-1 col-sm-8 col-sm-offset-2 col-md-4 col-md-offset-4">
<div class="login-panel panel panel-default">
<div class="panel-heading">Log in</div>
<div class="panel-body">
form:form id="loginForm" method="post" action="login" modelAttribute="login">
<fieldset>
<div class="form-group">
<input class="form-control" placeholder="UserId" name="userId" type="text" autofocus="autofocus" value=${loginBean.userName}>
</div>
<div class="form-group">
<input class="form-control" placeholder="Password" name="password" type="password" value=${loginBean.password}>
</div>
<div class="checkbox">
<label>
<input name="remember" type="checkbox" value="Remember Me">Remember Me
</label>
</div>
<input type="submit" class="btn btn-primary">
<div style="color: red">${error}</div>
</fieldset>
</form:form>
</div>
</div>
</div><!-- /.col-->
</div><!-- /.row -->
#RequestMapping(value="/login",method = RequestMethod.POST)
public ModelAndView login(Model md,#ModelAttribute("login")LoginBean loginBean) {
System.out.println("hello done");
System.out.println(loginBean.getUserName());
ModelAndView model = null;
if (loginBean != null && loginBean.getUserName() != null & loginBean.getPassword() != null) {
if (loginBean.getUserName().equals("santosh") && loginBean.getPassword().equals("Santosh#123")) {
model = new ModelAndView("hello");
return model;
} else {
model = new ModelAndView("login");
return model;
}
} else {
model = new ModelAndView("login");
return model;
}
}
if i understand correctly fromn your code you have problem seeing the page correctly.
In your ModelAndView you add just the view and not the model
So you need to add the object
Hence, the code can look something like that
#RequestMapping(value="/login",method = RequestMethod.POST) public ModelAndView login(Model md,#ModelAttribute("login")LoginBean loginBean) {
System.out.println("hello done");
System.out.println(loginBean.getUserName());
ModelAndView model = null;
if (loginBean != null && loginBean.getUserName() != null & loginBean.getPassword() != null) {
if (loginBean.getUserName().equals("santosh") && loginBean.getPassword().equals("Santosh#123")) {
//if the view hello expects an object add it here
model = new ModelAndView("hello");
return model;
} else {
model = new ModelAndView("login").addObject("login",loginBean);
return model;
}
} else {
model = new ModelAndView("login").addObject("login",loginBean);
return model;
}
}

Resources