Creating multi-column Dropdown in ASP.NET Core

Generally, in Asp.net core, to display multiple columns in the select elements, we could write our own control or use some JQuery plugins.

Here are some examples;

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option

https://dzone.com/articles/creating-a-multi-column-dropdown-in-aspnet-mvc

https://learn.microsoft.com/en-us/answers/questions/1251310/multi-column-drop-down-list-javascript-control-opt

Software Design Principles

Design principles form the foundations that design patterns are built upon. They are more fundamental than design patterns. When you follow proven design principles, your code base becomes infinitely more flexible and adaptable to change, as well as more maintainable.

Common Design Principles

There are a number of common design principles ;

Keep It Simple Stupid (KISS)
An all-too-common issue in software programming is the need to overcomplicate a solution. The goal of the KISS principle is concerned with the need to keep code simple but not simplistic, thus avoiding any unnecessary complexities.

Don’t Repeat Yourself (DRY)
The DRY principle aims to avoiding repetition of any part of a system by abstracting out things that are common and placing those things in a single location. This principle is not only concerned with code but any logic that is duplicated in a system; ultimately there should only be one representation for every piece of knowledge in a system.

Tell, Don’t Ask
The Tell, Don’t Ask principle is closely aligned with encapsulation and the assigning of responsibilities to their correct classes. The principle states that you should to tell objects what actions you want them to perform rather than asking questions about the state of the object and then making a decision yourself on what action you want to perform. This helps to align the responsibilities and avoid tight coupling between classes.

You Ain’t Gonna Need It (YAGNI)
The YAGNI principle refers to the need to only include functionality that is necessary for the application and put off any temptation to add other features that you may think you need. A design methodology that adheres to YAGNI is test-driven development (TDD). TDD is all about writing tests that prove the functionality of a system and then writing only the code to get the test to pass.

Separation of Concerns (SoC)
SoC is the process of dissecting a piece of software into distinct features that encapsulate unique behavior and data that can be used by other classes. Generally, a concern represents a feature or behavior of a class. The act of separating a program into discrete responsibilities significantly increases code reuse, maintenance, and testability.

The S.O.L.I.D. Design Principles

The S.O.L.I.D. design principles are a collection of best practices for object-oriented design. Allof the Gang of Four design patterns adhere to these principles in one form or another. The term
S.O.L.I.D. comes from the initial letter of each of the five principles;

Single Responsibility Principle (SR P)
The principle of SRP is closely aligned with SoC. It states that every object should only have one
reason to change and a single focus of responsibility. By adhering to this principle, you avoid the problem of monolithic class design that is the software equivalent of a Swiss army knife. By having concise objects, you again increase the readability and maintenance of a system.

Open-Closed Principle (OCP)
The OCP states that classes should be open for extension and closed for modification, in that you should be able to add new features and extend a class without changing its internal behavior. The principle strives to avoid breaking the existing class and other classes that depend on it, which would create a ripple effect of bugs and errors throughout your application.

Liskov Substitution Principle (LS P)
The LSP dictates that you should be able to use any derived class in place of a parent class and have it behave in the same manner without modification. This principle is in line with OCP in that it ensures that a derived class does not affect the behavior of a parent class, or, put another way, derived classes must be substitutable for their base classes.

Interface Segregation Principle (IS P)
The ISP is all about splitting the methods of a contract into groups of responsibility and assigning interfaces to these groups to prevent a client from needing to implement one large interface and a host of methods that they do not use. The purpose behind this is so that clases wanting to use the same interfaces only need to implement a specific set of methods as opposed to a monolithic interface of methods.

Dependency Inversion Principle (DIP)
The DIP is all about isolating your classes from concrete implementations and having them depend on abstract classes or interfaces. It promotes the mantra of coding to an interface rather than an implementation, which increases flexibility within a system by ensuring you are not tightly coupled to one implementation.

Dependency Injection (DI) and Inversion of Control (IoC)
Closely linked to the DIP are the DI principle and the IOC principle. DI is the act of supplying a low level or dependent class via a constructor, method, or property. Used in conjunction with DI, these dependent classes can be inverted to interfaces or abstract classes that will lead to loosely coupled systems that are highly testable and easy to change.

In IoC, a system’s flow of control is inverted compared to procedural programming. An example of this is an IoC container, whose purpose is to inject services into client code without having the client code specifying the concrete implementation. The control in this instance that is being inverted is the act of the client obtaining the service.

LINQ Operators

LINQ to Objects extends any type that inherits from IEnumerable (which is almost every collection class in .NET, from simple Arrays to List<T>) to support query operations similar to those available in SQL. We can write queries using any of the built-in Standard Query Operators, or add our own operators if we need to. The standard operators cover a wide variety of categories, at present there are over fifty that form the backbone of LINQ. To get an idea of their scope, here is a list of those operators available to us –

Operator TypeOperator Name
AggregationAggregate, Average, Count, LongCount, Max, Min, Sum
ConversionCast, OfType, ToArray, ToDictionary, ToList, ToLookup, ToSequence
ElementDefaultIfEmpty, ElementAt, ElementAtOrDefault, First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault
EqualityEqualAll
GenerationEmpty, Range, Repeat
GroupingGroupBy
JoiningGroupJoin, Join
OrderingOrderBy, ThenBy, OrderByDescending, ThenByDescending, Reverse
PartitioningSkip, SkipWhile, Take, TakeWhile
QuantifiersAll, Any, Contains
RestrictionWhere
SelectionSelect, SelectMany
SetConcat, Distinct, Except, Intersect, Union


