European ASP.NET 4.5 Hosting BLOG

BLOG about ASP.NET 4, ASP.NET 4.5 Hosting and Its Technology - Dedicated to European Windows Hosting Customer

European ASP.NET Core Hosting :: What Is Caching And How It Works?

clock July 30, 2021 05:35 by author Peter

A cache is a temporary storage used to store the results of frequently accessed data so that subsequent requests load faster. Most applications use the database as the main storage. Every time you load user data, one or more database calls are executed to get the particular data, which can hamper the performance.

Advantages
    One of the key benefits of having a separate cache is the ability to scale the cache system independently.
    The use of cache reduces latency for active data. This results in higher performance for a system or application.
    It reduces the overhead from server resources.
    It increases reliability

Disadvantage
Caching is not recommended for the system that performs more write operation than the read operation because every time to perform a write operation, you also have to update the cache.
Recommendations

Consistency
Keep the main data source and cache in sync, usually, inconsistency occurs when the write operation on the main data storage and the write/update on cache is not in a single transaction.

Eviction Policy
If you keep adding the items in the cache, there would be a time when you face a memory out-of-bounds exception so it’s recommended to clear the unwanted keys/items on the basis of eviction policies. There are multiple policies available to remove the keys from the cache, the most used is the Least-recently-used (LRU), which allows you to quickly identify which item hasn’t been used for the longest amount of time and removes it. Other eviction policies are Least Frequently Used (LFU), First in First Out (FIFO).

Single Point of Failure(SPOF)
Single point of failure refers to a part of the system which in the case failed will hamper the whole system and can entirely stop the system. To avoid this, deploy multiple cache systems across the different data centers/locations.

Expiration
It’s a good practice to have an expiration policy against every item. If you don’t provide it, the cached data will remain in the memory permanently or until the system restarts. There are different ways you can have an expiration policy. One of them is Absolute Time. In this, we provide the expiry time against every time and it will expire in that given time. Suppose you have set 5 mins absolute expiry time for item1. As soon as the 5 mins are over for item1. The system will remove the item1 from the cache and calls the eviction call back function. The other option is the Sliding Time, it will ensure that if the item is accessed within the specified time interval, the cache time span will be extended by the same interval value.

The above diagram is high level, we have used a load balancer to distribute the load among different web servers. All the write operations are redirected to the Master DB (SQL Server, NoSql, etc) and all the read operations are handled by the cache.



European ASP.NET Core Hosting :: Setting and Configuration Of .NET Core Web Application

clock July 22, 2021 07:16 by author Peter

Today we will learn some basic settings/configurations to be made before we actually start development in the .net core web application (3.0). Let's get started.


Refreshing MVC Views

By default .Net Core web application does not allow changes made in the MVC view to appear directly on the browser when we refresh the page. To make changes appear on the browser we need to perform few steps as below.

Add/Install Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation into your project/application with the help of NuGet Package Manager as below,

Basic settingsconfiguration of .Net Core Web Application

By clicking the above option, the NuGet package manager will appear then click on the Browse option and search for Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation as below,

Once the search is done, please click on this option, and on the right side of the panel select your project and before you click on the install button please choose the appropriate version and then click on the install button.

Note
For .Net Core Version 3.0 please choose 3.1.17 version.

By clicking the install button, it will install/add "Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" into your application/project, you can see the progress in the output pane of the Visual Studio as below. When the install button is clicked it will prompt the library to be installed into your project. Click on OK and then accept License Acceptance.

Basic settingsconfiguration of .Net Core Web Application

Once the above process is done please add the below line into your startup.cs file as below,

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews().AddRazorRuntimeCompilation();
}

As per the below screen.

Please run and check the application to see the real-time changes of views to appear on the browser on refresh.

MVC Views folder when Publish

By default .Net Core web application does not append/add Views folder when publishing the application. To have that folder when publish we need to perform the below steps.

Right-click on root project and click on Edit Project File as below.

Once click that option below screen will have appeared.

Add the below settings to the Property Group node as below,

<MvcRazorCompileOnPublish>false</MvcRazorCompileOnPublish>
<CopyRazorGenerateFilesToPublishDirectory>true</CopyRazorGenerateFilesToPublishDirectory>

Once done it will add the Views folder when publishing the application.



