MVC 5 not validating StringLength attribute properly - c#

I'm trying to validate the sortCode field in my PersonPaymentDetails model but my view is failing to validate the StringLength(6). If I submit the form with a value of length 1 it incorrectly validates successfully.
Am I doing something fundamentally wrong here?
/* [CONTROLLER] */
public class PersonController : Controller
{
[HttpGet]
[Route("person/paymentDetails/create/{personId?}")]
public ActionResult PaymentDetailsCreate(int? personId)
{
if (personId == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Person person = db.People.Find(personId);
if (person == null)
{
return HttpNotFound();
}
PersonPaymentDetailsViewModel personPaymentDetailsVM = new PersonPaymentDetailsViewModel();
personPaymentDetailsVM.SetPerson(person);
return View(personPaymentDetailsVM);
}
[HttpPost]
[Route("person/paymentDetails/create")]
public ActionResult PaymentDetailsCreate(PersonPaymentDetailsViewModel personPaymentDetailsVM)
{
if (ModelState.IsValid)
{
/* should not be entering here with sortCode = 123, as not 6 characters in length */
return Content("No errors: |" + personPaymentDetailsVM.SinglePaymentDetails.sortCode + "|");
}
}
}
/* [VIEW] */
#model X.ViewModels.PersonPaymentDetailsViewModel
#Html.ValidationSummary()
#using (Html.BeginForm("PaymentDetailsCreate", "Person", FormMethod.Post, new { #class = " form-horizontal" }))
{
#Html.HiddenFor(m => m.Person.id, "default")
<div class="form-group">
<label for="bankSortCode" class="col-md-3 control-label">Sort Code</label>
<div class="col-md-9">
#Html.EditorFor(m => m.SinglePaymentDetails.sortCode, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div class="form-group">
<label for="save" class="col-md-3 control-label"> </label>
<div class="col-md-9">
<button type="submit" class="btn btn-primary">Save</button>
</div>
</div>
}
/* [MODEL] */
public partial class PersonPaymentDetails
{
public int id { get; set; }
[Required, StringLength(6)]
public string sortCode { get; set; }
}
/* [ViewModel] */
public class PersonPaymentDetailsViewModel
{
public Person Person { get; set; }
public PersonPaymentDetails SinglePaymentDetails { get; set; }
public void SetPerson(Person person)
{
this.Person = person;
this.SinglePaymentDetails = new PersonPaymentDetails();
}
}

You want
[Required, StringLength(6, MinimumLength = 6)]
Constructor of StringLength takes in the maximum length only, so as you currently have it it checks that string is not longer than 6 characters, and therefore string of length 1 passes validation successfully.

Related

Why is the DepartmentID value not being properly bound to the view model's property?

Not sure why but the for some reason the view model is not binding the DepartmentId when I hit the submit button, I get an error:
Value cannot be null.
Parameter name: value
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Not sure which value is being null as the code does not break, rather after hitting the submit button that is the message that is being displayed.
I'm assuming it's the departmentId that's not being properly bound to the DepartmentID property in CoursePreReqViewModel.
Exception Details: System.ArgumentNullException: Value cannot be null.
Parameter name: value
View:
#using (Html.BeginForm("Catalog", "Courses", FormMethod.Post, new { #class = "pure-form pure-form-aligned" }))
{
#Html.AntiForgeryToken()
<div class="row">
<div class="col">
<input id="myInput" class="form-control" type="text" placeholder="Search..">
</div>
<div class="col">
#Html.LabelFor(model => model.DepartmentId, htmlAttributes: new { #class = "control-label col-md-2" })
#Html.DropDownListFor(model => model.DepartmentId, Model.DepartmentList, "Department", new { #class = "form-control required", id = "department-list" })
#Html.ValidationMessageFor(model => model.DepartmentId)
</div>
<div class="col">
<button type="submit" class="btn btn-success">Submit</button>
</div>
</div>
}
Controller:
[HttpPost]
public ActionResult Catalog(CoursePreReqViewModel viewModel)
{
DepartmentRepo dRepo;
CoursesRepo cRepo;
MajorPreRequisitesRepo reqRepo;
using (context)
{
dRepo = new DepartmentRepo(context);
cRepo = new CoursesRepo(context);
viewModel.PopulateDepermentSelectList(dRepo.GetAllDepartments());
reqRepo = new MajorPreRequisitesRepo(context);
viewModel.Courses = cRepo.GetAllCoursesAndPreReqsByDepartment(viewModel.DepartmentId);
}
return View(viewModel);
}
View model:
public class CoursePreReqViewModel
{
[Required]
[Display(Name = "")]
public int DepartmentId { get; set; }
public IList<Course> Courses { get; set; }
public IList<MajorPreRequisite> CoursesAndPreReqs { get; set; }
[Display(Name = "Department: ")]
public IList<Department> Departments { get; set; }
public CoursePreReqViewModel() { }
public SelectList DepartmentList
{
get
{
return new SelectList(Departments, "Id", "Name");
}
}
public void PopulateDepartmentSelectList(IList<Department> populatedDepartments)
{
Departments = populatedDepartments;
}
}
Sarthak here is another answer: this is the asp.net mvc fiddler https://dotnetfiddle.net/ARdtvr
!!!Display Name needs to be a value!!!
I can help you keep the code you have, and answer your question.
controller/classes:
public class Course
{
public int CourseId { get; set; }
}
public class MajorPreRequisite
{
public int MajorPreRequisiteId { get; set; }
}
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
}
public class CoursePreReqViewModel
{
//Make sure to comment this out -or- put this field in view
//try this first with your code, before you change code eg using a dictionary
//[Required]
//[Display(Name = "")]
public int DepartmentId { get; set; }
public IList<Course> Courses { get; set; }
public IList<MajorPreRequisite> CoursesAndPreReqs { get; set; }
[Display(Name = "Department: ")]
public IList<Department> Departments { get; set; }
public CoursePreReqViewModel() { }
public Dictionary<string, string> DepartmentList { get; set; }
public void GetAllCoursesAndPreReqsByDepartment(IList<Course> populateCourses)
{
Courses = populateCourses;
}
}
public class HomeController : Controller
{
[HttpPost]
public ActionResult Catalog(CoursePreReqViewModel viewModel)
{
//Put a breakpoint herre to see departmentid of user choice
return View();
}
public ActionResult Index11()
{
CoursePreReqViewModel viewModel = new CoursePreReqViewModel();
Dictionary<string, string> depts = new Dictionary<string, string>();
depts.Add("1", "deptOne");
depts.Add("2", "deptTwo");
viewModel.DepartmentList = depts;
IList<Course> courses = new List<Course>();
courses.Add(new Course { CourseId = 1 });
courses.Add(new Course { CourseId = 2 });
viewModel.GetAllCoursesAndPreReqsByDepartment(courses);
return View(viewModel);
}
view:
#model WebApplication4what2.Controllers.CoursePreReqViewModel
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index11</title>
</head>
<body>
#using (Html.BeginForm("Catalog", "Home", FormMethod.Post, new { #class = "pure-form pure-form-aligned" }))
{
<div class="row">
<div class="col">
<input id="myInput" class="form-control" type="text" placeholder="Search..">
</div>
<div class="col">
#Html.LabelFor(model => model.DepartmentId, htmlAttributes: new { #class = "control-label col-md-2" })
#Html.DropDownListFor(model => model.DepartmentId, new SelectList(Model.DepartmentList, "Key", "Value"), Model.DepartmentId)
#Html.ValidationMessageFor(model => model.DepartmentId)
</div>
<div class="col">
<button type="submit" class="btn btn-success">Submit</button>
</div>
</div>
}
</body>
</html>

How to implement custom LogIn with ASP.NET Core 3.1?

I have a testing database on my local server. In the database, I have a table called "Korisnici" (eng. Users). Using EntityFrameworkCore I generated classes from a database, and here is generated "Korisnici" class:
public partial class Korisnici
{
public Korisnici()
{
BankovniRacuni = new HashSet<BankovniRacuni>();
Dokumenti = new HashSet<Dokumenti>();
ObracuniZarada = new HashSet<ObracuniZarada>();
Poslodavci = new HashSet<Poslodavci>();
PrihodiPoslodavca = new HashSet<PrihodiPoslodavca>();
RashodiPoslodavca = new HashSet<RashodiPoslodavca>();
Takse = new HashSet<Takse>();
Zaposleni = new HashSet<Zaposleni>();
}
public int Id { get; set; }
public string Ime { get; set; }
public string Prezime { get; set; }
[Required]
[Display(Name = "Korisnicko ime")]
public string KorisnickoIme { get; set; }
public string Email { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Lozinka")]
public string Lozinka { get; set; }
public int? TipKorisnika { get; set; }
public virtual TipoviKorisnika TipKorisnikaNavigation { get; set; }
public virtual ICollection<BankovniRacuni> BankovniRacuni { get; set; }
public virtual ICollection<Dokumenti> Dokumenti { get; set; }
public virtual ICollection<ObracuniZarada> ObracuniZarada { get; set; }
public virtual ICollection<Poslodavci> Poslodavci { get; set; }
public virtual ICollection<PrihodiPoslodavca> PrihodiPoslodavca { get; set; }
public virtual ICollection<RashodiPoslodavca> RashodiPoslodavca { get; set; }
public virtual ICollection<Takse> Takse { get; set; }
public virtual ICollection<Zaposleni> Zaposleni { get; set; }
}
This class is used as a model for one View called "Index.cshtml":
#model Korisnici
<img src="/Content/images/LogoFinal.png" />
<div class="row">
<div class="col-md-8">
<section id="loginForm">
#using (Html.BeginForm("Login", "Account"))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.KorisnickoIme, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.KorisnickoIme, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.KorisnickoIme, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Lozinka, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.PasswordFor(m => m.Lozinka, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Lozinka, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Uloguj se" class="btn btn-primary" />
</div>
</div>
}
</section>
</div>
</div>
When I click on a submit button, Login action from Controler "AccountControler" is called.
public class AccountController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult Login(LoginViewModel model)
{
// Acces database and search for account
var dbContext = new AdministracijaZingDevDBContext();
var korisnik = dbContext.Korisnici
.Where(k => k.KorisnickoIme == model.KorisnickoIme)
.Where(k => k.Lozinka == model.Lozinka)
.FirstOrDefault();
if (korisnik != null)
{
HttpContext.Session.SetString("UserName" , model.KorisnickoIme);
return RedirectToAction("Index", "Main");
}
return RedirectToAction("Index", "Home");
}
}
I have inserted testing data in a database with one record of the Korisnici table.
When I enter correct data into the LogIn form, nothing happens (the user didn't pass login).
just to check, you created migration files and updated the db and so on ?
(This should be in a comment, but I lack the reputation)
also you shouldn't create a new context, but inject it into the constructor of your account controller
( or beter still ... you should inject a repository or better use a unit of work-design and CQRS-design)
Take care and good luck.
so you type ctor tab tab
which would give you
public AccountController(){}
and than you add a parameter in the AccountController-function like
public AccountController(MyContext context){}
right click => quick actions and refactoring => create and assign property MyContext
but you should create at least a repositorypattern and inject something like IKorisniciRepository.
It would be easier if you placed your repo on gitHub so I can test before writing.
Try
public class AccountController : Controller
{
private readonly AdministracijaZingDevDBContext Context {get;}
public AccountController (AdministracijaZingDevDBContext context) {Context = context;}
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult Login(LoginViewModel model)
{
// Access database and search for account
var korisnik = Context.Korisnici
.Where(k => k.KorisnickoIme == model.KorisnickoIme)
.Where(k => k.Lozinka == model.Lozinka)
.FirstOrDefault();
if (korisnik != null)
{
HttpContext.Session.SetString("UserName" , model.KorisnickoIme);
return RedirectToAction("Index", "Main");
}
return RedirectToAction("Index", "Home");
}
}

Dictionary<TEntity, string> and asp.net mvc how to?

I have:
public class Nomenclature
{
public virtual int NomenclatureId { get; set; }
public virtual NomenclatureType NomenclatureType { get; set; }
public virtual IDictionary<NomenclatureAttribute, string> Attributes { get; set; }
}
public class NomenclatureType
{
public virtual int NomenclatureTypeId { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<Nomenclature> Nomenclatures { get; set; }
public virtual ICollection<NomenclatureAttribute> NomenclatureAttributes { get; set; }
public NomenclatureType()
{
Nomenclatures = new HashSet<Nomenclature>();
NomenclatureAttributes = new HashSet<NomenclatureAttribute>();
}
}
public class NomenclatureAttribute
{
public virtual int NomenclatureAttributeId { get; set; }
public virtual string AttributeName { get; set; }
public virtual string AttributeType { get; set; }
public virtual NomenclatureType NomenclatureType { get; set; }
}
it's all represents a nomenclature of goods in my application. I'am tryin create new Nomenclature in my app. I use NHibernate. I create controller and create action:
[HttpGet]
public ActionResult Create(string nomenclatureType)
{
if (nomenclatureType == null)
return RedirectToAction("List", "Nomenclature");
ViewData["NomenclatureAttributes"] =
_repositoryNomenclatureType.Get(w => w.Name == nomenclatureType).NomenclatureAttributes.ToList();
return View();
}
[HttpPost]
public IActionResult Create(Nomenclature nomenclature)
{
try
{
if (ModelState.IsValid)
{
_repositoryNomenclature.Create(nomenclature);
return RedirectToAction("List", "Nomenclature");
}
}
catch (Exception)
{
ModelState.AddModelError("", "Unable to save changes. " +
"Try again, and if the problem persists see your system administrator.");
}
return View(nomenclature);
}
I need to foreach all NomenclatureAttrributes specified for any Nomenclature Type and create editors for all values and add all to Model.Attributes.
#model Nomenclature
#{
ViewBag.Title = "New nomenclature";
Layout = "_Layout";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true)
#foreach (var a in (List<NomenclatureAttribute>)ViewData["NomenclatureAttributes"])
{
<div class="form-group">
<label class="control-label col-md-2">#a.AttributeName</label>
<div class="col-md-10">
**What i should place to this???**
</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>
}
I use Asp.net core web application (.NET Framework)
First Create ViewModel.
public class CreateNomenclatureViewModel
{
//Add other properties if needed
public NomenclatureType SelectedNomenclatureType { get; set; }
public List<NomenclatureAttribute> Attributes { get; set;}
}
Second
[HttpGet]
public ActionResult Create(string nomenclatureType)
{
if (nomenclatureType == null)
return RedirectToAction("List", "Nomenclature");
var viewModel= new CreateMomenClatureViewModel
{
Attributes = _repositoryNomenclatureType.Get(w => w.Name == nomenclatureType).NomenclatureAttributes.ToList()
}
return View(viewModel);
}
Than fix your view
#model CreateNomenclatureViewModel
#{
ViewBag.Title = "New nomenclature";
Layout = "_Layout";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true)
#if (Model != null && Model.Attributes != null)
{
for (int i = 0; i < Model.Attributes.Count; i++)
{
<div class="form-group">
#Html.DisplayFor(modelItem => Model.Attributes [i].AttributeName)
#Html.TextBoxFor(modelItem => Model.Attributes [i].AttributeType )
</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>
}
if you want to use Nomenclature as ViewModel you can create new Nomenclature on Get Method than pass to view in razor view.
<div class="form-group">
#Html.DisplayFor(modelItem => Model.Attributes.Keys.ElementAt(i).AttributeName)
#Html.TextBoxFor(modelItem => Model.Attributes.Keys.ElementAt(i).AttributeType )
</div>

Pass List data along with ViewModel to view and populate list data in drop down

I have ModelView which I have created for purpose to create new record instance via view-- razor form. In controller I need to assign list data to ViewModel; IEnumerable and then pass this model along with with list data (of CategoryType) which then in view I need to populate in drop-down list, followed by ID of selected CategoryType send back to controller along with other data.
I have assign IEnumerable value to model in controller but not sure is correct and how to do rest part in view ???
View Model - CompanyProfileModelView
public class CompanyProfileModelView
{
public Company _Company { get; set; }
public IEnumerable<CategoryType> _CategoryType { get; set; }
}
Model Class
public class CategoryType
{
public int CategoryTypeID { get; set; }
public string CategoryTitle { get; set; }
public ICollection<Company> Companies { get; set; }
}
Controller Class
[HttpGet]
public ActionResult CreateCompany()
{
var _listData = _appFunctions.GetAllCategory();
var _model = new CompanyProfileModelView
{
_CategoryType = _listData
??????????????
};
return PartialView("_CreateNewCompanyPartial", _model);
}
[HttpPost]
public ActionResult CreateCompany(CompanyProfileModelView _model)
{
try
{
if (ModelState.IsValid)
{
//my code will be here to read for input data
}
}
catch (DataException ex)
{
ModelState.AddModelError("", "Unable To Create New Function Navigation" + ex);
}
return RedirectToAction("Home");
}
View
#model App.DAL.Model.CompanyProfileModelView
#using (Html.BeginForm("CreateCompany", "CompanyProfile", FormMethod.Post, new { id = "NewFunctionNavigationForm" }))
{
<div class="form-group">
#Html.LabelFor(#model => #model._Company.CompanyName, new { #class = "control-label col-md-2" })
<div class="form-group">
#Html.EditorFor(#model =>#model._Company.CompanyName)
#Html.ValidationMessageFor(#model=>#model._Company.CompanyName)
</div>
</div>
<div class="form-group">
// need help here for dropdown of CategoryType list
</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>
}
Since DropDownList uses IEnumerable<SelectListItem>, change the view model to
public class CompanyProfileModelView
{
public Company Company { get; set; }
public SelectList CategoryList { get; set; }
}
and assuming Company model contains
[Display(Name="Category")]
public int? CategoryType { get; set; }
Controller
[HttpGet]
public ActionResult CreateCompany()
{
var listData = _appFunctions.GetAllCategory();
var model = new CompanyProfileModelView
{
CategoryList = new SelectList(listData, "CategoryTypeID ", "CategoryTitle")
};
return View(model);
}
[HttpPost]
public ActionResult CreateCompany(CompanyProfileModelView model)
{
if (!ModelState.IsValid)
{
// Re-assign select list if returning the view
var listData = _appFunctions.GetAllCategory();
model.CategoryList = new SelectList(listData, "CategoryTypeID ", "CategoryTitle");
return View(model)
}
// Save and redirect
}
View
#model App.DAL.Model.CompanyProfileModelView
#using (Html.BeginForm()) // Note, no parameters required in this case
{
....
#Html.LabelFor(m => m.Company.CategoryType, new { #class = "control-label col-md-2" })
#Html.DropDownListFor(m => m.Company.CategoryType, Model.CategoryList, "--Please select--")
#Html.ValidationMessageFor(m => m.Company.CategoryType)
.....
<input type="submit" value="Create" class="btn btn-default" />
}

Model (complex type) won't submit to action

I have some problem posting a form with 'complex type' model:
I have a Model:
public class CircleEditViewModel
{
[Key]
public int CircleId { get; set; }
[Required]
[MaxLength(100)]
public string Name { get; set; }
public bool IsSystem { get; set; }
public class UserInCircle
{
public UserInCircle(User user)
{
this.UserId = user.UserId;
FullName = user.FullName;
}
public int UserId { get; set; }
public byte[] Picture { get; set; }
public string FullName { get; set; }
public bool isInCircle { get; set; }
}
public List<UserInCircle> Users { get; set; }
}
My first problem was that at post event, my Users where null.. so i followed a few posts on here (like MVC- Model Binding on a Complex Type) to use a for instead of a foreach,but since i did so, my form won't post anymore:
View:
#model Wims.Website.ViewModels.CircleEditViewModel
<script type="text/javascript">
$(document).ready(function () {
$.validator.unobtrusive.parse('form');
});
</script>
#using (Ajax.BeginForm(Html.ViewContext.RouteData.Values["Action"].ToString(), null, new AjaxOptions { HttpMethod = "POST", OnSuccess = "SaveDone(data)" }, new { id = "editform" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Circle</legend>
#Html.Label(DateTime.Now.ToString());
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
</fieldset>
if (Model.Users != null)
{
for (int i = 0; i < Model.Users.Count; i++)
{
<div class="userDetail">
<div>
<div>
#Html.DisplayFor(model => Model.Users[i].isInCircle);
</div>
<div class="iconDiv">
#Html.Image("~/Content/Images/defaultUser.jpg", Model.Users[i].FullName, null);
</div>
<div>
#Html.TextBoxFor(model => Model.Users[i].FullName)
#Html.HiddenFor(model => Model.Users[i].UserId)
</div>
</div>
</div>
<div style="clear: both"></div>
}
}
#Html.GenerateSecureDataControls(model => model.CircleId)
<input type="submit" value="Save" />
}
My view is rendered as a partial loaded thru ajax (not sure it makes any difference here).
Any idea why it won't post? If i remove all the '[]' like 'Users[0].FullName' to Users0.FullName i will post, but of course it won't be bound.
Thanks for your help
Edit just in case needed: Action:
[HttpPost]
public ActionResult Edit(CircleEditViewModel circleData, FormCollection collection)
{
if (ModelState.IsValid)
{
using (var logic = new CircleLogic())
{
Circle circle = logic.GetCircleById(circleData.CircleId, WebMatrix.WebData.WebSecurity.CurrentUserId);
if (circle == null)
{
return HttpNotFound();
}
else
{
circle.Name = circleData.Name;
logic.UpdateCircle(circle, GetSelectedUser(collection));
}
return PartialView("_CircleAndUsers", GetData(logic, circle.CircleId));
}
}
return this.Json(new { success = false, viewdata = RenderRazorViewToString("_CircleAndUsers", circleData) });
}
Pablo Romeo was right, i added a default ctor and it worked.

Categories

Resources