Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds
Arrow up icon
GO TO TOP
Hands-On Design Patterns with C# and .NET Core

You're reading from   Hands-On Design Patterns with C# and .NET Core Write clean and maintainable code by using reusable solutions to common software design problems

Arrow left icon
Product type Paperback
Published in Jul 2019
Publisher Packt
ISBN-13 9781789133646
Length 410 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Authors (2):
Arrow left icon
Jeffrey Chilberto Jeffrey Chilberto
Author Profile Icon Jeffrey Chilberto
Jeffrey Chilberto
Gaurav Aroraa Gaurav Aroraa
Author Profile Icon Gaurav Aroraa
Gaurav Aroraa
Arrow right icon
View More author details
Toc

Table of Contents (19) Chapters Close

Preface 1. Section 1: Essentials of Design Patterns in C# and .NET Core FREE CHAPTER
2. Overview of OOP in .NET Core and C# 3. Modern Software Design Patterns and Principles 4. Section 2: Deep Dive into Utilities and Patterns in .NET Core
5. Implementing Design Patterns - Basics Part 1 6. Implementing Design Patterns - Basics Part 2 7. Implementing Design Patterns - .NET Core 8. Implementing Design Patterns for Web Applications - Part 1 9. Implementing Design Patterns for Web Applications - Part 2 10. Section 3: Functional Programming, Reactive Programming, and Coding for the Cloud
11. Concurrent Programming in .NET Core 12. Functional Programming Practices 13. Reactive Programming Patterns and Techniques 14. Advanced Database Design and Application Techniques 15. Coding for the Cloud 16. Miscellaneous Best Practices 17. Assessments 18. Other Books You May Enjoy

Inventory application use case

In this section, we will continue with our FlixOne inventory application. Throughout this section, we will discuss the web application pattern and extend our web application developed in Chapter 4, Implementing Design Patterns - Basics Part 2.

This chapter continues looking at web applications that were discussed in the previous chapter. If you skipped the previous chapter (Chapter 9, Functional Programming Practices), please revisit it to get up to speed with the current chapter.

In this section, we will go through the process of requirement gathering and then discuss the various challenges of development and business with our web application that we developed previously.

Starting the project

In Chapter 7, Implementing Design Patterns for Web Applications - Part 2, we added features to our FlixOne inventory web application. We extended the application after considering the following points:

  • The business needs a rich UI.
  • New opportunities demand a responsive web application.

Requirements

After several meetings and discussions with management, Business Analyst (BA), and presales folks, the management of the organization decided to work upon the following high-level requirements.

Business requirements

Our business team listed the following requirements:

  • Item filtering: Currently, users are unable to filter items by category. To extend the list-view feature, the user should be able to filter the product item based on its respective category.
  • Item sorting: Currently, items are appearing in the order in which they have been added to the database. There is no mechanism where a user can sort items based on the item's name, price, and so on.
The FlixOne inventory management web application is an imaginary product. We are creating this application to discuss the various design patterns required/used in the web project.

Getting inventory with a filter, paging, and sorting

As per our business requirements, we need to apply a filter, paging, and sorting to our FlixOne inventory application. First, let's start implementing the sorting. To do so, I've created a project and put this project in the FlixOneWebExtended folder. Start Visual Studio and open the FlixOne solution. We will apply to sort to our product listing sheet for these columns: Category, productName, Description, and Price. Please note that we will not be using any external component for sorting, but we will create our own login.

Open the Solution Explorer, and open ProductController, which is available in the Controllers folder. Add the [FromQuery]Sort sort parameter to the Index method. Please note that the [FromQuery] attribute indicates that this parameter is a query parameter. We will use this parameter to maintain our sorting order.

The following code shows the Sort class:

public class Sort
{
public SortOrder Order { get; set; } = SortOrder.A;
public string ColName { get; set; }
public ColumnType ColType { get; set; } = ColumnType.Text;
}

The Sort class contains three public properties as detailed here:

  • Order: Indicates the sorting order. The SortOrder is an enum defined as public enum SortOrder { D, A, N }.
  • ColName: Indicates the column name.
  • ColType: Indicates the type of a column; ColumnType is an enum defined as public enum ColumnType { Text, Date, Number }.

Open the IInventoryRepositry interface, and add the IEnumerable<Product> GetProducts(Sort sort) method. This method is responsible for sorting the results. Please note that we are going to use LINQ queries to apply sorting. Implement this InventoryRepository class method and add the following code:

public IEnumerable<Product> GetProducts(Sort sort)
{
if(sort.ColName == null)
sort.ColName = "";
switch (sort.ColName.ToLower())
{
case "categoryname":
{
var products = sort.Order == SortOrder.A
? ListProducts().OrderBy(x => x.Category.Name)
: ListProducts().OrderByDescending(x => x.Category.Name);
return PDiscounts(products);

}

The following code is handling the case when sort.ColName is productname:


case "productname":
{
var products = sort.Order == SortOrder.A
? ListProducts().OrderBy(x => x.Name)
: ListProducts().OrderByDescending(x => x.Name);
return PDiscounts(products);
}

The following code is handling the case when sort.ColName is productprice:


case "productprice":
{
var products = sort.Order == SortOrder.A
? ListProducts().OrderBy(x => x.Price)
: ListProducts().OrderByDescending(x => x.Price);
return PDiscounts(products);
}
default:
return PDiscounts(ListProducts().OrderBy(x => x.Name));
}
}

In the previous code, we set the value of the sort parameter as blank if it contains a null value, and then we process it by using switch..case in sort.ColName.ToLower().

The following is our ListProducts() method that gives us the result of the IIncludeIQuerable<Product,Category> type:

private IIncludableQueryable<Product, Category> ListProducts() =>
_inventoryContext.Products.Include(c => c.Category);

