C# MVC Post form not passing ID from view to controller - c#

I am new to MVC. I work on an auction application. On the auction site, there should be a form for making a bid. I have a problem passing the auction parameter to the controller
My models:
public class Auctions
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string title { get; set; }
(..) some other fields
public List<Bid> bids = new List<Bid>();
}
public class BiddingViewModel
{
public Auctions auctionToSend { get; set; }
public double bid { get; set; }
}
My view:
#model BiddingViewModel
#using(Html.BeginForm("CreateBid", "Auction", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.HiddenFor(model=>model.auctionToSend)
#Html.EditorFor(model => model.bid)
<input type="submit" value="Make it work" />
}
and my controller:
[AllowAnonymous]
public ActionResult AuctionPage(int id)
{
var tmp = _context.Auctions.FirstOrDefault(i => i.ID == id);
BiddingViewModel bvm = new BiddingViewModel
{
auctionToSend = tmp,
bid = -1
};
return View(bvm);
}
[Authorize]
[HttpPost]
public async Task<ActionResult> CreateBid(BiddingViewModel bvm)
{
//After filling the form from view, the bvm.auctionToSend is null, whereas the bvm.bid value is visible
return RedirectToAction("AuctionList", "Auction");
}
My problem is that the auction data (perfectly visible in the view) is not sent back to the controller. I checked the internet and it showed me some naming-conflicts' solutions, so I made sure the naming is different, but this didn't fix my problem.

auctionToSend is a complex object and your use of #Html.HiddenFor(model=>model.auctionToSend) is generating
<input type="hidden" name="auctionToSend" value="yourAssembly.Auctions" ... />
If you just need the ID of the Auctions, then use
#Html.HiddenFor(m => m.auctionToSend.ID)
otherwise you need to generate a hidden input for each property of Auctions but that would be inefficient, particularly as Auctions contains a property which is a collection, so if you need the Auctions object in the POST method, better to just get it again based on the ID value your submitting.
As a side note, you really should be using a view model with just properties for the double Bid and int AuctionID

Related

Creating a form and binding to a class without a datatabase

