Html.BeginForm post null model - c#

I have created a create page using asp.net scafolding and below is my page to create expenses.
#model ExpenCare.Models.ExpenseCategory
#{
ViewBag.Title = "CreateExpenseCategories";
}
<div class="row">
#Html.Partial("_RightSidePane")
<div class="col-10 col-sm-8 col-xs-8 col-md-10">
<div class="tab-content" id="nav-tabContent">
<div class="tab-pane fade show active" id="list-Profile-Content" role="tabpanel" aria-labelledby="list-Profile">
#using (Html.BeginForm("CreateExpenseCategories", "Admin", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>ExpenseCategory</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Expense, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Expense, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Expense, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Description, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Description, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "text-danger" })
</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>
}
</div>
</div>
</div>
</div>
On create this page post data to CreateExpenseCategories method of Admin controller.
public ActionResult CreateExpenseCategories(ExpenseCategory expense)
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:1888/api/");
var responseTask = client.PostAsJsonAsync("ExpenseCategories", expense);
responseTask.Wait();
var result = responseTask.Result;
if (result.IsSuccessStatusCode)
{
return RedirectToAction("ExpenseCategories");
}
else
{
ModelState.AddModelError(string.Empty, "Server Error. Please contact administrator.");
}
return View("ExpenseCategories/CreateExpenseCategories", model: expense);
}
The model used to create this page is as below,
namespace ExpenCare.Models
{
public class ExpenseCategory
{
public int Id { get; set; }
[Required]
public string Expense { get; set; }
public string Description { get; set; }
public Company Company { get; set; }
}
}
When I create an expense this always give null exception error and when I debug, the posted data model is null. This must be a small issue for sure, because I have done other create pages in similar way and I did nit encounter any issue. I must be missing a small thing and this might be a stupid question, but I can't find why this gives a null exception error on post.

I think the problem is the usage of Html.EditorFor,
Try this
#Html.TextBoxFor(model => model => model.Expense,new { #class = "form-control" })
or
#Html.EditorFor(model => model.Expense,null,"Expense",new { htmlAttributes = new { #class = "form-control" } } )
Edit problem solved
Property on type ExpenseCategory has the same parameter 'expense'. Change
public ActionResult CreateExpenseCategories(ExpenseCategory expense)
to
public ActionResult CreateExpenseCategories(ExpenseCategory model)

In your model the Id field is not null able Probably you should add an input or hidden input for it to correct model binding:
#Html.HiddenFor(model => model.Id)
Revise:
I test this case and model binding in mvc is working and setting default value for id. Entirely in absence of an input in form model binding in mvc still working. This behavior is sense because all fields by default are optional and we can set required attribute to enforce inputting them.

Related

View returns null to a controller, accepting ViewModel