European ASP.NET Core Hosting :: Building Lightweight APIs In .NET 6

clock July 19, 2021 08:37 by author Peter

The next major release is .NET 6. It has support for MAUI, Improved SPA templates, Blazor Enhancements, Lightweight API development, and much more. In this tutorial, we are going to cover how .NET 6 will allow us to develop lightweight APIs.

The good thing about this lightweight API is that no additional template and no extra effort are required. You can start from an empty web project and be up and running without any extra configuration. So, let’s get started with development.

Software and Packages Required

  1. Visual Studio Code
  2. .NET 6 SDK Preview 4

What we are building,

URL VERB MAP OVERLOAD
/ GET MapGet
/ POST MapPost
/id DELETE MapDelete

 

Solution Setup

Check .NET version

D:\LightweightDotnetAPI>dotnet --version

6.0.100-preview.4.21255.9

dotnet new sln #create new solution
dotnet new web -o Api #create API Project
dotnet sln add Api/Api.csproj #Add Project in Solution
dotnet add package Microsoft.AspNetCore.Mvc.NewtonsoftJson --version 5.0.7

The result project structure will look like this,

Code Walkthrough

Now, our Program.cs will be like the below file.

Program.cs

using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using System.IO;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
await using
var app = builder.Build();
if (app.Environment.IsDevelopment()) {
    app.UseDeveloperExceptionPage();
}
app.MapGet("/", (Func < string > )(() => "Hello World!"));
app.MapPost("/", (Func < PostBody, string > )((body) => {
    string text = JsonConvert.SerializeObject(body);
    return text;
}));
app.MapDelete("/{id}", (Func < int, string > )((id) => "Delete Request for " + id));
await app.RunAsync();
public class PostBody {
    public string Data {
        get;
        set;
    }
}

That’s it, we have built an API that can accept GET, POST, and DELETE with very few lines of code. You can see in the above code that all the things have been done in one file and with minimal effort. With less overhead, we get more performance. Microservices can be built using this simple structure. It is very simple and easy to understand. 

We have built a lightweight API in .NET 6 (currently in Preview). We have covered the new Routing APIs that use Map Overloads in .NET 6 starting from Preview 4. We built API using GET, POST, and DELETE verbs in the Program.cs file. The lines of code were minimal. With minimum lines of code, we have better performance. So far we have covered,

  1. Setting up the project in .NET CLI
  2. Creating lightweight API in Program.cs
  3. Advantages

 

 



European ASP.NET Core Hosting :: How to Create A .NET 5 Client To Call An API Protected By Certificates?

clock July 16, 2021 05:43 by author Peter

As we all know, security is particularly important for all applications especially APIs as these expose our business logic to be consumed by various clients over the web. We created the .NET API server and then used Postman to call the .NET API endpoints. We attached the certificate to our request in Postman. In today’s article, we will see how to create a .NET client application which makes the call to the same .NET API server endpoint by using a certificate.

Creating the Client Application
To our existing solution, add a new C# console application project as below,

 

You will see the solution as below,


Add the below code to the “Program.cs” file,
using System;
using System.Net.Http;
using System.Security.Cryptography.X509Certificates;
var certificate = new X509Certificate2(@ "C:\CloudItems\Learning\CSharpCorner\Article 62\clientcert.pfx", "Client123");
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(certificate);
using(var httpClient = new HttpClient(handler)) {
    using(var response = await httpClient.PostAsync("https://localhost:5001/api/Test", null)) {
        var returnValue = await response.Content.ReadAsStringAsync();
        Console.WriteLine(returnValue);
    }
}
Console.ReadLine();

Here, we see that we provide the client certificate and password to the client who then attaches it to the request. The server then validates the certificate and if all looks good, the request is processed. We run the server application and then the client application and make the request as below. Please note that I ran the server app from Visual Studio and the client application from the command line.

In this article, we looked at how to attach the client certificate we generated in the previous article and make an API call to the server using a .NET 5 client. Happy coding!



European ASP.NET Core Hosting :: How to Fix Error While Upgrading Solution From ASP.NET Core 2.1 To ASP.NET Core 3.1?

clock July 14, 2021 07:51 by author Peter

I was upgrading the project framework from ASP.NET Core 2.1 to ASP.NET Core 3.1 and I got the below exception error in my Startup.cs class.

 