The preceding code simply gives us Products by including Categories for each product. The sorting order will come from our user, so we need to modify our Index.cshtml page. We also need to add an anchor tag to the header columns of the table. For this, consider the following code:

 <thead>
<tr>
<th>
@Html.ActionLink(Html.DisplayNameFor(model => model.CategoryName), "Index", new Sort { ColName = "CategoryName", ColType = ColumnType.Text, Order = SortOrder.A })
</th>
<th>
@Html.ActionLink(Html.DisplayNameFor(model => model.ProductName), "Index", new Sort { ColName = "ProductName", ColType = ColumnType.Text, Order = SortOrder.A })

</th>
<th>
@Html.ActionLink(Html.DisplayNameFor(model => model.ProductDescription), "Index", new Sort { ColName = "ProductDescription", ColType = ColumnType.Text, Order = SortOrder.A })
</th>
</tr>
</thead>

The preceding code show the header columns of the table; new Sort { ColName = "ProductName", ColType = ColumnType.Text, Order = SortOrder.A } is the main way we are implementing SorOrder.

Run the application and you will see the following snapshot of the Product Listing page with the sorting feature:

Now, open the Index.cshtml page, and add the following code to the page:

@using (Html.BeginForm())
{
<p>
Search by: @Html.TextBox("searchTerm")
<input type="submit" value="Search" class="btn-sm btn-success" />
</p>
}

In the preceding code, we are adding a textbox under Form. Here, the user inputs the data/value, and this data submits to the server as soon as the user clicks the submit button. At the server side, the filtered data will returned back and show the product listing. After the implementation of the preceding code, our Product Listing page will look like this:

Go to the Index method in ProductController and change the parameters. Now the Index method looks like this:

public IActionResult Index([FromQuery]Sort sort, string searchTerm)
{
var products = _repositry.GetProducts(sort, searchTerm);
return View(products.ToProductvm());
}

Similarly, we need to update the method parameters of GetProducts() in InventoryRepository and InventoryRepository. The following is the code for the InventoryRepository class:

private IEnumerable<Product> ListProducts(string searchTerm = "")
{
var includableQueryable = _inventoryContext.Products.Include(c => c.Category).ToList();
if (!string.IsNullOrEmpty(searchTerm))
{
includableQueryable = includableQueryable.Where(x =>
x.Name.Contains(searchTerm) || x.Description.Contains(searchTerm) ||
x.Category.Name.Contains(searchTerm)).ToList();
}

return includableQueryable;
}

Now run the project by pressing F5 from Visual Studio and navigating to the filter/search option in Product Listing. For this, see this snapshot:

After entering your search term, click on the Search button, and this will give you the results, as shown in the following snapshot:

In the preceding Product Listing screenshot, we are filtering our Product records with searchTerm mango, and it produces single results, as shown in the previous snapshot. There is one issue in this approach for searching data: add fruit as a search term, and see what will happen. It will produce zero results. This is demonstrated in the following snapshot:

We do not get any result, which means our search is not working when we are putting searchTerm in lowercase. This means our search is case-sensitive. We need to change our code to get it started.

Here is our modified code:

var includableQueryable = _inventoryContext.Products.Include(c => c.Category).ToList();
if (!string.IsNullOrEmpty(searchTerm))
{
includableQueryable = includableQueryable.Where(x =>
x.Name.Contains(searchTerm, StringComparison.InvariantCultureIgnoreCase) ||
x.Description.Contains(searchTerm, StringComparison.InvariantCultureIgnoreCase) ||
x.Category.Name.Contains(searchTerm, StringComparison.InvariantCultureIgnoreCase)).ToList();
}

We are ignoring the case to make our search case-insensitive. We used StringComparison.InvariantCultureIgnoreCase and ignored the case. Now our search will work with either capital or lowercase letters. The following is the snapshot that produces results using lowercase fruit:

In a previous discussion during the FlixOne app extension, we applied Sort and Filter; now we need to add paging. To do so, we have added a new class named, PagedList as follows:

public class PagedList<T> : List<T>
{
public PagedList(List<T> list, int totalRecords, int currentPage, int recordPerPage)
{
CurrentPage = currentPage;
TotalPages = (int) Math.Ceiling(totalRecords / (double) recordPerPage);

AddRange(list);
}
}

Now, change the parameters of the Index method of ProductController as follows:

public IActionResult Index([FromQuery] Sort sort, string searchTerm, 
string currentSearchTerm,
int? pagenumber,
int? pagesize)

Add the following code to the Index.cshtml page:

@{
var prevDisabled = !Model.HasPreviousPage ? "disabled" : "";
var nextDisabled = !Model.HasNextPage ? "disabled" : "";
}

<a asp-action="Index"
asp-route-sortOrder="@ViewData["CurrentSort"]"
asp-route-pageNumber="@(Model.CurrentPage - 1)"
asp-route-currentFilter="@ViewData["currentSearchTerm"]"
class="btn btn-sm btn-success @prevDisabled">
Previous
</a>
<a asp-action="Index"
asp-route-sortOrder="@ViewData["CurrentSort"]"
asp-route-pageNumber="@(Model.CurrentPage + 1)"
asp-route-currentFilter="@ViewData["currentSearchTerm"]"
class="btn btn-sm btn-success @nextDisabled">
Next
</a>

The preceding code makes it possible to move our screen to the next or the previous page. Our final screen will look like this:

In this section, we have discussed and extended the features of our FlixOne application by implementing Sorting, Paging, and Filter. The aim of this section was to give you hands-on experience with a working application. We have coded our application in such a way that it will directly meet real-world applications. With the preceding enhancement, our application is now capable of giving a product listing that can be sorted, paginated, and filtered.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €18.99/month. Cancel anytime