Model Validation using StringLength validation attribute - asp.net-core

In my ASP.NET MVC Core 1.1.1 I've a form with one input as follows where user is required to enter the district code as two characters such as 01, 02,...10,11, etc. But when I submit the form by entering district code as, say, 123, it still successfully submits the form without forcing user to enter the district code as two characters. What I may be missing?
MyViewModels
...
[Display(Name = "District"),StringLength(2)]
public string Dist { get; set; }
...
Form
#model MyProj.Models.MyViewModel
...
<div class="form-group">
<label asp-for="Dist" class="col-md-2 control-label"></label>
<div class="col-md-2">
<input asp-for="Dist" class="form-control" />
<span asp-validation-for="Dist" class="text-danger"></span>
</div>
<div class="col-sm-pull-8">01 to 53 — district codes</div>
</div>
...
NOTE
I am using default ASP.NET Core Web Application template with latest version of VS2017 that by default installs necessary Bootstrap and other javascripts when one uses such template.

1 - The first parameter of StringLength is Maximum length. To define minimum you can do:
[Display(Name = "District")]
[Required]
[StringLength(2, MinimumLength = 2)]
public string Dist { get; set; }
or:
[Display(Name = "District")]
[Required]
[MinLength(2)]
[MaxLength(3)]
public string Dist { get; set; }
The Required attribute is missing!
View
Validation script:
#section Scripts { #Html.Partial("_ValidationScriptsPartial") }
Controller
[HttpPost]
public IActionResult Contact(MyViewModel model)
{
if (ModelState.IsValid)
{ ... }

Related

How to Insert Input into sql table code first ef

I'm trying to Insert my product properties into SQL table, I have a razor page that gets input data that needs to be inserted but i don't know how to insert them
This is my product model:
namespace Market.ViewModels
{
public class ProductListView
{
public Guid ID { get; set; }
public string ProductName { get; set; }
public decimal ProductPrice { get; set; }
}
}
This is my razor page :
I made a simple example, using EF Core code first to create a database and then query the data, the process of binding the value to the page is as follows.
1.The first dependency packages used are as follows:
Model:
public class ProductListView
{
[Key]
public Guid ID { get; set; }
public string ProductName { get; set; }
public double ProductPrice { get; set; }
}
I modified your ProductPrice type, because there will be problems with this type during migration. If you must change the type, refer to this article:
http://jameschambers.com/2019/06/No-Type-Was-Specified-for-the-Decimal-Column/
Create Model and Context Classes:
Now you can add the database context
: name the class TestDbContext and click Add and change the code in TestDbContext.cs as follows:
public class TestDbContext:DbContext
{
public TestDbContext(DbContextOptions<TestDbContext> options) : base(options)
{
}
public DbSet<ProductListView> productListViews { get; set; }
}
Connection string you need to write inside the appsetting.json file as follows:
"ConnectionStrings": {
"DefaultDatabase": "Your DB"
}
In ASP.NET Core, services such as the DB context must be registered with the dependency injection container. The container provides the service to controllers.
Update Startup.cs with the following highlighted code:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddDbContext<TestDbContext>(item => item.UseSqlServer(Configuration.GetConnectionString("DefaultDatabase")));
}
In order to create the migration code, we use the "add-migration MigrationName" command. After the add migration command is successfully executed, it will create a folder named "Migration" in the project. We only created the migration responsible for creating the database and its tables. script. But we have not yet created the actual database and tables. So let's execute the migration script and generate the database and tables. Therefore, to execute the migration script we must execute the'update-database' command.
Next, let us create a context class, define the database connection and register the context. Then perform the query work in the controller, and then return the data.
public class TestController : Controller
{
private readonly TestDbContext _context;
public TestController(TestDbContext context)
{
_context = context;
}
public IActionResult Test(ProductListView product)
{
var value = _context.productListViews.SingleOrDefault(item => item.ProductPrice == 12.1);
product.ProductName = value.ProductName;
product.ProductPrice = value.ProductPrice;
return View(product);
}
}
View:
#model WebApplication132.Models.ProductListView
<h1>AddProducts</h1>
<div class="row">
<div class="col-md-12">
<form method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="ProductName"></label>
<input asp-for="ProductName" class="form-control" />
</div>
<div class="form-group">
<label asp-for="ProductPrice"></label>
<input asp-for="ProductPrice" class="form-control" />
</div>
<button type="Add Product" class="btn-primary">Add Product</button>
</form>
</div>
</div>
Db data:
Result:

Why are my Guids being cleared and time being stripped from my DateTime fields in AspnetCore app?