I have a problem with my controller. It accepts null.
First of all I've got such ViewModel
public class FilmVM
{
public Film Film { get; set; }
public IEnumerable<Image> Images { get; set; } = new List<Image>();
public IEnumerable<SimilarFilm> SimilarFilms { get; set; } = new List<SimilarFilm>();
}
Controller looks like
public ActionResult Edit(FilmVM film)
{
_unitOfWork.Film.Update(film);
return RedirectToAction("Index");
}
Index action looks like
public ActionResult Index(int? page)
{
int pageSize = Constaints.Constaint.FilmsCount;
int pageNumber = page ?? 1;
var films = _unitOfWork.Film.GetDatas();
return View(films.ToPagedList(pageNumber, pageSize));
}
Models in VM are defaults like Guid, name, etc.
View model looks like
#model GuessTheMovieApp.ViewModels.FilmVM
#{
ViewBag.Title = "Edit";
}
<div class="wrapper">
<h2>Edit</h2>
#using (Html.BeginForm("Edit", "Films", FormMethod.Post, new { #class = "form-edit-film" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Editing Film</h4>
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.Partial("EditFilm", Model.Film)
<div class="form-group">
<div class="form-group__input">
<input type="submit" value="Save" class="btn btn-success" />
</div>
</div>
</div>
}
</div>
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Partial View looks like
#model GuessTheMovieApp.Models.DataBase.Film
<div class="form-horizontal">
<h4>Film</h4>
<hr />
#Html.HiddenFor(model => model.FilmId)
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Year, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Year, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Year, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Genre, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Genre, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Genre, "", new { #class = "text-danger" })
</div>
</div>
</div>
they are common, I think.
I saw similar question on stackoverflow, but it didn't helped me. So I'm asking if someone knows an answer on such question, please help.
UPD
Figured out how to work with this by changing Action Edit to this format
public ActionResult Edit(Film films, IEnumerable<Image> images, IEnumerable<SimilarFilm> similarFilms)
{
_unitOfWork.Film.Update(new FilmVM { Film = films, Images = images, SimilarFilms = similarFilms });
_unitOfWork.Save();
return RedirectToAction("Index");
}
I don't think you can prevent your controller from accepting null at the parameter level, the best you can do is to do a null check on the object coming in before you call your _unitofWork
try to use the same model for view, partial view and action. You can use FilmVm as well as any another model. For example you can use Film in this case
#model GuessTheMovieApp.ViewModels.Film
.....
<partial name="EditFilm" />
......
and action
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(Film film)
{
var filmVm= new FilmVm { Film=film}
.....
}

Remote validation not working in ASP.NET MVC 5

Here I have create form which consists customer email field for which I am trying to check whether the entered email already exits or not and if exist show email already exists message.
To do this i have tried to use remote validation but the problem is that its not showing any error even though email exists, it not even hitting the controller in IsEmailExists method which is used for remote validation
Any help with my code will be a great help. Thank you
Below is my action in controller
public JsonResult IsEmailExists(string CustomerEmail)
{
emedicineEntities _db = new emedicineEntities();
return Json(!_db.Customers.Any(x => x.CustomerEmail == CustomerEmail), JsonRequestBehavior.AllowGet);
}
Below is my metadata
namespace eMedicine.Model
{
public class CustomerMetaDta
{
[Remote("IsEmailExists", "Customers", ErrorMessage = "EmailId already exists.")]
[Required(ErrorMessage = "Please Enter Emailw")]
public string CustomerEmail { get; set; }
}
}
Below is my partial class
namespace eMedicine.Model
{
[MetadataType(typeof(CustomerMetaDta))]
public partial class Customer
{
}
}
Below is my view consisting customer email
<link href="~/Content/Site.css" rel="stylesheet" />
<script src="~/Scripts/jquery-3.3.1.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
#using (Html.BeginForm("Create", "Customers", FormMethod.Post, new { #id = "register" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.CustomerName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.CustomerName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CustomerName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CustomerEmail, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.CustomerEmail, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CustomerEmail, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.PasswordHash, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.PasswordHash, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.PasswordHash, "", new { #class = "text-danger" })
</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>
}
Change your method signature as follows to include the Bind Prefix attribute/property.
public JsonResult IsEmailExists([Bind(Prefix="Customer.CustomerEmail")] string CustomerEmail)
{
emedicineEntities _db = new emedicineEntities();
return Json(!_db.Customers.Any(x => x.CustomerEmail == CustomerEmail), JsonRequestBehavior.AllowGet);
}
Now it should work!
Not sure what real problem with your source code, but i tried to reproduce in my side, it worked well.
Here are my source code.
namespace WebApplication1.Controllers
{
public class CustomerMetaDta
{
[Remote("IsEmailExists", "Customer", ErrorMessage = "EmailId already exists.")]
[Required(ErrorMessage = "Please Enter Emailw")]
public string CustomerEmail { get; set; }
}
[MetadataType(typeof(CustomerMetaDta))]
public partial class Customer
{
}
public partial class Customer
{
public string CustomerEmail { get; set; }
public string CustomerName { get; set; }
public string PasswordHash { get; set; }
}
public class CustomerController : Controller
{
public JsonResult IsEmailExists(string CustomerEmail)
{
//emedicineEntities _db = new emedicineEntities();
List<Customer> _db = new List<Customer>
{
new Customer { CustomerEmail = "hien#gmail.com"},
new Customer { CustomerEmail = "hien1#gmail.com"}
};
return Json(!_db.Any(x => x.CustomerEmail == CustomerEmail), JsonRequestBehavior.AllowGet);
}
// GET: Customer
public ActionResult Index()
{
return View();
}
}
}
Index.cshtml file:
#model WebApplication1.Controllers.Customer
#{
ViewBag.Title = "Index";
}
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.0/jquery.validate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min.js"></script>
#using (Html.BeginForm("Create", "Customer", FormMethod.Post, new { #id = "register" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.CustomerName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.CustomerName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CustomerName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CustomerEmail, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.CustomerEmail, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CustomerEmail, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.PasswordHash, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.PasswordHash, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.PasswordHash, "", new { #class = "text-danger" })
</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>
}
It jumps to method IsEmailExists() and this is result output
May be you missed setting
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
in web.config?
If it's not hitting the controller that suggests it isn't a problem with the actual validation logic, but more an issue with how you're addressing the server.
There are a few things to check:
Is your remote validation code available to the client at all?
The first thing to consider is could this be a security/authentication issue. There are a few simple things you can do to check that:
If you have authentication attributes set on your controllers or methods, try commenting them out
try commenting out any other authentication code
If that doesn't fix it, then when you've got the app running in debug, try using Postman to call your remote validation endpoint and see whether:
Postman gets a 200 back from your method.
If so, put a breakpoint in your code and check it is actually getting executed.
If Postman can get to your endpoint then...
Is there an issue in your code?
I can't see anything obviously wrong in your code, but it is different to how I write validation code. This is an example of some working remote validation straight out of my code
This is the model property with the remote validation set:
[System.Web.Mvc.Remote(
action: "CheckExistingDocumentCode",
controller: "Documents",
AdditionalFields = "DocumentId",
HttpMethod = "POST",
ErrorMessage = "Code already exists")]
public string DocumentCode { get; set; }
This is the corresponding method in the Documents controller:
[HttpPost]
public async Task<ActionResult> CheckExistingDocumentCode(string DocumentCode, int DocumentId)
{
try
{
if (!await _documentValidationRules.IsExistingDocumentCodeAsync(DocumentCode, DocumentId))
{
return Json(true, JsonRequestBehavior.AllowGet);
}
return Json("This Document Code is already in use", JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
return Json(ex.ToString(), JsonRequestBehavior.AllowGet);
}
}
You can see that I've explicitly named all the parameters on the model property just to be clear exactly what's going on.
Comparing my code to yours the main differences are:
Mines async. That shouldn't make any difference.
My contrller method is a POST (so is has the HttpPost attribute, which also means I needed to tell the model the HttpMethod was POST too)
My remote validation method takes two parameters, so I'm passing in an extra property via the AdditionalFields argument
I can't see what the issue is in your code, but try changing it a piece at a time to work more like mine (particularly, try making it a post method and naming the parameters) and see if that exposes any issues.
Hopefully something in the above will get you closer.
One thing no one mentioned which will cause your symptons: If the method needs to be executed by Anonymous users and you don't have the AllowAnonymous attribute on your method, the method will not fire (and the submit button won't do anything.
You can try to add this code to the bottom of your View:
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
This is the issue I had.
//[AcceptVerbs("Get,Post")]
//[AllowAnonymous]
public async Task<IActionResult> IsEmailInUse(string email)
{
var registration = await
_context.Registration.FirstOrDefaultAsync(m => m.Email == email);
if(registration == null)
{
return Json(true);
}
else
{
return Json($"Email {email} is already in use");
}
}
//In Model Class
public class Registration
{
[System.ComponentModel.DataAnnotations.Key]
public int EmpId { get; set; }
public string UserName { get; set; }
[Required]
[EmailAddress]
[Remote(action: "IsEmailInUse",controller: "Registrations")]
public string Email { get; set; }
}

Posted ViewModel is always NULL

My ViewModel always returns null and don't know why. Can someone look at my code and check what is wrong here and why my filled model with data from view returns to controller as null?
public class PaintballWorkerCreateViewModel
{
public PaintballWorker PaintballWorker { get; set; }
public PaintballWorkerHourlyRate HourlyRate { get; set; }
}
Controller
public ActionResult Create()
{
PaintballWorkerCreateViewModel model = new PaintballWorkerCreateViewModel()
{
PaintballWorker = new PaintballWorker(),
HourlyRate = new PaintballWorkerHourlyRate()
{
Date = DateTime.Now
}
};
return View(model);
}
[HttpPost]
[PreventSpam(DelayRequest = 20)]
[ValidateAntiForgeryToken]
public ActionResult Create(PaintballWorkerCreateViewModel paintballWorker)
{
(...)
}
View, even added HiddenFor IDs (which aren't created in GET function in controller).
#model WerehouseProject.ViewModels.PaintballWorkerCreateViewModel
#{
ViewBag.Title = "Utwórz pracownika";
Layout = "~/Views/Shared/_Layout_Paintball.cshtml";
}
<h2>Dodawanie pracownika</h2>
#using (Html.BeginForm("Create", "PaintballWorkers", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.PaintballWorker.Active)
#Html.HiddenFor(model => model.PaintballWorker.MoneyGot)
#Html.HiddenFor(model => model.PaintballWorker.PaintballWorkerID)
#Html.HiddenFor(model => model.HourlyRate.Date)
#Html.HiddenFor(model => model.HourlyRate.PaintballWorkerID)
#Html.HiddenFor(model => model.HourlyRate.PWHourlyRateID)
<div class="form-group">
#Html.LabelFor(model => model.PaintballWorker.Imie, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.PaintballWorker.Imie, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.PaintballWorker.Imie, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.PaintballWorker.Nazwisko, htmlAttributes: new { #class = "control-label col-md-2" })
(...)
<div class="form-group">
#Html.LabelFor(model => model.HourlyRate.HourlyRate, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.HourlyRate.HourlyRate, new { htmlAttributes = new { #class = "form-control", #type = "number", #min = "0.1", #step = "0.1", #value = "10" } })
#Html.ValidationMessageFor(model => model.HourlyRate.HourlyRate, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Dodaj pracownika" class="btn btn-primary" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Powrót do listy", "Index", new object { }, new { #class = "btn btn-default" })
</div>
Your code looks fine.. looks like it's reference is being lost somewhere.. have you tried to remove the [PreventSpam(DelayRequest = 20)] attribute? So your controller would be like this:
public ActionResult Create()
{
PaintballWorkerCreateViewModel model = new PaintballWorkerCreateViewModel()
{
PaintballWorker = new PaintballWorker(),
HourlyRate = new PaintballWorkerHourlyRate()
{
Date = DateTime.Now
}
};
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(PaintballWorkerCreateViewModel paintballWorker)
{
(...)
}
You are not posting the form correctly. Since the attributes are hidden, they are null by default.
After posting your controller does not see anything since the elements are hidden. Therefore it is null.
Use the extension method #Html.TextboxFor instead. Mvc viewengine will render the textbox and then you can put some values and post them.
you also need to make sure that you have mapped the route correctly in your code.
your problem may be because of naming conflict, that is the parameter name of the post action may not be the same as the property name in your viewmodel
Kindly follow the below link:
https://forums.asp.net/t/1670962.aspx?ViewModel+in+post+action+is+null
In that it specified solution and route cause for the problem clearly.
Note : I also had the same problem long back and solved it in the same way as mentioned. Hope it will be useful for you too
thanks
Karthik

How to realize Edit method with 2 models in 1 View

The beginning of my topic is here.
Using DropDownList dont allow me to edit Author`s name.. But that solution is really good.
I need to edit all 4 fields like name, title, year and price.
I try to make something like composite model:
public class BookAuthorCompositeModel
{
public Author author { get; set; }
public Book book { get; set; }
}
Change my get method:
// GET: Books/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return HttpNotFound();
}
var compModel = new BookAuthorCompositeModel();
compModel.book = dbBooks.Books.ToList().Where(a => a.Id == id).SingleOrDefault();
compModel.author = dbBooks.Authors.ToList().Where(x => x.Id == id).SingleOrDefault();
return View(bookEdit);
}
and it displays all I need (i tap "edit" and see information about name, title, year and price).
My view is:
#model BookStorage.Models.BookAuthorCompositeModel
#{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Edit</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Book</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.book.Id)
<div class="form-group">
#Html.LabelFor(model => model.author.AuthorName, "AuthorName", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.author.AuthorName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.author.AuthorName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.book.Title, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.book.Title, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.book.Title, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.book.YearPublish, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.book.YearPublish, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.book.YearPublish, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.book.Price, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.book.Price, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.book.Price, "", new { #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>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
but something wrong is in Post method:
[HttpPost]
public ActionResult Edit(Book book)
{
if (ModelState.IsValid)
{
dbBooks.Entry(BAmodel.book).State = EntityState.Modified;
dbBooks.Entry(BAmodel.author).State = EntityState.Modified;
dbBooks.SaveChanges();
return RedirectToAction("Index");
}
return View(book);
}
dbBooks.SaveChanges(); leads to error (Store update, insert, or delete statement affected an unexpected number of rows (0).). I think using this kind of ViewModel helps to edit all rows, but it doesn't send data to DB, and I can't understand what is wrong.
If I understood correctly, what you want is to update a context "Book" and a context "Author" at the same API method.
To do so, first I would make a ViewModel that contains both objects, which you already have:
public class BookAuthorCompositeModel
{
public Author author { get; set; }
public Book book { get; set; }
}
(This can be modified for validation purposes, maybe creating new objects for validation. At least, I did it like that. But let's go on.)
Now, you need to receive that same ViewModel on your API call, and then do what you need with the objects. To do so, I usually do a Get call on my context, obtaining the object to be modified. Then I'm free to update my context:
[HttpPost]
public ActionResult Edit(BookAuthorCompositeModel model)
{
...
}
To get the object to be modified, depends on what you are using. Simple repository pattern, Unit pattern, doesn't matter. When you get the Book entry, just modify it, use Update function, which I belive to be dbBooks.BookContext.Update(book), where "book" is variable. Something like this:
var book = dbBooks.BooksContext.Get(model.Book.BookId);
book.name = "New Name";
dbBooks.BooksContext.Update(book);
dbBooks.SaveChanges();
The method for getting and updating ( .Get() and .Update() ) must be the ones from the pattern that you are using, but maybe this can help you.
Good luck!

Asp.Net MVC 5 Html.DropDownList is not validating

I've been stuck on this problem for a while now and can't seem to find anything online that helps.
I have a Html.DropDownList in an Editor Template. I use that template in my form and don't select anything for the dropdownlist. When I click submit, I expect the form to notify me that the required dropdownlist doesn't have a value selected, but it doesn't. What am I doing wrong?
Here are the View Models:
public class RequestCreateViewModel
{
public int ID { get; set; }
public TesterLocationCreateViewModel TesterLocationCreateViewModel { get; set; }
....
public RequestCreateViewModel()
{
}
}
public class TesterLocationCreateViewModel
{
public int ID { get; set; }
[Required]
[UIHint("OEM")]
public string OEM { get; set; }
[Required]
[DisplayName("City")]
public string LocationCity { get; set; }
public TesterLocationCreateViewModel()
{
}
}
Here's a snippet of the Create.cshtml
#model WdcTesterManager.Models.Request
#{
ViewBag.Title = "Create Request";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Create Request</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.EditorFor(model => model.TesterLocationCreateViewModel)
</div>
</div>
}
Here's the TesterLocationCreateViewModel.cshtml (Editor Template):
#model WdcTesterManager.Models.TesterLocationCreateViewModel
<div class="col-md-6">
<h4>Tester Location</h4>
<div class="container-fluid">
<div class="form-group">
#Html.LabelFor(model => model.OEM, htmlAttributes: new { #class = "control-label col-md-4" })
<div class="col-md-8">
#Html.DropDownList("OEM", (SelectList)ViewBag.OemList, "Choose one", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.OEM, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.LocationCity, htmlAttributes: new { #class = "control-label col-md-4" })
<div class="col-md-8">
#Html.EditorFor(model => model.LocationCity, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.LocationCity, "", new { #class = "text-danger" })
</div>
</div>
</div>
</div>
When I submit the form without filling anything out, I get a validation error for the City but nothing for the OEM. Any ideas?
#StephenMuecke pointed out that something looked wrong with the code, since the code seemed to be referencing the Request model rather than the Request View model.
So, I took a second look at the code and realized that the Create.cshtml was using the Request model rather than the RequestCreateViewModel.
I'm getting the correct validation errors after changing the Create.cshtml to use the RequestCreateViewModel:
#model WdcTesterManager.Models.RequestCreateViewModel

Categories