Error Details
“System.InvalidOperationException HResult=0x80131509 Message=Endpoint Routing does not support ‘IApplicationBuilder.UseMvc(…)’. To use ‘IApplicationBuilder.UseMvc’ set ‘MvcOptions.EnableEndpointRouting = false’ inside ‘ConfigureServices(…). Source=Microsoft.AspNetCore.Mvc.Core StackTrace: at Microsoft.AspNetCore.Builder.MvcApplicationBuilderExtensions.UseMvc(IApplicationBuilder app, Action`1 configureRoutes) at ProjectName.Startup.Configure(IApplicationBuilder app, IHostingEnvironment env) in ProjectDirectoryPath\Startup.cs:line 89 at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at Microsoft.AspNetCore.Hosting.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder) at Microsoft.AspNetCore.Hosting.ConfigureBuilder.<>c__DisplayClass4_0.<Build>b__0(IApplicationBuilder builder) at Microsoft.AspNetCore.Hosting.ConventionBasedStartup.Configure(IApplicationBuilder app)”

When I continued to run the project, the page loaded with the below error message.


Solution
Inside Configuration() method of Startup.cs file, we need to change the below code.
app.UseMvc(routes => {
    routes.MapRoute(name: "default", template: "{controller=Home}/{action=Index}/{id?}");
});

To
app.UseEndpoints(endpoints => {
    endpoints.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
});


Thing is after migration to ASP.NET Core 3.1, we have to use UseEndPoints() instead of UseMVC().

I hope this helps you resolve your issue after upgrading your project framework from ASP.NET Core 2.1 to ASP.NET Core 3.1.



European ASP.NET Core Hosting :: Asynchronous Calls And Thread-Safe Control Usage In .NET

clock July 12, 2021 08:18 by author Peter


When using asynchronous programming with Windows Forms controls, you need to remember that background threads cannot directly invoke controls that were not created by them. If you want a control to be manipulated from within a background thread, you first need to check if the call to the control is safe or not and then make a call to it. To achieve this, you use a combination of the InvokeRequired property and Invoke() method of the Control class.
Working of InvokeRequired Property

The InvokeRequired property returns false if the current thread is one that created the control and no special invoke is required to use the control. The property returns true if the control is being accessed from a different thread than the one it was created on and has to be invoked using the Invoke() method. The following code demonstrates this,
public delegate void SetColorDelegate()
public void setColor() {
    MessageBox.Show(pnlstatus.InvokeRequired.ToString());
    if (pnlStatus.InvokeRequired) {
        Set ColorDelegate del = new SetColorDelegate(SetColor);
        this.Invoke(del);
    } else {
        pnlStatus.BackColor = Color.Tomato;
    }
}
private void bgWrkrDownload_DoWork(object sender, DoWorkEventArgs e) {
    //some background action
    set Color();
}

When the background task is in progress, the panel background color needs to change. However, if you try to access the panel control directly through the background thread, an error may occur because the owner of the control is different from the one invoking it. To avoid the error, you check the InvokeRequired property and create a delegate for the method containing the action and call Invoke with that delegate. Within Invoke, you pass a delegate to a method that will actually perform the action desired on the control. For example, in this case, you want to change the background color of the panel. Instead of doing it directly, you change it within a method named SetColor() and call SetColor() in the DoWork event handler. This way, you can be sure that you are making thread-safe calls to the control.

The InvokeRequired property returns false if the current thread is one that created the control and no special invoke is required to use the control. Use InvokeRequired property and create a delegate for the method containing the action and call Invoke with that delegate.

 



European ASP.NET Core Hosting :: .NET Core NUGET packages

clock July 6, 2021 06:40 by author Peter

.Net core development is based on the open-source NuGet packages. NuGet will reduce work and manage the application very easily. We need to select the NuGet based on the purpose otherwise it will impact the application performance as well as application size also.

As part of the development lifecycle, we need to select different libraries at each stage. 

While creating a new service, we need to inform what kind of APIs will be available and what fields will belong to that each APIs. It will provide high-level information about the service. So we need clear documentation at the first phase of development itself. We can create documentation either manually or code-wise. Manual documentation will double the work. In code wise we can generate the document by code and based on the needs we can update it later. We can create high-level API documentation using Swashbuckle at the initial stage of development.

Swashbuckle

We need to share the API specs while developing a restful API. Swashbuckle provides documentation for the API in JSON object format. The document contains the resource URL, request and response object, corresponding content format, and different status code types with the response object. It provides a built-in UI and loads the results into that. We can load the swagger YAML file into any open API application. It will be easy to understand and validate the API specs.

Once we have done the high level of documentation we need to start the API development. Each API will have a set of inputs. We need to validate the input before the processing logic. So we need to add a different set of validation rules based on the needs. We can add the validation to our API inputs using the Fluent validation library.

Fluent Validation

It contains .NET libraries and the validation is performed using the Lambda expression. We can add different types of custom validation to the input. We can build our custom validation rules based on the needs.

Once we have validated our inputs we need to store the data into a database based on our business. We need to convert the input model based on the data layer. So data conversion layer is required to map the user input to a data model. We can use AutoMapper to convert the user input objects to data objects and vice versa.

Automapper

It's a popular object-to-object mapping library that can be used to map objects belonging to dissimilar types. AutoMapper saves you the tedious effort of having to manually map one or more properties of such incompatible types.

Once data has been converted to data layer format we need to store the data into the database. So we need to establish the connection and form the query to run on the database. We need some mediators to establish a connection between the data context layer to the database. ORM serve this purpose using a different set of libraries

ORM

Data access is an important part of almost any software application. ASP.NET Core supports a variety of data access options and can work with any .NET data access framework. The choice of which data access framework to use depends on the application's needs. Dapper and Entity are used in most of the .net core apps. I have used both ORM into a single service based on the need to achieve better results in terms of performance and simplicity.

Once we have developed our APIs we need to check the application flow. We can identify the flows using logs. Logs will have information about the request, what will be the response for that request. Logs are mainly used to trace the application flows while debugging the errors. We can use ILogger to enable the logging mechanism to our services.

ILogger

It provides a set of features to store the application level logs to track the application flow. We can configure any logging providers like Serilog or Nlog and we can log any level of logs like information, warning, and errors. We can customize it based on our needs.

Based on the logs we can identify the root cause of errors. Sometimes due to network or data unavailability, the request will get failed. So we need to retry the request or retry after a while. It will increase the application's reliability while serving the request. We can enable the retry mechanism using the Polly library.

Polly

Polly is a .NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout in a fluent and thread-safe manner.

Service will serve the request as per the user's needs. After a while service might be get stopped due to some errors. We need to get the information once the service is stopped due to some errors. We need some kind of monitor to ensure our service is working as expected. .Net Core provides Diagnostics health checks libraries. Using this we can monitors service heartbeats at frequent intervals.

HealthChecks

Microservices-based applications often use heartbeats or health checks to enable their performance monitors, schedulers, and orchestrators to keep track of the multitude of services. Microsoft.AspNetCore.Diagnostics.HealthChecks package is referenced implicitly for ASP.NET Core apps

Once we have developed the API we need to test the new unit is working as expected. We shouldn't break the existing functions because of the new addition. So we need to test the unit of code before release to testing. We can do by adding unit test as part of development.

Unit Test

It's a type of software testing where individual units or components of the software are tested. The purpose is to validate that each unit of the software code performs as expected. Unit Testing is done during the development (coding phase) of an application by the developers. We can do the Unit testing by XUNIT or NUnit. I have used XUNIT for unit testing and open cover Nuget to generate the unit test reports.

Once we have developed our service we need to deploy it. Our service might be a web service or hosted service. While deploying multiple services it will be better to forward the request via a single endpoint using a gateway pattern. It will hide the request and forward it to internal services. We can implement API gateway using ocelot in .net core

Ocelot

It's a .NET API gateway library. It provides a single entry point to the system that is running multiple microservices and service-oriented services. It acts as a reverse proxy. Its supports HTTPS, HTTP, and WebSockets protocol. It provides gateway features like authentication, authorization, routing, query-string, and header transformation and aggregation. It's lightweight and provides better results while managing multiple services.

Some of the alternative libraries are available at each stage but we need to select them based on our needs. Choosing correct libraries will ease the development, increase the application performance and reduce the development time.



European ASP.NET Core Hosting :: SOLID With .Net Core

clock July 5, 2021 07:44 by author Peter

This article will explain the SOLID principles, which one of those acronyms means alongside a practical example of an architectural problem that we could use in order to fix the problem.
 
What are the SOLID principles?

 
A SOLID principle is a group of 5 different design patterns whose main focus is to create loosely coupled architectures with a high level of maintainability. The name SOLID comes from the acronym of its five principles,

    S - Single Responsability Principle;
    O - Open-Closed Principle;
    L - Liskov Substitution Principle;
    I - Interface Segregation Principle;
    D - Dependency Inversion Principle.

If in your project you have the common problem to solve a bug and create two different bugs then you must start to think about using the SOLID principles in order to have a cleaner and more organized code, which does not break other features when you are working in a specific feature.
 
S - SRP - Single Responsibility Principle
“A class should have one, and only one, a reason to change.”  (Robert C. Martin)
 
Avoiding Jack of all Trades' classes, the Single Responsibility Principle says that your class should have a single responsibility. This means that you should avoid, at all costs, having classes with multiple functionalities.
 
Imagine doing maintenance in a class with multiples functionalities, the risk of fixing one functionality but breaking other functionalities as a side-effect is extremely high. To overcome those kinds of situations you should apply the Single Responsibility Principle.
 
One great example of the Single Responsibility Principle being applied is the 3-tier projects architectures, where each tier is responsible only for their tasks. Ie.: the business layer is responsible to validate business logics where the repository layer is responsible to manipulate the database.

O - OCP - Open/Closed Principle
“A class is closed, since it may be compiled, stored in a library, baselined, and used by client classes. But it is also open, since any new class may use it as parent, adding new features. When a descendant class is defined, there is no need to change the original or to disturb its clients.” (Bertrand Meyer)
 
The topic here is also to avoid Jack of All Trades' classes but in a different manner. The open-closed says that instead of modifying your base classes to add more features you should create a new class with those features, and this class will be inheriting the base class.
 
Imagine having a class that based on the input could go through one workflow or another workflow completely different, you will probably manage that with conditional statements.
 
Now, imagine when you need to do maintenance in one of those workflows. What is the risk of breaking, by mistake, the other ones?
 
The Open-Closed Principle says that you must have a base class that should not be modified to add new functionalities. Instead, you should have a new class inheriting the base class with those new functionalities. In this case, when you need to do maintenance in a specific workflow you are certain that will have no side effects in the others workflows.
 
L - LSP - Liskov Substitution Principle

“Subtypes must be substitutable for their base types.” (Barbara Liskov)
 
The main point from the Liskov Substitution Principle is to create a base class in order to be inherited from its subtypes.
 
The Liskov Substitution Principle states that if we change our class for its base class we should not have different behaviour. Instead, if we change our subclasses by our base class without breaking the application because our base classes must behave in the same way as our subclasses. In this case, if we need to replace our subclasses for another subclass we would not need to refactor anything because it would be a simple substitution and everything would still be working the same.
 
I - ISP - Interface Segregation Principle
 
“No client should be forced to depend on methods it does not use.” (Robert C. Martin)
 
This principle is not the best friend of laziness, it is the worst friend of those projects when you place all your needed contracts in a single place, with this we are not going to be able to reuse those interfaces or, even worse, we will not be using every method in the contract. Instead of creating an interface with many contracts on it, you should create as many interfaces as possible by grouping common behaviours.
 
The Interface Segregation Principle says that we should segregate interfaces in order to do not depend on interfaces with a contract that will not be implemented. We should have smaller interfaces and depend on more than one interface if needed.

D - DIP - Dependency Inversion Principle
“High-level modules should not depend upon low-level modules. Both should depend upon abstractions” (Robert C. Martin)
 
Here we have the best friend of loosely coupled applications and unit testing. If you have ever needed to replace one dependency and to do this you had to refactor every method that this dependency was being called then you should use the Dependency Inversion Principle.
 
The Dependency Inversion Principle says that our classes should not depend on low-level modules because this created a strong coupled application, making it very difficult to unit test and substitute dependencies. Instead, our classes should receive in their constructor the abstraction of their low-level dependencies, with this we may change our classes that implement the contract without breaking or needing to refactor the whole application. Also, we could easily mock our dependencies in order to create unit tests.

SOLID principles Practical Usage One-by-One
SRP - The Problem - Class that sum two numbers and log it
Here we have a problem that we can use the Single Responsability Principle to fix it. A class with two functionalities, the first is to sum two numbers and the second is to log this operation
    class Problem    
    {    
        public int Sum(int numberOne, int numberTwo)    
        {    
            int result = numberOne + numberTwo;    
            LogCalculations("Sum Operation. Number one: " + numberOne + ", Number two: " + numberTwo + ". Result: " + result);    
            return result;    
        }    
      
        private void LogCalculations(string message)    
        {    
            string path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + @"\log.txt";    
            using (StreamWriter sw = File.CreateText(path))    
            {    
                sw.WriteLine(message);    
            }    
        }    
    }
   

Applying the Single Responsibility Principle we would have two different classes:
    class Solution    
    {    
        public int Sum(int numberOne, int numberTwo)    
        {    
            int result = numberOne + numberTwo;    
            Logging.Log("Sum Operation. Number one: " + numberOne + ", Number two: " + numberTwo + ". Result: " + result);    
            return result;    
        }    
    }    
    public static class Logging    
    {    
        public static void Log(string message)    
        {    
            string path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + @"\log.txt";    
            using (StreamWriter sw = File.CreateText(path))    
            {    
                sw.WriteLine(message);    
            }    
        }    
    }  


OCP - The Problem 1 - Class to do math calculations
In this class, we receive as input one enumeration with the operation type and return the result from this operation. If we need to add or modify one operation, we would need to modify the class.

    public class MathCalculate    
    {    
        public double Calculate(double numberA, double numberB, CalculationType calculationType)    
        {    
            double result = 0;    
            switch (calculationType)    
            {    
                case CalculationType.addition:    
                    result = numberA + numberB;    
                    break;    
                case CalculationType.multiplication:    
                    result = numberA * numberB;    
                    break;    
                case CalculationType.subtraction:    
                    result = numberA - numberB;    
                    break;    
                default:    
                    break;    
            }    
            return result;    
        }    
    }    
      
    public enum CalculationType    
    {    
        addition,    
        multiplication,    
        subtraction    
    }


Applying the Open-Closed Principle we would have a base class "Calculation" and each operation would be a new class, inheriting from this base class. If we need to add or modify a new operation, then we would need to work only in the class of this operation
    public abstract class BaseCalculation    
    {    
        public abstract double Calculate(double numberA, double numberB);    
    }    
      
    public class AdditionCalculation : BaseCalculation    
    {    
        public override double Calculate(double numberA, double numberB)    
        {    
            return numberA + numberB;    
        }    
    }    
    public class MultiplicationCalculation : BaseCalculation    
    {    
        public override double Calculate(double numberA, double numberB)    
        {    
            return numberA * numberB;    
        }    
    }    
      
    public class SubtractionCalculation : BaseCalculation    
    {    
        public override double Calculate(double numberA, double numberB)    
        {    
            return numberA - numberB;    
        }    
    }    
      
    public class DivisionCalculation : BaseCalculation    
    {    
        public override double Calculate(double numberA, double numberB)    
        {    
            return numberA / numberB;    
        }    
    }


OCP - The Problem 2 - Email sender with validations
If we need to add or modify the validations, we would need to modify the email sender class, risking to break the email sender functionality.
    public class MailSender    
    {    
        public void SendMail(string subject, string body, string recipient)    
        {    
            SmtpClient smtpClient = new SmtpClient("smtp.gmail.com")    
            {    
                Port = 587,    
                Credentials = new NetworkCredential("email", "password"),    
                EnableSsl = true,    
            };    
      
            //validate recipient's domain    
            if (!recipient.ToString().EndsWith("@hostforlifeasp.net"))    
            {    
                Console.WriteLine("Mail destinatary not in the domain");    
                return;    
            }    
      
            //validate body    
            if (string.IsNullOrEmpty(body))    
            {    
                Console.WriteLine("Mail body is empty.");    
                return;    
            }    
      
            smtpClient.SendAsync("[email protected]", recipient, subject, body, null);    
        }    
    }


Applying the open-closed principle to solve this problem we would have this email sender class receiving a list of validations. When we need to add or modify one of those validations would not be necessary to update the mail sender class:
    public class MailClass    
    {    
        public string Subject { get; set; }    
        public string Body { get; set; }    
        public string Recipient { get; set; }    
    }    
      
    public interface IValidation<T>    
    {    
        bool Validate(T mail);    
    }    
      
      
    public class DomainValidation : IValidation<MailClass>    
    {    
        public bool Validate(MailClass mail)    
        {    
            if (mail.Recipient.ToString().EndsWith("@hostforlifeasp.net"))    
                return false;    
      
            return true;    
        }    
    }    
    public class BodyValidation : IValidation<MailClass>    
    {    
        public bool Validate(MailClass mail)    
        {    
            if (string.IsNullOrEmpty(mail.Body))    
                return false;    
      
            return true;    
        }    
    }    
      
    public class Main    
    {    
        public void SendMail(MailClass mailClass, List<IValidation<MailClass>> validations)    
        {    
            List<bool> validationsResult = new List<bool>();    
            validations.ForEach(x => validationsResult.Add(x.Validate(mailClass)));    
      
            if (!validationsResult.Any(x => !x))    
            {    
                SmtpClient smtpClient = new SmtpClient("smtp.gmail.com")    
                {    
                    Port = 587,    
                    Credentials = new NetworkCredential("email", "password"),    
                    EnableSsl = true,    
                };    
      
                smtpClient.SendAsync("[email protected]", mailClass.Recipient, mailClass.Subject, mailClass.Body, null);    
            };    
        }    
    }


OCP - Extension Methods

We can also make usage of extension methods in order to extend objects without modifying their base behaviour. The following code extends the String object, adding this new method on it:
    public static class SolutionThree    
    {    
        public static string PeterString(this string normalString)    
        {    
            return "peter's String is: " + normalString;    
        }    
    }  

LSP - The Problem - Having a subtraction class inheriting from an addition class
In this example, we have an addition class working fine and then we extended it, wrongly, creating the subtraction class. If we replace the subtraction class with the base class (addition) our output will differ.
    public class AdditionCalculation    
    {    
        public AdditionCalculation(int numberA, int numberB)    
        {    
            this.NumberB = numberB;    
            this.NumberA = numberA;    
        }    
        public int NumberA { get; set; }    
        public int NumberB { get; set; }    
        public virtual int Calculate()    
        {    
            return this.NumberA + NumberB;    
        }    
    }    
    public class SubtractionCalculation : AdditionCalculation    
    {    
        public SubtractionCalculation(int numberA, int numberB) : base(numberA, numberB)    
        {    
        }    
      
        public new int Calculate()    
        {    
            return NumberA - NumberB;    
        }    
    }

LSP - The Problem - Having a subtraction class inheriting from an addition class
In this example, we have an addition class working fine and then we extended it, wrongly, creating the subtraction class. If we replace the subtraction class with the base class (addition) our output will differ.
    public class AdditionCalculation    
    {    
        public AdditionCalculation(int numberA, int numberB)    
        {    
            this.NumberB = numberB;    
            this.NumberA = numberA;    
        }    
        public int NumberA { get; set; }    
        public int NumberB { get; set; }    
        public virtual int Calculate()    
        {    
            return this.NumberA + NumberB;    
        }    
    }    
    public class SubtractionCalculation : AdditionCalculation    
    {    
        public SubtractionCalculation(int numberA, int numberB) : base(numberA, numberB)    
        {    
        }    
      
        public new int Calculate()    
        {    
            return NumberA - NumberB;    
        }    
    }


 

Applying the Liskov Substitution Principle to solve this problem we have a base class then we have the addition and subtraction classes inheriting from this base class. If we change our class with the base class our output is not going to be affected.
    public abstract class MathCalculate    
    {    
        public MathCalculate(int numberA, int numberB)    
        {    
            this.NumberB = numberB;    
            this.NumberA = numberA;    
        }    
        public int NumberA { get; set; }    
        public int NumberB { get; set; }    
      
        public abstract int Calculate();    
    }    
    public class Addition : MathCalculate    
    {    
        public Addition(int numberA, int numberB) : base(numberA, numberB)    
        {    
        }    
      
        public override int Calculate()    
        {    
            return this.NumberA + NumberB;    
        }    
    }    
    public class Subtraction : MathCalculate    
    {    
        public Subtraction(int numberA, int numberB) : base(numberA, numberB)    
        {    
        }    
      
        public override int Calculate()    
        {    
            return NumberA - NumberB;    
        }    
    }  



ISP - The Problem - A single interface for many type of classes
In this example, we have a single interface called IAnimal that is used for both Human and Whale classes. The problem is that not the whole contract fit both items because, as we all know, whales do not walk.
    public interface IAnimal    
    {    
        void Walk();    
        void Breath();    
        void Eat();    
        void Argument();    
    }    
      
    public class Human : IAnimal    
    {    
        public void Argument()    
        {    
            // Argumentation    
        }    
      
        public void Breath()    
        {    
            // Breathing    
        }    
      
        public void Eat()    
        {    
            // Eating    
        }    
      
        public void Walk()    
        {    
            // Walk    
        }    
    }    
    public class Whale : IAnimal    
    {    
        public void Argument()    
        {    
            // Argumentation    
        }    
      
        public void Breath()    
        {    
            // Breathing    
        }    
      
        public void Eat()    
        {    
            // Eating    
        }    
      
        public void Walk()    
        {    
            throw new NotImplementedException();    
        }    
    }


Applying the Interface Segregation Principle we break the single interface in smaller interfaces with common methods and then we only use the interfaces that we need in our classes.
    public interface IFeed {    
        void Eat();    
    }    
      
    public interface IArgument    
    {    
        void Argument();    
    }    
      
    public interface IGroundMoviment    
    {    
        void Walk();    
    }    
    public interface IAirMoviment    
    {    
        void Fly();    
    }    
    public interface IWaterMoviment    
    {    
        void Swimm();    
    }    
      
    public class Human : IGroundMoviment, IArgument, IFeed    
    {    
        public void Argument()    
        {    
            // Argument    
        }    
      
        public void Eat()    
        {    
            // Eat    
        }    
      
        public void Walk()    
        {    
            // Walk    
        }    
    }    
    public class Whale : IWaterMoviment, IFeed    
    {    
        public void Eat()    
        {    
            // Eat    
        }    
      
        public void Swimm()    
        {    
            // Swimm    
        }    
    }

DIP - The Problem - Strong Coupled Application
In this example, we need to instantiate our dependency inside our method, creating a strong coupled application. If we want to replace this dependency we would need a lot of work, needing to change those dependencies at every method that is being used. Also, it is much more difficult to unit test this layer because it is much harder to mock this dependency.
    public class BusinessLayer    
    {    
        public void AddItem(int itemId)    
        {    
            RepositoryLayer repositoryLayer = new RepositoryLayer();    
            if (!string.IsNullOrEmpty(repositoryLayer.GetItem(itemId)))    
                repositoryLayer.Update();    
            else    
                repositoryLayer.Create();    
        }    
    }    
    public class RepositoryLayer    
    {    
        public void Create()    
        {    
            //save data into the Database    
        }    
        public void Delete()    
        {    
            //delete data from the Database    
        }    
        public void Update()    
        {    
            //update data in the Database    
        }    
        public string GetItem(int itemId)    
        {    
            //get item from the Database    
            return "item";    
        }    
    }


Applying the Dependency Inversion Principle, we receive the interface of our dependency in the class constructor. If we need to change the class that implements this dependency contract it would not be necessary to refactor any method where we use this dependency.
    public class BusinessLayer    
    {    
        private readonly IRepositoryLayer repositoryLayer;    
      
        public BusinessLayer(IRepositoryLayer repositoryLayer)    
        {    
            this.repositoryLayer = repositoryLayer;    
        }    
        public void AddItem(int itemId)    
        {    
            if (!string.IsNullOrEmpty(repositoryLayer.GetItem(itemId)))    
                repositoryLayer.Update();    
            else    
                repositoryLayer.Create();    
        }    
    }    
    public interface IRepositoryLayer    
    {    
        void Create();    
      
        void Delete();    
      
        void Update();    
      
        string GetItem(int itemId);    
    }



About HostForLIFE

HostForLIFE is European Windows Hosting Provider which focuses on Windows Platform only. We deliver on-demand hosting solutions including Shared hosting, Reseller Hosting, Cloud Hosting, Dedicated Servers, and IT as a Service for companies of all sizes.

We have offered the latest Windows 2019 Hosting, ASP.NET 5 Hosting, ASP.NET MVC 6 Hosting and SQL 2019 Hosting.


Month List

Tag cloud

Sign in