Net 5.0
I have several fields on a form, two are readonly fields. One is a Guid the other is a DateTime.
When changes are submitted, the Guid is set to 00000000-0000-0000-0000-000000000000 and the time is removed from my DateTime column 2021-04-13 02:36:37.4567940 becomes 2021-04-13 00:00:00.0000000
<div class="form-group">
<label asp-for="Record.MyGuid" class="control-label"></label>
<input asp-for="Record.MyGuid" class="form-control" readonly/>
<span asp-validation-for="Record.MyGuid" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Record.FormInserted" class="control-label"></label>
<input asp-for="Record.FormInserted" class="form-control" readonly/>
<span asp-validation-for="Record.FormInserted" class="text-danger"></span>
</div>
As they appear on the model
[Display(Name="Website ID")]
[Required(ErrorMessage = "There should not be a record without an Id. Please report this.")]
[Editable(false)]
public Guid MyGuid { get; private set; }
[Display(Name="Submission date")]
[DataType(DataType.Date)]
[Required(ErrorMessage = "Submission date is required.")]
public DateTime FormInserted { get; set; }
When the record is retrieved, it has a complete Guid and date. However, when the Post action starts those values are changed even though the form still correctly displays the values.
Here is the Post handler. When I inspect the object Record, the values have been changed when it reaches the if(Record.Id > 0) step.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
// At this point, the Guid is now all Zeros
// and the time has been removed from the DateTimee
if(Record.Id > 0)
{
await _RecordData.Update(Record);
}
else
{
// No Id means new record. Add it.
_RecordData.Add(Record);
}
// Commit the changes to the database
await _RecordData.Commit();
// Message to pass to the details page
TempData["StatusMessage"] = "Record saved!";
// Redirect to Details page
return RedirectToPage("./Edit", new { RecordId = Record.MyGuid });
}
What do I need to do to prevent this from happening and preserve the read-only data?
The issue is caused by your model class.
You can change your model like following.
[Display(Name="Website ID")]
[Required(ErrorMessage = "There should not be a record without an Id. Please report this.")]
public Guid MyGuid { get; set; }
[Display(Name="Submission date")]
[DataType(DataType.DateTime)]
[Required(ErrorMessage = "Submission date is required.")]
public DateTime FormInserted { get; set; }
Delete the line [Editable(false)] and change DataType.Date to DataType.DateTime.
Besides delete the code [Editable(false)],you also need to delete the code private.

How to reset value in a cascading dropdown in .NET Core Razor Pages