Most of the operators should be familiar if you have ever worked with a relational database writing queries in SQL.

One important distinction between writing SQL queries and LINQ queries is that the operator order is reversed. If you are used to Select-From-Where-OrderBy, it might take some time to overcome the muscle memory and move to From-Where-OrderBy-Select.

Design Pattern Groups

The numbers of design patterns are less than English alphabets. Twenty-three design patterns are featured in the GoF design patterns book, falling within one of three subgroups: Creational, Structural, or Behavioral.

Creational

Creational patterns deal with object construction and referencing. They abstract away the responsibility of instantiating instances of objects from the client, thus keeping code loosely coupled and the responsibility of creating complex objects in one place adhering to the Single Responsibility and Separation of Concerns principles.

Following are the patterns in the Creational group:

➤➤ Abstract Factory: Provides an interface to create families of related objects.

➤➤ Factory: Enables a class to delegate the responsibility of creating a valid object.

➤➤ Builder: Enables various versions of an object to be constructed by separating the construction for the object itself.

➤➤ Prototype: Allows classes to be copied or cloned from a prototype instance rather than creating new instances.

➤➤ Singleton: Enables a class to be instantiated once with a single global point of access to it.

Structural

Structural patterns deal with the composition and relationships of objects to fulfill the needs of Larger systems.

Following are the patterns in the Structural group:

➤➤ Adapter: Enables classes of incompatible interfaces to be used together.

➤➤ Bridge: Separates an abstraction from its implementation, allowing implementations andabstractions to vary independently of one another.

➤➤ Composite: Allows a group of objects representing hierarchies to be treated in the same way as a single instance of an object.

➤➤ Decorator: Can dynamically surround a class and extend its behavior.

➤➤ Facade: Provides a simple interface and controls access to a series of complicated interfaces and subsystems.

➤➤ Flyweight: Provides a way to share data among many small classes in an efficient manner.

➤➤ Proxy: Provides a placeholder to a more complex class that is costly to instantiate.

Behavioral

Behavioral patterns deal with the communication between objects in terms of responsibility and algorithms. The patterns in this group encapsulate complex behavior and abstract it away from the flow of control of a system, thus enabling complex systems to be easily understood and maintained.

Following are the patterns in the Behavioral group:

➤➤ Chain of Responsibility: Allows commands to be chained together dynamically to handle arequest.

➤➤ Command: Encapsulates a method as an object and separates the execution of a command from its invoker.

➤➤ Interpreter: Specifies how to evaluate sentences in a language.

➤➤ Iterator: Provides a way to navigate a collection in a formalized manner.

➤➤ Mediator: Defines an object that allows communication between two other objects without them knowing about one another.

➤➤ Memento: Allows you to restore an object to its previous state.

➤➤ Observer: Defines the way one or more classes can be alerted to a change in another class.

➤➤ State: Allows an object to alter its behavior by delegating to a separate and changeable state object.

➤➤ Strategy: Enables an algorithm to be encapsulated within a class and switched at run time to alter an object’s behavior.

➤➤ Template Method: Defines the control of flow of an algorithm but allows subclasses to override or implement execution steps.

➤➤ Visitor: Enables new functionality to be performed on a class without affecting its structure.

Guidelines for Choosing and Applying Design Pattern

There are so many design patterns to choose from, so how do you identify which one is appropriate for your problem? To know which design pattern to use and how to apply the solution template to your specific problem, it’s important to understand these guidelines.

➤➤ Patterns can’t be applied without knowing about them. The first important step is to expand your knowledge and study patterns and principles both in the abstract and concrete form. You can implement a pattern in many ways. The more you see different implementations of patterns, the more you will understand the intent of the pattern and how a single pattern can have varying implementations.

➤➤ Do you need to introduce the complexity of a design pattern? It’s common for developers to try to use a pattern to solve every problem. You always need to weigh the upfront time needed to implement a pattern for the benefit that it’s going to give. Remember the KISS principle: Keep It Simple, Stupid.

➤➤ Generalize your problem; identify the issues you’re facing in a more abstract manner. Remember that design patterns are high-level solutions; try to abstract your problem, and don’t focus too hard on the details of your specific issue. Try to keep a list of intent of each pattern and principle in your implementation for reference.

➤➤ Look at patterns of a similar nature and patterns in the same group. Just because you have used a pattern before doesn’t mean it will always be the correct pattern choice when solving a problem.

➤➤ Look at what will likely change with your application and encapsulate what varies. If you know that a special offer discount algorithm will change over time, look for a pattern that will help you change it without impacting the rest of your application.

➤➤ After you have chosen a design pattern, ensure that you use the language of your pattern along with the language of the domain when naming the participants in a solution. For example, if you are using the strategy pattern to provide a solution for costing various shipping couriers, name them accordingly, such as USPSCostStrategy. By using the pattern’s common vocabulary along with the language of your domain, you will immediately make your code more readable and understandable to other developers with patterns knowledge.
When it comes to design patterns, there is no substitute for studying.  A great learning exercise is to try to identify patterns in the .NET Framework. For example, the ASP.NET Cache uses the Singleton pattern; creating a new Guid uses the Factory pattern; the .NET 2 XML classes use the Factory pattern.