I'm new to learning MVC and could really use some help- as I'm trying to move on from ASP.NET Web Forms. I have a custom web API for all my database transactions that is very efficient and reliable. I've recoded it in .NET Core.
My problem is that I'm finding most of the model binding examples pertaining to MVC are composed of Entity Framework examples. I'm looking for help showing how to link a ViewModel to a Controller with get() post(form) actions. I need to see how to bind to a radio button list, etc...
I'm working with the class below, which has removed the database connections to simplify the answers/suggestions.
public class BuildSearch
{
//Bootstrap date entry
public DateTime StartDate { get; set; }
//Bootstrap date entry
public DateTime EndDate { get; set; }
//Need this bound to a radio button list
public List<GeoArea> GeoAreas { get; set; }
public BuildSearch()
{
GeoAreas = new List<GeoArea>();
// StartDate = DateTime.Parse(DateTime.Now.AddDays(-31).ToShortDateString());
// EndDate = DateTime.Parse(DateTime.Now.ToShortDateString());
GeoAreas.Add(new GeoArea { GeoAreaItem = "Region", Id = 0 });
GeoAreas.Add(new GeoArea { GeoAreaItem = "Manager1", Id = 1 });
GeoAreas.Add(new GeoArea { GeoAreaItem = "Manager2", Id = 2 });
}
}
public class GeoArea
{
public int Id { get; set; }
public string GeoAreaItem { get; set; }
}
I'm trying to create a view, that will display this data, and then allow for me to post back the user edits. I'm purposely looking to keep the example simple as once I figure out how to post back, with the updated data I can handle the pass off to a web API to do the work I need to be done. Just frustrated trying to figure out how I bind to this type of class.
For the radio buttons I would add a property to your BuildSearch class called GeoAreaId. The radio button selection will be model bound to this property on post back. Your BuildSearch class therefore becomes
public class BuildSearch
{
//Bootstrap date entry
public DateTime StartDate { get; set; }
//Bootstrap date entry
public DateTime EndDate { get; set; }
public int GeoAreaId { get; set; } //added field
//Need this bound to a radio button list
public List<GeoArea> GeoAreas { get; set; }
public BuildSearch()
{
GeoAreas = new List<GeoArea>();
// StartDate = DateTime.Parse(DateTime.Now.AddDays(-31).ToShortDateString());
// EndDate = DateTime.Parse(DateTime.Now.ToShortDateString());
GeoAreas.Add(new GeoArea { GeoAreaItem = "Region", Id = 0 });
GeoAreas.Add(new GeoArea { GeoAreaItem = "Manager1", Id = 1 });
GeoAreas.Add(new GeoArea { GeoAreaItem = "Manager2", Id = 2 });
}
public class GeoArea
{
public int Id { get; set; }
public string GeoAreaItem { get; set; }
}
}
Your get method in your controller will look something like this
public IActionResult Search()
{
var buildSearch = new BuildSearch();
return View(buildSearch);
}
Your view will need to look something like this
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
#model BuildSearch
<form asp-controller="Home" asp-action="Search" method="post">
<label asp-for="StartDate">Start date</label>
<input asp-for="StartDate" />
<label asp-for="EndDate">End date</label>
<input asp-for="EndDate" />
<fieldset>
<legend>
GeoArea
</legend>
#foreach (var item in Model.GeoAreas)
{
<input type="radio" name="GeoAreaId" value="#item.Id" />
#item.GeoAreaItem
}
</fieldset>
<input type="submit" />
</form>
For the radio buttons note how the name attribute matches the new property GeoAreaId that I added to your BuildSearch class. This is important for the model binding to work.
Then your post method in your controller will need to look like this
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Search(BuildSearch buildSearch)
{
//...
}
To see what happens set a break point inside this method. Run the code, enter some values into the form and click submit. When the code has stopped inside hover over buildSearch and you will see the model binding has worked. The properties StartDate, EndDate and GeoAreaId will contain the values that you need from the form.
Here is an radio button example:
https://www.c-sharpcorner.com/article/radiobutton-in-asp-net-mvc/
here is a good example of getting your form data from the html form into the controller.
https://learn.microsoft.com/en-us/aspnet/core/tutorials/first-mvc-app/controller-methods-views?view=aspnetcore-2.2
Getting the data into the viewmodel you will do something like this
public async Task<IActionResult> Index()
{
var movies = await _context.Movies.ToList();
if (movies == null)
{
return NotFound();
}
return View(movies);
}
You will then need the form to have a post action to you Action e.g.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ID,Title,ReleaseDate,Genre,Price")] Movie movie)
{
if (ModelState.IsValid)
{
//post to api here
return RedirectToAction(nameof(Index));
}
return View(movie);
}
You will have to pass the model or view model into the html class as
#model MvcMovie.Models.Movie
#{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>Movie</h4> etc etc

How to Save ASP Net Membership Provider UserId into another sql table?

I want to get and Save ASP NET MVC Membership provider UserProfile table UserId field into a separate table?
Here is my model
public class Product
{
public int ProductId { get; set; }
public int UserId { get; set; }
public string ProductName { get; set; }
}
I don't want to add UserId field into my Model anymore.
and here is my controller:
private UsersContext db = new UsersContext();
// GET: /ProCont/
int UserID = (int)Membership.GetUser().ProviderUserKey;
public ActionResult Index()
{
return View(db.Products.Where(p => p.UserId == UserID).ToList());
}
What to do?
Regards
Dostdar
Disclaimer: It will be too long as a edit in already existing answer and hence posted in separate answer.
To answer your question on how will you add the UserID field in your model object below is a detailed description with code segment.
Considering that you have already added Products entity using ADO.NET Entity Data Model. Add a separate class file to your Models folder with any name you like and have the below code. Below code segment decorate your model object.
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
namespace MvcApplication1.Models
{
[MetadataType(typeof(productMetadata))]
public partial class Product
{
}
public class productMetadata
{
[HiddenInput(DisplayValue = false)]
public int ProductId { get; set; }
// to make the field hidden and will not display in browser
[HiddenInput(DisplayValue = false)]
//directing that don't scaffold this column since it will be added manually
[ScaffoldColumn(false)]
public int UserId { get; set; }
}
}
You can directly modify the auto-generated Product class in EF but if you re-generate the EF model again then the changes will be gone. So, above shown approach is best using MetadataType attribute.
Controller Action Method(s):
namespace MvcApplication1.Controllers
{
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create([Bind(Include = "ProductName, ProductId")]Product product)
{
SampleContext db = new SampleContext();
if (ModelState.IsValid)
{
product.UserId = (int)Membership.GetUser().ProviderUserKey;
db.Products.Add(product);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(product);
}
public ActionResult List()
{
SampleContext db = new SampleContext();
IEnumerable<Product> products = db.Products.ToList();
return View(products);
}
}
}
View (Specifically the Create view):
#model MvcApplication1.Models.Product
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Product</legend>
<div class="editor-label">
#Html.LabelFor(model => model.ProductName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ProductName)
#Html.ValidationMessageFor(model => model.ProductName)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Notice that the view has editable controls for only productname as input field since ProductID is an identity column and UserID getting value in code and not from user.
It's really not clear what you are asking here? But are you saying that you want to store the UserID into your Products table in database. Then you can designate your model class so like below
[System.ComponentModel.DataAnotations.Table("Products")]
public class Product
{
public int ProductId { get; set; }
public int UserId { get; set; }
public string ProductName { get; set; }
}
Either you can create the table Products manually or you can use EF code first Migrations to create a snapshot and update the database in order to add the newly created table.
Then you can add data to your table like
Product p1 = new Product
{
UserID = (int)Membership.GetUser().ProviderUserKey,
ProductName = "XXX";
};
db.Products.Add(p1);
db.SaveChanges();
Per your comment:
What you have currently is the right way to do it else there is no way you can relate a product(s) with a specific user. This is what called as Referential Integrity Constraint. Here UserID in your model class is acting as FOREIGN KEY. You just can't do it automatically.