I'm new to whole .NET Core thing, I was using ASP.NET 4 (Forms) for many year. I probably just can't think properly or I miss something obvious.
I have 3 classes:
public class Rat {
public int RatId { get; set; }
public string Name { get; set; }
public Color Color { get; set; }
public int ColorId { get; set; }
public ColorAddition ColorAddition {get;set;}
public int? ColorAdditionId { get; set; }
}
public class Color {
public int ColorId { get; set; }
public string Name { get; set; }
public bool Addition { get; set; }
}
public class ColorAddition {
public int ColorAdditionId { get; set; }
public string Name { get; set; }
}
Every rat must have a color and can have color addition. Some Colors have Color Additions (all colors with color additions have the same subset of color additions). If the rat have color with color addition, you need to specify that addition, otherwise it can be null.
I created CRUD for the rat class. The update view looks like this:
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Rat.RatId" />
<div class="form-group">
<label asp-for="Rat.Name" class="control-label"></label>
<input asp-for="Rat.Name" class="form-control" />
<span asp-validation-for="Rat.Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Rat.ColorId" class="control-label"></label>
<select asp-for="Rat.ColorId" class="form-control" asp-items="ViewBag.ColorId"></select>
<span asp-validation-for="Rat.ColorId" class="text-danger"></span>
</div>
<div class="form-group">
<div id="ColorAddition">
<label asp-for="Rat.ColorAdditionId" class="control-label"></label>
<select asp-for="Rat.ColorAdditionId" class="form-control" asp-items="ViewBag.ColorAdditionId">
<option disabled selected>Zvolte</option>
</select>
<span asp-validation-for="Rat.ColorAdditionId" class="text-danger"></span>
</div>
</div>
It consists of two select lists, one for Color, another one for color addition. When the color have color addition, I would like to display Color addition list, otherwise it should be hidden.
So I created this at the end of the view:
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script type="text/javascript">
$('#Rat_ColorId').change(function () {
var selectedColor = $("#Rat_ColorId").val();
$.getJSON(`?handler=HaveColorAddition&id=${selectedColor}`, function (emp) {
console.log(emp);
if (emp == true)
{
$('#ColorAddition').show();
}
else
{
$('#ColorAddition').hide();
$('#Rat_ColorAdditionId').val(null);
}
});
});
</script>
}
It calls Json, which is returning true if the color have color addition and false if not:
public JsonResult OnGetHaveColorAddition(int id)
{
return new JsonResult(_context.Colors.Find(id).Addition);
}
So far so good, this is working as intended. If I choose color without color addition, the color addition select list is hidden and its value is null.
The trouble is that if I update the form now, color addition is not empty, but it keeps the previous value.
Example: I have blue color (with color addition light and dark) and black color (without color addition). Right now the color is blue and addition is light. I want to change color to black, so I choose black. Select list with addition is hidden now and without a value. Once I submit the form, color is changed to black but addition is still light.
Update code looks like this:
public async Task<IActionResult> OnPostAsync(int? id)
{
if (!ModelState.IsValid)
{
return Page();
}
var ratToUpdate = await _context.Rats.FindAsync(id);
if (ratToUpdate == null)
{
return NotFound();
}
if (await TryUpdateModelAsync<Rat>(
ratToUpdate,
"rat",
r => r.Name, r =>r.ColorId, r => r.ColorAdditionId))
{
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
return Page();
}
What am I doing wrong?
When you post data to handler , you just post Rate id to server side , and still query the database to base on id :
var ratToUpdate = await _context.Rats.FindAsync(id);
So that the ColorAdditionId is always the one which stored in database . You should include the Rat.ColorAdditionId when page posting data to OnPostAsync handler , so that server side can update the ColorAdditionId and save to database , and show new value after return RedirectToPage("./Index"); which will query the datbabase to get the newest value .
Please refer to below documents for how to use Forms in Razor Pages :
https://www.learnrazorpages.com/razor-pages/forms
https://learn.microsoft.com/en-us/aspnet/core/razor-pages/?view=aspnetcore-3.1&tabs=visual-studio

ASP NET CORE MVC - Correct way to pass data from view to controller?

I am learning ASP Net Core 2.2 MVC. I have read several articles regarding passing data from controller to view and vice-versa. At one point I wanted to pass more than 1 model to the view.
Then I realized that I cannot, and have to use what is called a View Model. I came up with this:
My Domain Models:
Blog.cs:
A blog can have many categories, all the other properties are the usual title, body etc.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Blogspot.Datas.Models
{
public class Blog
{
[Key]
public int id { get; set; }
[Required]
[DataType(DataType.Text)]
public string title { get; set; }
[Required]
[DataType(DataType.Text)]
public string body { get; set; }
[DataType(DataType.DateTime)]
public DateTime created_at { get; set; }
[Column(TypeName = "boolean")]
public bool comments { get; set; }
public List<Category> categories { get; set; }
}
}
Category.cs:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Blogspot.Datas.Models
{
public class Category
{
[Key]
public int id { get; set; }
[Required]
public string title { get; set; }
public int blog_id { get; set; }
[ForeignKey("blog_id")]
public Blog blog { get; set; }
}
}
In one of my view - Info.cshtml, I want to show a blog with its categories.
InfoViewModel.cs:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Blogspot.Datas.Models;
namespace Blogspot.Datas.Models.Pages
{
public class InfoViewModel
{
public InfoViewModel()
{
this.categories = new List<Category>();
this.category = new Category();
}
public int id { get; set; }
[Required]
public string title { get; set; }
[Required]
public string body { get; set; }
[Required]
public Category category { get; set; }
public List<Category> categories { get; set; }
}
}
Info.cshtml:
It shows the title and body of the blog, an its categories. I can also add a category (in the modal form).
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
#model Blogspot.Datas.Models.Pages.InfoViewModel
<section class="infos">
<form action="#">
<input type="hidden" asp-for="#Model.id">
<div class="form-group">
<label for="title">Title</label>
<input class="form-control" type="text" asp-for="#Model.title">
</div>
<div class="form-group">
<label for="body">Body</label>
<input class="form-control" type="text" asp-for="#Model.body">
</div>
</form>
<div class="categories">
<h3>Categories
<button type="button" style="float: right" class="btn btn-primary add-category">Add category</button>
</h3>
#foreach (var c in #Model.categories)
{
<div class="cat">
<p>#c.title</p>
<form asp-route="deleteCategory" asp-route-id="#c.id">
<button type="submit" class="btn btn-danger">Delete</button>
</form>
<hr>
</div>
}
</div>
</section>
<div class="modal fade category" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form asp-route="storeCategory" method="post" asp-anti-forgery="true">
<div class="form-group">
<label asp-for="#Model.category.title">Title</label>
<input class="form-control" type="text" asp-for="#Model.category.title">
<span class="text-danger" asp-validation-for="#Model.category.title"></span>
</div>
<input type="hidden" asp-for="#Model.category.blog_id" value="#Model.id">
<input type="submit" value="Save category" class="btn btn-success">
</form>
</div>
</div>
</div>
</div>
Now what has got me thinking is what would be the correct way of passing a parameter to the POST store function?
[HttpPost("categories", Name="storeCategory")]
[ExportModelState]
public async Task<IActionResult> storeCategory(Category category)
{
if (ModelState.IsValid)
{
await _context.category.AddAsync(category);
await _context.SaveChangesAsync();
TempData["success"] = true;
TempData["message"] = "Category added succesfully!";
}
return RedirectToRoute("postDetails", new { id = category.blog_id });
}
What I've done is pass in the Category Domain Model. I saw articles which said that it should be View Model that gets passed, because its not a good practice to pass around Domain Models. Now my function works perfectly, but in the instance I pass a View Model, like storeCategory(InfoViewModel infoViewModel) wouldn't the other properties id, title, property, categories be redundant? Because all I need for that function is a category object.
Please enlightened me all of this patterns and conventions used.
Thank you.
This is an opinionated question so here is my opinionated answer.
You should follow these principles and conventions if:
The project you are building is for your own practice. (Practising good practices h3h3)
The project you are making is going to be maintained by someone else. (The project will be easier to maintain in the future)
The project is massive. (The structure will be cleaner, easier to maintain)
You shouldn't worry about this sort of thing if:
This is a 1 time throw away project. (You'll quickly knock it up use it for a bit and throw it away, in this case you value time above anything else)
Now to specifically answer your case of displaying the Domain Model in the View. Why is this a bad thing?
When working with Objects it's important to know their place in the program (Where do I put my code?). Why not just create a single object with 100 fields and just use it in every view/method, because you are going to forget what's the context of methods and what they are meant to be doing and which fields belong where. As well as having an object like DataModel.cs it's a data model we know but what does it represent? what's the context? what is this program about? So now you might name is BlogPost.cs to bring clarity.
Single responsibility is key, an object/class/function is responsible only for 1 thing and 1 thing only.
BlogPost - Blog post DTO.
BlogPostViewModel - The data that we would like to display to the users.
BlogPostInputModel - The data we would like to capture to create this Blog post.
CreateBlogPost(BlogPostInputModel im) - Create BlogPost from BlogPostInputModel
SaveBlogPost(BlogPost bp) - Save BlogPost to the database.
I hope you understand that this extra work is done to produce self documenting code.
If you need to display BlogPostViewModel but capture only BlogPostInputModel it's fine that BlogPostViewModel has some properties that BlogPostInputModel because we need them for the view.
Update, further explanations:
CreateBlogPost(BlogPostInputModel im) - This is a pure function it takes and input A and spits out B, pure means there are no side effects and isn't affected by state. So saying that if a function depended on state like time if we supplied A it might spit out C or F depending on what time it is. This way it's easier to test and it always returns a valid BlogPost.
SaveBlogPost(BlogPost bp) - This is a function with a side effect which is: writing to a database. It just consumes a valid BlogPost and saves it to the database. This sort of function would be in your repository, essentially all your state management is contained in a Repository object.
If we would save the BlogPost to the database inside CreateBlogPost, if we would write a test, it would need to seed and then revert the changes in the database for every test. This is problematic.

Input radio buttons in ASP.NET Core MVC with taghelpers

I am trying to create a form with a radio button with two values : Automatic and Manual.
In order to do so I adapted an answer to a GitHub issue to my code but unfortunately I have an issue in the line foreach of the View where "Model.GearingType" is not recognized and if I change it to "GearingType" it's not recognized either.
Thanks !
ViewModel
public class EvaluationForm
{
public enum GearingType
{
Manual,
Automatic
}
[Required(ErrorMessage = "Please select your car gearing's type")]
[Display(Name = "Gearing Type")]
public GearingType SelectedGearingType { get; set; }
View
<div class="row">
<div class="col-md">
#{
foreach (Model.GearingType gearType in Enum.GetValues(typeof(Model.GearingType))
{
<label>
<input asp-for="SelectedGearingType" type="radio" value="#gearType" />
#gearType
</label>
}
}
</div>
</div>
You need to specify the Type, not the instance of the model in the typeof statement
foreach (var gearType in Enum.GetValues(typeof(EvaluationForm.GearingType)))
{
....
}