In this session of ASP.NET Core MVC Tutorial, we are going to distinguish the differences between AddSingleton, AddTransient, and AddScoped in practice. Also, we will implement Form Validation for the DropDownList element. We already mentioned the differences between these methods in theory during Session 16. But now, we are going to show you the distinction between them in practice. Moreover, we will present the way that you can implement validation for the DropDownLists.
Differences between AddSingleton, AddTransient & AddScoped
As we already mentioned, the main difference between these three methods is their lifetime of them. AddSingleton method creates an instance of the service which is available in the whole life of the Web App and is the same in all the requests. Then, the AddTransient method creates an instance of the service each time they are requested. Thus, the instance is always new in the different requests. Finally, the AddScoped method creates an instance of the service per each request. The instance is valid and the same for whole the request but it is different in another request.
Hence, based on the above explanations, we implement all the scenarios on our Web App. Therefore, first, we need to create a Static Repository for the Category entity.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public class StaticCategoryRepository : ICategoryRepository { List<Category> _categories = new() { new Category { ID = 1, CategoryName="Bill", Description="Category for recording cost of bills", Active=CategoryActiveOptions.Yes}, new Category { ID = 2, CategoryName = "Rent", Description = "Category for recording montly rental cost", Active = CategoryActiveOptions.Yes }, new Category { ID = 3, CategoryName = "Petrol", Description = "Category for recording cost of petrol (Gas)", Active = CategoryActiveOptions.Yes } }; public IEnumerable<Category> GetAllCategories() { return _categories; } } |
Now, I need to switch to Static Repositories for both Cost and Category entities. As you know we do it through Startup Class and Configure Service.
1 2 3 4 5 6 7 8 9 10 11 12 |
public void ConfigureServices(IServiceCollection services) { services.AddDbContextPool<WebAppDBContext> (options=> options.UseSqlServer(Configuration.GetConnectionString("CostDBConnectionSQLServer"))); services.AddControllersWithViews(); services.AddSingleton<ICostRepository, StaticCostRepository>(); services.AddSingleton<ICategoryRepository, StaticCategoryRepository>(); } |
To find the result in practice, you need to watch this session video.
The best option for Static Repositories is AddSingleton.
The best option for Database Repositories is AddScoped.
DropDownList Validation
We already discussed Form Validation during Session 38. Also, we introduce several Data Annotations in Session 39 and Session 40. Therefore, based on our current knowledge we show you the way that you can implement Data Validation for the DropDownList element.
For this purpose, first, we need to add an option to the DropDownList whose value should be invalid. For instance, if the Controller expects to receive a positive Integer from that property, we can set a negative integer for that option or even a null value. Then, we can set proper Data annotation and message on the respective property.
Controller Required Changes for DropDownList Validation
Based on the above instruction, we try to implement these changes on the project that we implement during this tutorial series. Hence, we need to add a Non-Action Method to the CostController which is to prepare a List of SelectListItem for the DropDownList. Worth mentioning that, we need to add this Non-Action method to obey the Don’t Repeat Yourself (DRY) principle because we need to use this piece of code in different places.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
private void LoadDropDownList() { var Categories = categoryRepository.GetAllCategories(); List<SelectListItem> CategoryList = new(); CategoryList.Add(new SelectListItem("Select a Category", "-1")); foreach (var category in Categories) { CategoryList.Add(new SelectListItem(category.CategoryName, category.ID.ToString())); } List<SelectListItem> PaymentMethodsList = new(); PaymentMethodsList.Add(new SelectListItem("Select a Payment Method", "")); var PaymentMethods = Enum.GetValues(typeof(PaymentMethods)).Cast<PaymentMethods>().ToList(); for (var i = 0; i < PaymentMethods.Count; i++) { PaymentMethodsList.Add(new SelectListItem(PaymentMethods[i].ToString(), i.ToString())); } ViewBag.Categories = CategoryList; ViewBag.PaymentMethods = PaymentMethodsList; } |
As you can see, within the above code the data are caught from the repository and Enum class. Then the received data are stored in the object of List<SelectListItem>. Also, the invalid option has been added to both lists with null and -1 values. Later, we pass the lists to the View with ViewBag dynamic object. Now, we can use this method in Create & Edit Action Methods (HttpHet/HttpPost).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
[HttpGet] public IActionResult Create() { LoadDropDownList(); return View(); } [HttpPost] public IActionResult Create(CreateCostViewModel model) { if (ModelState.IsValid) { Cost NewCost = new() { Amount = model.Amount, CategoryID = model.CategoryID, Comment = model.Comment, RegisteredDate = model.RegisteredDate, PaymentMethod = model.PaymentMethod }; costRepository.Create(NewCost); return RedirectToAction("Index"); } LoadDropDownList(); return View(model); } [HttpGet] public IActionResult Edit(int id) { var cost = costRepository.GetCostByID(id); LoadDropDownList(); return View(cost); } [HttpPost] public IActionResult Edit(Cost editedCost) { if (ModelState.IsValid) { costRepository.Update(editedCost); return RedirectToAction("Index"); } LoadDropDownList(); return View(editedCost); } |
View And Model Required Changes for DropDownList Validation
Also, We need to add asp-validation-for Tag Helper for these elements in Create and Edit Views. Also, both elements should load their items from the received ViewBags.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<div> <label asp-for="CategoryID" class="form-label"></label> <select asp-for=CategoryID asp-items="@ViewBag.Categories" class="form-select"></select> <span asp-validation-for="CategoryID" class="text-danger"></span> </div> <div> <label asp-for="PaymentMethod" class="form-label"></label> <select asp-for="PaymentMethod" class="form-select" asp-items="@ViewBag.PaymentMethods"></select> <span asp-validation-for="PaymentMethod" class="text-danger"></span> </div> |
Insomuch, we set a -1 value for the default option of CategoryID and null value for PaymentMethod, and both expect to receive a positive integer. We can use RegularsExpression and Required Data Annotation for these properties in the respective ViewModel and View.
CreateCostViewModel Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class CreateCostViewModel { [Column(TypeName = "decimal(18, 2)")] [Required] [DataType(DataType.Currency)] public decimal Amount { get; set; } [Required] [DataType(DataType.Date)] [Display(Name ="Registered Data")] public DateTime RegisteredDate { get; set; } [MaxLength(150)] public string Comment { get; set; } [RegularExpression("^\\d+$",ErrorMessage ="Please select a category")] [Display(Name ="Category")] public int CategoryID { get; set; } [Display(Name ="Payment Method")] [Required(ErrorMessage = "Please select a Payment Method")] public PaymentMethods? PaymentMethod { set; get; } } |
Cost Model Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public class Cost { [Key] public int ID { get; set; } [Column(TypeName = "decimal(18, 2)")] [Required] public decimal Amount { get; set; } [Required] [DataType(DataType.Date)] public DateTime RegisteredDate { get; set; } [MaxLength(150)] public string Comment { get; set; } [Required] [RegularExpression("^\\d+$", ErrorMessage = "Please select a category")] public int CategoryID { get; set; } [ForeignKey(nameof(CategoryID))] public virtual Category Category { get; set; } [Required(ErrorMessage = "Please select a Payment Method")] public PaymentMethods? PaymentMethod { set; get; } public string InvoiceImagePath { get; set; } } |
Just, as you can see I added a question mark “?” to the PaymentMethod properties which allows this property to accept a null value.
If you need more details, watch this session video. Also, for being updated about our coming sessions, follow us on Instagram, Facebook, Telegram, or YouTube. Moreover, you can have access to the list of all sessions HERE and you can download this session source code from our GitHub.
You can download this Session Slides form HERE.