Model properties are null when the form is submitted

On an ASP.NET MVC 5 project I have the following model:
public class ScheduleIndexModel {
public IPageList<DataModel> Data { get; set; }
public FormModel Form { get; set; }
public class DataModel {
public Int32 Id { get; set; }
public String[] Attendees { get; set; }
public String Location { get; set; }
public DateTime Date { get; set; }
} // DataModel
public class FormModel {
public String Location { get; set; }
public String Date { get; set; }
} // FormModel
}
The view is the following:
<form action="#Url.Action(MVC.Schedule.Index())" method="post">
#Html.LabelFor(x => x.Form.Date, "Date")
#Html.TextBoxFor(x => x.Form.Date)
#Html.LabelFor(x => x.Form.Location, "Location")
#Html.TextBoxFor(x => x.Form.Location)
#Html.SubmitButton("Filter", "Index", new { #class = "submit" })
#Html.AntiForgeryToken()
</form>
Then the HTTPPost controller action is as follows:
[HttpPost]
public virtual ActionResult Index(ScheduleIndexModel.FormModel model, Int32 p = 1) {
return View();
} // Index
When I submit the form the model is not null but its properties are even if I write something on the TextBoxes.
Does anyone knows what am I doing wrong?
You may need to use a binding prefix because your viewmodel is nested. Something like this may work:
public virtual ActionResult Index([Bind(Prefix = "Form")] FormModel model)
Your html helpers (e.g. #Html.TextBoxFor(x => x.Form.Date) will be generating html like this
<input name="Form.Date" .../>
but because your post method accepts parameter of type FormModel it cant match up (FormModel only has property Date, not property Form that has property Date).
You can either change you action method to
public virtual ActionResult Index(ScheduleIndexModel model,...
{
FormModel form = model.Form; // get the FormModel
or use the [Bind(Prefix..)] as suggested by Big Daddy

Can't get all my ViewModel properties back after the postback -MVC Partial Views

Can't get all my ViewModel properties back after the postback (After user entered some values on HttpPost)
There are numerous questions here related to losing data or getting nulls after the postback
I tried some of them and played around on my case, I think the scenario is a bit different,
Using a PartialView or Editor Templates(except a list property), Always the returned result properties are null.
In partialView approach always all the properties are null, I think maybe I missed a piece.
In the "custom editor template approach for the type", I'll have just "EnteredNums" List returned. (Maybe because these are what the template have EditorFor for them, but what is the solution here if that's the case?)
Don't know weather if it's important here or not, the application also uses Unity. I don't think it be the problem here.
The HttpGet Passed model is the same as HttpPost : DataVm
The name of the action is also the same : ProcessEnteredData
=================== Controller and action
[HttpPost]
public ActionResult ProcessEnteredData(DataVm vm)
{
if (ModelState.IsValid)
{
foreach (NumType num in vm.EnteredNums)
{
int i1 = num.Score1;
int i2 = num.Score2;
string profTitle = vm.Profile.Title;
Repository.Context.EnteredNums.Add(num);
}
return RedirectToAction("ShowTable");
}
else
{
return View(vm);
}
}
==============
The Partial View of Custom Editor Template are similar :
#model xxxx.NumType
#Html.LabelFor(m => m.TheTitle)
#Html.TextBoxFor(m => m.Score1)
#Html.TextBoxFor(m => m.Score2)
#Html.HiddenFor(m => m.Profile)
// Profile or ProfileId - Just used to see could it bring the property back or not as a test
============
NumType Model
[Key]
public int NumTypeId { get; set; }
[ForeignKey("Profile")]
[Required]
public int ProfileId { get; set; }
public int Score1 { get; set; }
public int Score2 { get; set; }
public int BoxId { get; set; }
public Box Box { get; set; } // something not important here
public virtual Profile Profile { get; set; }
============
The ViewModel
public class DataVm
{
public Profile Profile { get; set; }
public string TheTitle { get; set; }
public List<NumType> EnteredNums { get; set; }
// In the Editor template approach it's the only item with data and others are null
public List<Box> Boxes { get; set; }
}
=========
View for PartialView approach :
#model xxxx.DataVm
#using (Html.BeginForm("ProcessEnteredData", "Profile", FormMethod.Post))
{
#Html.AntiForgeryToken()
Model.EnteredNums = new List<NumType>();
foreach(var box in Model.Boxes)
{
NumType num = new NumType();
num.Profile = Model.Profile;
num.Box = box;
int iCount = Model.EnteredNums.Count;
Model.EnteredNums.Add(num);
#Html.Partial("NumView", Model.EnteredNums[iCount]);
}
<input type="submit" value="Do Process" />
}
===================
View for Editor for approach :
// instead of #Html.Partial :
#Html.EditorFor(m => m.EnteredNums[iCount]);

complex model stays null after post

My question is related to this question and answer
The following complex model:
public class EditSubmissionModel
{
public string foo { get; set; }
public Submission submission { get; set; }
}
The simple model
[Table(Name = "Submission")]
public class Submission
{
[Column(IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
public int SubmissionId { get; set; }
[Column]
public string Title { get; set; }
}
The view:
#model Project.WebUI.Models.EditSubmissionModel
#{
ViewBag.Title = "editSubmission";
}
<h2>editSubmission</h2>
#using (Html.BeginForm())
{
<legend>SubmissionModel</legend>
#Html.EditorFor(m => m.foo)
#Html.EditorFor(m => m.submission)
<input type="submit" value="Save" />
}
the editorTemplate
#model Project.Domain.Entities.Submission
#Html.EditorFor(m => m.Title)
the controller
[Authorize]
[HttpPost]
public ActionResult editSubmission(string shortName, EditSubmissionModel model)
{
shortname = "second" (is ok)
model.foo = aaa (also ok i edited it on the view)
model.submission = null (not binded? or i dont know?)
I can't see the error, any ideas?
Status no repro. Steps:
Create a new ASP.NET MVC 3 application using the default template
Define 2 models:
public class Submission
{
public int SubmissionId { get; set; }
public string Title { get; set; }
}
public class EditSubmissionModel
{
public string foo { get; set; }
public Submission submission { get; set; }
}
Modify HomeController so that it looks like this:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(string shortName, EditSubmissionModel model)
{
return Content(model.submission.Title);
}
}
Update ~/Views/Home/Index.cshtml view so that it looks like this:
#model EditSubmissionModel
#using (Html.BeginForm())
{
<legend>SubmissionModel</legend>
#Html.EditorFor(m => m.foo)
#Html.EditorFor(m => m.submission)
<input type="submit" value="Save" />
}
Add a custom editor template for the Submission type (~/Views/Home/EditorTemplates/.cshtml) like this:
#model Submission
#Html.EditorFor(m => m.Title)
Hit Ctrl+F5, fill in the form and submit. As totally expected the value you have entered in the Title textbox will be correctly bound and shown on the screen.
So I repeat the question that I've already asked you in the comments section: what did you do differently? You answered that it is a copy-paste from your code, but as I have illustrated you (with a full step-by-step guide) this is not the case.
Now here's a suspicion that I have. Your actual POST action looks like this:
public ActionResult editSubmission(string shortName, EditSubmissionModel submission)
and not like this:
public ActionResult editSubmission(string shortName, EditSubmissionModel model)
Notice the parameter name.
#Darin Dimitrov you were completely right, what do i do different. The code above was completely fine. The problem was the get command which looked like:
[Authorize]
public ActionResult editSubmission(string confShortName, string submission)
{
//do stuff
return View();
}
And the Modelbinder will get problems if the httpPost anywhere has same name like the HttpGet in my case string submission and Editsubmission.submission. Big thanks to your detailed step by step advice!

Categories