I have a form for users to submit data, but some fields such as UserID which the model requires are added using code behind as part of the OnPostAsync event. Even though my OnPostAsync should be submitting all the required information the form is throwing
an error when I click the submit button and it posts back saying that the UserID field is required.
A sample of my code is as follows:
From Posts.cs model:
[Required]
public string Userid
{
get;
set;
}
From Index.cshtml.cs
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
if (User.Identity.IsAuthenticated)
{
IdentityUser user = await _userManager.GetUserAsync(User);
Posts.Userid = await _userManager.GetUserIdAsync(user);
}
else
{
return Page();
}
_context.Posts.Add(Posts);
await _context.SaveChangesAsync();
string targeturl = linkgenerator.postlink(Posts.Postid, Posts.Title);
return RedirectToPage(targeturl);
}
There is also a problem when it requires required fields from models that only apply when specific categories are selected. When the user selects a category then a partial page is loaded via jQuery containing the inputs for that specific category. If the
user selects Shopping then they get inputs for the Shopping table, Housing for the Housing table, etc. Certain fields are marked as required the models for those tables, but they should only be required if records are being inserted into those specific tables
after the record is added to the Posts table. Then they get their data from the partial pages and Posts.Postid for Postid fields in those tables, where a relationship exists between the primary key Postid in the Posts table and a foreign key for Postid in
the category specific tables.
The problem here is that if I select a specific category like "Community" I get errors that fields required for all the other categories are missing. How do I mark something as required only if the corresponding category is selected?
Validation takes place before the OnPost(Async) method executes. If you intend to set the UserId property on the server after form submission, it should not be included as a form field in your page.
It is not a form field at all, but at the same time every post must have a UserID. Is there a way to mark the user ID as required in the model without having to have it included with the form before posting?
Right now the only workaround I can think of is removing the required annotation and just make every insert that needs it has it programmed anyway.
I got rid of some errors by removing the required annotations from things that are not populated via the form like UserID, but I still have the problem of category specific required fields being required no matter what category is selected. Those fields
must be required whenever a record is inserted into a category specific table, but not when a record is being added to a different category.
These are some code snippets from the relevant parts of my page:
Here is an example from the Services model
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace PostAlmostAnything.Models
{
public partial class Services
{
[Key]
public int Serviceid { get; set; }
[Required]
public int? Post { get; set; }
[Required(ErrorMessage = "Price Required!")]
[Range(.00, Double.PositiveInfinity, ErrorMessage = "Must Be Positive Number!")]
[Display(Name = "Price:")]
[DataType(DataType.Currency)]
[Column(TypeName = "decimal(18, 2)")]
public decimal? Price { get; set; }
[Required(ErrorMessage = "Service Rate Required!")]
[Range(1, Double.PositiveInfinity, ErrorMessage = "Service Rate Required!")]
[Display(Name = "Service Rate:")]
public int? Servicerate { get; set; }
[Required(ErrorMessage = "Currency Required!")]
[Range(1, Double.PositiveInfinity, ErrorMessage = "Currency Required!")]
[Display(Name = "Currency:")]
public int? Currency { get; set; }
[Required(ErrorMessage = "Service Hours Required!")]
[StringLength(500)]
[RegularExpression("^(?!.*<[^>]+>).*", ErrorMessage = "No HTML tags allowed!")]
[Display(Name = "Service Hours:")]
public string Hours { get; set; }
public virtual Currency CurrencyNavigation { get; set; }
public virtual Posts PostNavigation { get; set; }
public virtual Servicerate ServicerateNavigation { get; set; }
}
}
Then the form used to create a new post loads partials like this when a category is selected:
[BindProperty]
public Posts Posts
{
get;
set;
}
[BindProperty]
public Community Community
{
get;
set;
}
[BindProperty]
public Housing Housing
{
get;
set;
}
[BindProperty]
public Jobs Jobs
{
get;
set;
}
[BindProperty]
public Personals Personals
{
get;
set;
}
[BindProperty]
public Rantrave Rantrave
{
get;
set;
}
[BindProperty]
public Services Services
{
get;
set;
}
[BindProperty]
public Shopping Shopping
{
get;
set;
}
public InputModel Input
{
get;
set;
}
public IActionResult OnGet()
{
ViewData["ListofCategories"] = new SelectList(_context.Categories.Where(c => c.Site == GlobalStatic.SITENUMBER()), "Categoryid", "Categoryname");
return Page();
}
public PartialViewResult OnGetCategoryPartial(int Categoryid)
{
string partialname = String.Empty;
switch (Categoryid)
{
case 1:
partialname = "_CreateCommunity";
break;
case 2:
partialname = "_CreatePersonals";
break;
case 3:
partialname = "_CreateHousing";
break;
case 4:
partialname = "_CreateShopping";
break;
case 5:
partialname = "_CreateServices";
break;
case 6:
partialname = "_CreateJobs";
break;
case 8:
partialname = "_CreateRantrave";
break;
}
return Partial(partialname);
}
Here are the parts of the post event relevant to Posts and categories
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
// Set Parameters That Don't Require User Input
Posts.Adminban = false;
Posts.Active = true;
Posts.Dateposted = DateTime.Now;
Posts.Site = GlobalStatic.SITENUMBER();
Posts.Ipaddress = _accessor.HttpContext.Connection.RemoteIpAddress.ToString();
int? sc = Convert.ToInt32(HttpContext.Request.Form["Subcategoryid"]);
int? sc2 = Convert.ToInt32(HttpContext.Request.Form["Subcategory2id"]);
Posts.Subcategory = sc;
Posts.Subcategory2 = sc2;
if (User.Identity.IsAuthenticated)
{
IdentityUser user = await _userManager.GetUserAsync(User);
Posts.Userid = await _userManager.GetUserIdAsync(user);
}
else
{
return Page();
}
_context.Posts.Add(Posts);
if (Posts.Category != null)
{
int? catid = Posts.Category;
switch (catid)
{
case 1:
Community.Post = Posts.Postid;
_context.Community.Add(Community);
break;
case 2:
Personals.Post = Posts.Postid;
_context.Personals.Add(Personals);
break;
case 3:
Housing.Post = Posts.Postid;
_context.Housing.Add(Housing);
break;
case 4:
Shopping.Post = Posts.Postid;
_context.Shopping.Add(Shopping);
break;
case 5:
Services.Post = Posts.Postid;
_context.Services.Add(Services);
break;
case 6:
Jobs.Post = Posts.Postid;
_context.Jobs.Add(Jobs);
break;
case 8:
Rantrave.Post = Posts.Postid;
Rantrave.Nameurl = linkgenerator.rrtagurl(Rantrave.Name);
_context.Rantrave.Add(Rantrave);
break;
}
}
await _context.SaveChangesAsync();
string targeturl = linkgenerator.postlink(Posts.Postid, Posts.Title);
return RedirectToPage(targeturl);
}
So there it is. For efficiency I did not include all of the model and I limited the code for the dropdown lists to just categories because the partials loaded based on category on the ones with the required fields.
The problem seems to be that every partial is being required regardless of which one is actually loaded into the form. The solution to this is probably simple, but I am new to .Net Core and have never built a form this complex before. Even after I fix this
issue I still have to add a feature for uploading images.
UPDATE: I made some changes. I removed the BindProperty for all the category tables in my index.cshtml.cs file and I moved await _context.SaveChangesAsync(); up so that it runs before I need to grab the Postid of the new record. Now I get the following error:
Unfortunately, When I try to post to Community I get an error saying that
An unhandled exception occurred while processing the request.
<div class="titleerror">NullReferenceException: Object reference not set to an instance of an object.</div>
PostAlmostAnything.Pages.post.IndexModel.OnPostAsync() inIndex.cshtml.cs, line 177
Stack
Query
Cookies
Headers
Routing
<div id="stackpage" class="page">
NullReferenceException: Object reference not set to an instance of an object.
So, why isn't scoped identity working as described here https://www.entityframeworktutorial.net/faq/how-to-get-id-of-saved-entity-in-entity-framework.aspx
</div>
</div>
According to this
https://entityframework.net/retrieve-id-of-inserted-entity what I am doing should work because the record is created the same way right before the Postid is needed and used. I checked the database and confirmed that the new record was created and the new
record has a Postid, but Posts.Postid is null according to the page.
Thanks for all the great feedback Microsoft! It really is useful. Now I know that my best option is to export my SQL Sever database to a CSV and insert it into a MySQL database so that I can use PHP to create a site that works or maybe just use Wordpress
since at least Wordpress can be trusted to stay basically the same.
Thanks for all the great feedback Microsoft! It really is useful. Now I know that my best option is to export my SQL Sever database to a CSV and insert it into a MySQL database so that I can use PHP to create a site that works or maybe just use Wordpress
since at least Wordpress can be trusted to stay basically the same.
Of course what you have stated is very dubious. Your real issue IMHO is you don't understand how to effectively use the models, like knowing the difference between a viewmodel and a persistence model and when to use one as opposed to the other. The persistence
model should never be used in a view. The job of the viewmodel is to be strong typed to a view and used by the view.
Your other problem is that you have too much business and database logic in a page's action method. And before you go off, a Razor page still uses the ASP.NET MVC pipeline.
Example Razor page solution do things that the links explain along with using SoC in a layered style.
It seems that with the BindProperty removed the partials no longer bind to the model. When I try to make a new post in the community category I get an error saying that Community.Date is null even though the partial that I loaded clearly binds an input to
Community.Date. Here is the Community partial:
You're right, I don't know the "difference between a viewmodel and a persistence model" because I have never needed to know that before. I'll check out those links.
Would it be possible to make the BindProperty annotation conditional on the value of Posts.Category. That way the BindProperty would only apply to objects represented in views corresponding to the selected category?
Here is a workaround that just worked for inserting the date from the date input on the Community partial. Are there any vulnerabilities with this that I am not aware of?
var newCommunity = new Community();
newCommunity.Post = postid;
newCommunity.Date = Convert.ToDateTime(HttpContext.Request.Form["Community.Date"]);
_context.Community.Add(newCommunity);
Member
5 Points
207 Posts
Validation Firing for Required Fields Populated from Code Behind When Form is Submitted
Oct 24, 2020 01:46 AM|CopBlaster|LINK
I have a form for users to submit data, but some fields such as UserID which the model requires are added using code behind as part of the OnPostAsync event. Even though my OnPostAsync should be submitting all the required information the form is throwing an error when I click the submit button and it posts back saying that the UserID field is required.
A sample of my code is as follows:
There is also a problem when it requires required fields from models that only apply when specific categories are selected. When the user selects a category then a partial page is loaded via jQuery containing the inputs for that specific category. If the user selects Shopping then they get inputs for the Shopping table, Housing for the Housing table, etc. Certain fields are marked as required the models for those tables, but they should only be required if records are being inserted into those specific tables after the record is added to the Posts table. Then they get their data from the partial pages and Posts.Postid for Postid fields in those tables, where a relationship exists between the primary key Postid in the Posts table and a foreign key for Postid in the category specific tables.
The problem here is that if I select a specific category like "Community" I get errors that fields required for all the other categories are missing. How do I mark something as required only if the corresponding category is selected?
All-Star
194428 Points
28074 Posts
Moderator
Re: Validation Firing for Required Fields Populated from Code Behind When Form is Submitted
Oct 24, 2020 06:13 AM|Mikesdotnetting|LINK
Validation takes place before the OnPost(Async) method executes. If you intend to set the UserId property on the server after form submission, it should not be included as a form field in your page.
Member
5 Points
207 Posts
Re: Validation Firing for Required Fields Populated from Code Behind When Form is Submitted
Oct 24, 2020 06:29 AM|CopBlaster|LINK
It is not a form field at all, but at the same time every post must have a UserID. Is there a way to mark the user ID as required in the model without having to have it included with the form before posting?
Right now the only workaround I can think of is removing the required annotation and just make every insert that needs it has it programmed anyway.
All-Star
194428 Points
28074 Posts
Moderator
Re: Validation Firing for Required Fields Populated from Code Behind When Form is Submitted
Oct 24, 2020 04:00 PM|Mikesdotnetting|LINK
Please could you post the actual code where the error occurs, and the error message?
Member
5 Points
207 Posts
Re: Validation Firing for Required Fields Populated from Code Behind When Form is Submitted
Oct 25, 2020 02:09 AM|CopBlaster|LINK
I got rid of some errors by removing the required annotations from things that are not populated via the form like UserID, but I still have the problem of category specific required fields being required no matter what category is selected. Those fields must be required whenever a record is inserted into a category specific table, but not when a record is being added to a different category.
These are some code snippets from the relevant parts of my page:
Here is an example from the Services model
Then the form used to create a new post loads partials like this when a category is selected:
Then in the Index.cshtml.cs file I have this
Here are the parts of the post event relevant to Posts and categories
So there it is. For efficiency I did not include all of the model and I limited the code for the dropdown lists to just categories because the partials loaded based on category on the ones with the required fields.
The problem seems to be that every partial is being required regardless of which one is actually loaded into the form. The solution to this is probably simple, but I am new to .Net Core and have never built a form this complex before. Even after I fix this issue I still have to add a feature for uploading images.
UPDATE: I made some changes. I removed the BindProperty for all the category tables in my index.cshtml.cs file and I moved await _context.SaveChangesAsync(); up so that it runs before I need to grab the Postid of the new record. Now I get the following error:
Unfortunately, When I try to post to Community I get an error saying that
An unhandled exception occurred while processing the request.
<div class="titleerror">NullReferenceException: Object reference not set to an instance of an object.</div>
PostAlmostAnything.Pages.post.IndexModel.OnPostAsync() in
Index.cshtml.cs
, line 177<div id="stackpage" class="page">
NullReferenceException: Object reference not set to an instance of an object.
PostAlmostAnything.Pages.post.IndexModel.OnPostAsync() in
<button class="expandCollapseButton" data-frameid="frame1">-</button> <div class="source">Index.cshtml.cs
So, why isn't scoped identity working as described here https://www.entityframeworktutorial.net/faq/how-to-get-id-of-saved-entity-in-entity-framework.aspx
</div></div>
According to this https://entityframework.net/retrieve-id-of-inserted-entity what I am doing should work because the record is created the same way right before the Postid is needed and used. I checked the database and confirmed that the new record was created and the new record has a Postid, but Posts.Postid is null according to the page.
Member
5 Points
207 Posts
Re: Validation Firing for Required Fields Populated from Code Behind When Form is Submitted
Oct 27, 2020 02:04 AM|CopBlaster|LINK
Thanks for all the great feedback Microsoft! It really is useful. Now I know that my best option is to export my SQL Sever database to a CSV and insert it into a MySQL database so that I can use PHP to create a site that works or maybe just use Wordpress since at least Wordpress can be trusted to stay basically the same.
Contributor
4923 Points
4198 Posts
Re: Validation Firing for Required Fields Populated from Code Behind When Form is Submitted
Oct 27, 2020 04:10 AM|DA924|LINK
Of course what you have stated is very dubious. Your real issue IMHO is you don't understand how to effectively use the models, like knowing the difference between a viewmodel and a persistence model and when to use one as opposed to the other. The persistence model should never be used in a view. The job of the viewmodel is to be strong typed to a view and used by the view.
https://deviq.com/kinds-of-models/
https://www.dotnettricks.com/learn/mvc/understanding-viewmodel-in-aspnet-mvc
Your other problem is that you have too much business and database logic in a page's action method. And before you go off, a Razor page still uses the ASP.NET MVC pipeline.
Example Razor page solution do things that the links explain along with using SoC in a layered style.
https://github.com/darnold924/PubCompanyCore3.x
https://en.wikipedia.org/wiki/Separation_of_concerns#:~:text=In%20computer%20science%2C%20separation%20of,code%20of%20a%20computer%20program.
https://docs.microsoft.com/en-us/previous-versions/msp-n-p/ee658117(v=pandp.10)
All-Star
194428 Points
28074 Posts
Moderator
Re: Validation Firing for Required Fields Populated from Code Behind When Form is Submitted
Oct 27, 2020 06:55 AM|Mikesdotnetting|LINK
Member
5 Points
207 Posts
Re: Validation Firing for Required Fields Populated from Code Behind When Form is Submitted
Oct 27, 2020 11:36 PM|CopBlaster|LINK
It seems that with the BindProperty removed the partials no longer bind to the model. When I try to make a new post in the community category I get an error saying that Community.Date is null even though the partial that I loaded clearly binds an input to Community.Date. Here is the Community partial:
Here is the code behind from an async task for creating a new Community entry:
Member
5 Points
207 Posts
Re: Validation Firing for Required Fields Populated from Code Behind When Form is Submitted
Oct 27, 2020 11:49 PM|CopBlaster|LINK
You're right, I don't know the "difference between a viewmodel and a persistence model" because I have never needed to know that before. I'll check out those links.
Member
5 Points
207 Posts
Re: Validation Firing for Required Fields Populated from Code Behind When Form is Submitted
Oct 28, 2020 12:00 AM|CopBlaster|LINK
Would it be possible to make the BindProperty annotation conditional on the value of Posts.Category. That way the BindProperty would only apply to objects represented in views corresponding to the selected category?
Member
5 Points
207 Posts
Re: Validation Firing for Required Fields Populated from Code Behind When Form is Submitted
Oct 28, 2020 01:05 AM|CopBlaster|LINK
Here is a workaround that just worked for inserting the date from the date input on the Community partial. Are there any vulnerabilities with this that I am not aware of?