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 :: Web API HttpContextAccessor in Asp.NET Core

clock May 29, 2023 09:55 by author Peter

The HttpContextAccessor class provides access to the present HTTP request and response context within ASP.NET Core Web API. It provides access to headers, cookies, query parameters, and user claims, among other aspects of the HTTP request and response.

Add Dependency to your program in Step 1.cs

Example Source Code builder.Services.AddSingletonIHttpContextAccessor, HttpContextAccessor>();

Utilizing HttpContextAccessor
In the example given below, the User is obtained, and then the company ID and user ID are obtained from the Auth Microservice.


Example Source Code

builder.Services.AddSingletonIHttpContextAccessor, HttpContextAccessor>();

Utilizing HttpContextAccessor
In the example given below, the User is obtained, and then the company ID and user ID are obtained from the Auth Microservice.

Code Example
using AC_Projects_API_Domain_Layer.Models;
using AC_Projects_API_Repository_Layer.IRepository;
using AC_Projects_API_Service_Layer.IService;
using AutoMapper;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace AC_Projects_API_Service_Layer.Services
{
    public class ColumsService : ICustomService<Columns>
    {
        private readonly IRepository<Columns> _statusRepository;
        public long LoggedInUserCompanyId;
        public long LoggedInUserId;
        private readonly IHttpContextAccessor _httpContextAccessor;

        public ColumsService(
            IRepository<Columns> statusRepository,
            IMapper mapper,
            IHttpContextAccessor httpContextAccessor
            )
        {
            _statusRepository = statusRepository;
            _httpContextAccessor = httpContextAccessor;
            ClaimsIdentity user = (ClaimsIdentity)_httpContextAccessor.HttpContext.User.Identity;
            LoggedInUserCompanyId = Convert.ToUInt32(user.FindFirst("CompanyId").Value.ToString());
            LoggedInUserId = Convert.ToUInt32(user.FindFirst("UserId").Value.ToString());
        }
        public void Delete(Columns entity)
        {
            try
            {
                if (entity != null)
                {
                    entity.ModifiedBy = LoggedInUserId;
                    entity.ModifiedDate = DateTime.Now;
                    _statusRepository.Delete(entity);
                    _statusRepository.SaveChanges();
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        public Columns Get(long Id)
        {
            try
            {
                var obj = _statusRepository.Get(Id);
                if (obj != null)
                {
                    return obj;
                }
                else
                {
                    return null;
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        public IEnumerable<Columns> GetAll()
        {
            try
            {
                var obj = _statusRepository.GetAll();
                if (obj != null)
                {
                    return obj;
                }
                else
                {
                    return null;
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        public Columns Insert(Columns entity)
        {
            try
            {
                if (entity != null)
                {
                    entity.CreatedBy = LoggedInUserId;
                    entity.CreatedDate = DateTime.Now;
                    entity.CompanyId = LoggedInUserCompanyId;
                    _statusRepository.Insert(entity);
                    _statusRepository.SaveChanges();
                    return entity;
                }
                return null;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public void Remove(Columns entity)
        {
            try
            {
                if (entity != null)
                {
                    _statusRepository.Remove(entity);
                    _statusRepository.SaveChanges();
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        public void Update(Columns entity)
        {
            try
            {
                if (entity != null)
                {
                    entity.ModifiedBy = LoggedInUserId;
                    entity.ModifiedDate = DateTime.Now;
                    _statusRepository.Update(entity);
                    _statusRepository.SaveChanges();
                }
            }
            catch (Exception)
            {
                throw;
            }
        }
    }
}

Why'd we Use HttpContextAccessor?
For the following reasons, the `HttpContextAccessor` is frequently utilized in ASP.NET Core Web API applications:

Accessing HTTP Request and Response
Access to the current HTTP request and response context is the main goal of the `HttpContextAccessor` component. You can use it to get details about incoming requests, including headers, cookies, query parameters, and user claims. By gaining access to the "Response" property, you can also alter the HTTP response.

Authentication and Authorization

You can gain access to authentication-related data via the `HttpContextAccessor`, including the claims, identity, and roles of an authenticated user. This can be used to implement authorization checks inside of your application, figure out the permissions of the current user, or alter behavior based on authentication status.

Logging and Diagnostics
You can record information relevant to the current HTTP request using the `HttpContextAccessor' To assist with debugging or monitoring, you can use it to record request-specific information, such as the URL, headers, or user information. It enables you to effectively detect problems by correlating log entries with particular requests.

Custom Middleware and Filters
You can design action filters or middleware components in ASP.NET Core that need access to the HTTP context. You can access request-specific data and adjust the request or response pipeline by using the `HttpContextAccessor`, which enables you to inject the current context into your custom components.

Integration with External Services

To carry out specific tasks, some third-party services or libraries can need access to the HTTP context. These external components or services can interact with the current request and response by utilizing the "HttpContextAccessor" to give them the necessary context.

Although the "HttpContextAccessor" offers practical access to the HTTP context, it should only be used sparingly. By exposing the HTTP context too broadly in your application, you risk creating security holes or adding dependencies that make it harder to maintain your code. The use of "HttpContextAccessor" in your application should, therefore, be carefully considered in terms of its scope and objectives.

The ASP.NET Core Web API's `ttpContextAccessor` gives users access to the current HTTP request and response context. It enables you to carry out actions based on authentication and authorization, alter the response, and retrieve information about the request. It is frequently employed for logging, diagnostics, developing unique middleware, interfacing with outside services, and other purposes. It should be utilized cautiously, taking into account security issues and keeping a clean codebase.



European ASP.NET Core Hosting :: Non-nullable Property Must Contain a Non-null Value in .NET 6

clock January 17, 2022 08:19 by author Peter

Prior to .NET 6, null-state analysis and variable annotations are disabled for existing project, however, it is enabled for all .NET 6 projects by default. Because of this, all the properties or models are showing warning for non-nullable elements. In this article, I will explore the warning with null reference in more detail and also provide the possible recommended solutions.

Recent version of Visual Studio 2022 and .NET 6, we have a common warning for the models or properties. The warning is given below.

    Non-nullable property must contain a non-null value when exiting constructor. Consider declaring the property as nullable

Recently, I was getting this warning for most of the properties which are not specified as nullable. I was not happy with this warning though I was able to run my .NET 6 application smoothly.

Exact Warning

    Non-nullable property ‘propertyname’ must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

After carefully observing this error message, it makes sense for those properties. In order to minimize the likelihood that, our code causes the runtime to throw System.NullReferenceException, we need to resolve this warning.

Therefore, the compiler is giving warning in the solution that the default assignment of your properties (which is null) doesn’t match its state type (which is non-null string).

Let’s explore a bit more on nullable references.

Null state Analysis

This analysis highlights and tracks the null reference. In other words, our code may return null response, which may result in runtime error throwing System.NullReferenceException. The .NET compiler utilizes Null State Analysis to identify the null-state of a variable or properties.

To overcome this System.NullReferenceException and resolve this warning, we can assign a default value to the variable. Alternatively, we can check against null before doing any modification or operation.

Sample:
String peter = null;
//code
//code
// warning: dereference null.
Console.WriteLine($"The length of the message is {peter.Length}");
if(peter != null)
{
// No warning. Analysis determined "message" is not null.
Console.WriteLine($"The length of the message is { peter.Length}");
}
peter = "Hello, World!";


// No warning. Analysis determined "message" is not null.
Console.WriteLine($"The length of the message is {peter.Length}");

This is how static analysis determines that the peter is dereference null, i.e. maybe-null. However, if we check null before using it or assign the value, then the warning will be removed.

Null variable Annotations
We generally define the variable to be either null or not null, explicitly with reference type.

We can declare a variable as non-nullable reference type or nullable reference type, which provides clear indication to null-state analysis. Based on the declaration, the compiler gets enough information to make assumptions about the variable.

Nullable Reference Type: we use syntax “?” for nullable value types in variables. By appending ? in the type of the variable, we declare a nullable variable.

Example

string? peter;

Using the above declaration, it is verified that all reference type variables are in the existing code. However, local variables var is considered as nullable.

We can also overwrite a warning if we know the variable is not null. Even though we know that value can’t be null, the compiler detects it as maybe-null with its null-state analysis. We can use null-forgiving operator ! after the variable. To overwrite compiler’s analysis:

Example
peter!.Length

Generally, the rules for handling any type of parameter as per C# 8.0.
    If the type argument for T is a reference type, T? references the corresponding nullable reference type. For example, if T is a string, then T? is a string?.
    If the type argument for T is a value type, T? references the same value type, T. For example, if T is an int, the T? is also an int.
    If the type argument for T is a nullable reference type, T? references that same nullable reference type. For example, if T is a string?, then T? is also a string?.
    If the type argument for T is a nullable value type, T? references that same nullable value type. For example, if T is a int?, then T? is also a int?.

For more detail Click here.

Now we will explore possible solutions.

Solution
Now, we will discuss the solution for the above warning. We can resolve this error in 3 ways.

Solution 1
We can simply make the properties nullable, as shown below:

Sample:
public class AppSetting {
    public string? ReferenceKey { get; set; }
    public string? Value { get; set; }
    public string? Description { get; set; }
}

Solution 2

We can assign default value to those properties as shown below:
public class AppSetting {
    public string ReferenceKey { get; set; } = "Default Key";
    public string Value { get; set; } = "Default Value";
    public string Description { get; set; } = "Default Description";
}


Alternatively, you can give a reasonable default value for non-nullable strings as string.empty.

Example
public class AppSetting {
    public string ReferenceKey { get; set; } = string.empty;
    public string Value { get; set; } = string.empty;
    public string Description { get; set; } = string.empty;
}


Solution 3
You can disable this warning from project level. You can disable it by removing the below line from project file csproj or setting.
<Nullable>enable</Nullable>

For more detail check this link.

Alternatively, you can go to project properties by right-clicking on it. Then make nullable enable from build general option as portrayed.
Non-nullable property must contain a non-null value when exiting constructor. Consider declaring the property as nullable



European ASP.NET Core Hosting :: Send Push Notification To Android Device From .Net Core Web API

clock September 20, 2021 06:57 by author Peter

In this article, you will learn about how to send push notifications to android devices using FCM from .Net Core Web API.  You will also learn how to create a Firebase project and get Server Key and Sender ID step by step. Our main focus is how to handle send notification functionality on the API side.
What is FCM?

Firebase Cloud Messaging (FCM) is a free cloud service from Google that allows users to send notifications and messages to users.

It is a cross-platform messaging solution. You can send notifications and messages across a variety of platforms, including Android, iOS and web applications.
Create Firebase Project

Go to Firebase Console.

Follow the steps to set up the project.
After adding the project to the Firebase, add App to the same project.
Send Push Notification To Android Device From .Net Core Web API

 

Enter package name, app name, and SHA-1 key of your Android Studio project.

Follow all the steps to complete the add Firebase to your Android app. If you face any problem, you can refer to  Add Firebase to your Android Project.
Get FCM Sender ID & Server Key

Click on the “Gear” icon and access “Project settings”.

Go to the “Cloud Messaging” section and you will have access to the Sender ID and Server Key. We will use it later in the API.


For setting up a Firebase Cloud Messaging client app on Android and getting FCM registration token or device token please refer to this Set up an Android client.
How to send FCM push notification from an ASP.NET Core Web API project.

What CorePush Package?

It’s very Lightweight .NET Core Push Notifications for Android and iOS. I used it in my project to send Firebase Android and Apple iOS push notifications. In this article just I focus on sending push notifications on Android devices Useful links,

  • NuGet package
  • Documentation

Step 1 – Create Project
Open Visual Studio click on “Create a new project”.

Select ASP.NET Core Web Application option.


Add Project name and Solution name.

Select the “API” option with “.NET Core” and “ASP.NET Core 3.1” to create ASP.NET API.


You can see the default folder structure.

Step 2 – Install NuGet Packages
Open Package Manager Console


And run the below commands one by one:

  1. Install-Package CorePush
  2. Install-Package Newtonsoft.Json
  3. Install-Package Swashbuckle.AspNetCore

Step 3 – Create Models for the controller
Now, create a directory with name Models and add the following files

  • ResponseModel.cs
  • NotificationModel.cs
  • FcmNotificationSetting.cs

ResponseModel.cs will contain definitions for response models.
NotificationModel.cs will contain definitions for notification and google notification model
FcmNotificationSetting.cs will contain definitions for FCM notification settings.

Code for ResponseModel.cs file,
using Newtonsoft.Json;

namespace net_core_api_push_notification_demo.Models
{
    public class ResponseModel
    {
        [JsonProperty("isSuccess")]
        public bool IsSuccess { get; set; }
        [JsonProperty("message")]
        public string Message { get; set; }
    }
}


Code for NotificationModel.cs file,
using Newtonsoft.Json;

namespace net_core_api_push_notification_demo.Models
{
    public class NotificationModel
    {
        [JsonProperty("deviceId")]
        public string DeviceId { get; set; }
        [JsonProperty("isAndroiodDevice")]
        public bool IsAndroiodDevice { get; set; }
        [JsonProperty("title")]
        public string Title { get; set; }
        [JsonProperty("body")]
        public string Body { get; set; }
    }

    public class GoogleNotification
    {
        public class DataPayload
        {
            [JsonProperty("title")]
            public string Title { get; set; }
            [JsonProperty("body")]
            public string Body { get; set; }
        }
        [JsonProperty("priority")]
        public string Priority { get; set; } = "high";
        [JsonProperty("data")]
        public DataPayload Data { get; set; }
        [JsonProperty("notification")]
        public DataPayload Notification { get; set; }
    }
}


Code for FcmNotificationSetting.cs file,
namespace net_core_api_push_notification_demo.Models
{
    public class FcmNotificationSetting
    {
        public string SenderId { get; set; }
        public string ServerKey { get; set; }
    }
}


Step 4 – Update appsettings.Development.json file

Code for appsettings.Development.json file,

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "FcmNotification": {
    "SenderId": "*SENDER_ID*",
    "ServerKey": "*SERVER_KEY*"
  }
}

Replace *SENDER_ID* with your sender Id.
Replace *SERVER_KEY* with your server key.
Which we have found from the Cloud Messaging section of Firebase project settings.

Step 5 – Create Service
Now, create a directory with the name Services and add the following file.
Code for NotificationService.cs file,
using CorePush.Google;
using Microsoft.Extensions.Options;
using net_core_api_push_notification_demo.Models;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using static net_core_api_push_notification_demo.Models.GoogleNotification;

namespace net_core_api_push_notification_demo.Services
{
    public interface INotificationService
    {
        Task<ResponseModel> SendNotification(NotificationModel notificationModel);
    }

    public class NotificationService : INotificationService
    {
        private readonly FcmNotificationSetting _fcmNotificationSetting;
        public NotificationService(IOptions<FcmNotificationSetting> settings)
        {
            _fcmNotificationSetting = settings.Value;
        }

        public async Task<ResponseModel> SendNotification(NotificationModel notificationModel)
        {
            ResponseModel response = new ResponseModel();
            try
            {
                if (notificationModel.IsAndroiodDevice)
                {
                    /* FCM Sender (Android Device) */
                    FcmSettings settings = new FcmSettings()
                    {
                        SenderId = _fcmNotificationSetting.SenderId,
                        ServerKey = _fcmNotificationSetting.ServerKey
                    };
                    HttpClient httpClient = new HttpClient();

                    string authorizationKey = string.Format("keyy={0}", settings.ServerKey);
                    string deviceToken = notificationModel.DeviceId;

                    httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", authorizationKey);
                    httpClient.DefaultRequestHeaders.Accept
                            .Add(new MediaTypeWithQualityHeaderValue("application/json"));

                    DataPayload dataPayload = new DataPayload();
                    dataPayload.Title = notificationModel.Title;
                    dataPayload.Body = notificationModel.Body;

                    GoogleNotification notification = new GoogleNotification();
                    notification.Data = dataPayload;
                    notification.Notification = dataPayload;

                    var fcm = new FcmSender(settings, httpClient);
                    var fcmSendResponse = await fcm.SendAsync(deviceToken, notification);

                    if (fcmSendResponse.IsSuccess()) {
                        response.IsSuccess = true;
                        response.Message = "Notification sent successfully";
                        return response;
                    } else {
                        response.IsSuccess = false;
                        response.Message = fcmSendResponse.Results[0].Error;
                        return response;
                    }
                }
                else {
                    /* Code here for APN Sender (iOS Device) */
                    //var apn = new ApnSender(apnSettings, httpClient);
                    //await apn.SendAsync(notification, deviceToken);
                }
                return response;
            }
            catch (Exception ex) {
                response.IsSuccess = false;
                response.Message = "Something went wrong";
                return response;
            }
        }
    }
}

Step 6 – Update Startup.cs file
Code for Startup.cs file,

using CorePush.Apple;
using CorePush.Google;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using net_core_api_push_notification_demo.Models;
using net_core_api_push_notification_demo.Services;

namespace net_core_api_push_notification_demo
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddTransient<INotificationService, NotificationService>();
            services.AddHttpClient<FcmSender>();
            services.AddHttpClient<ApnSender>();

            // Configure strongly typed settings objects
            var appSettingsSection = Configuration.GetSection("FcmNotification");
            services.Configure<FcmNotificationSetting>(appSettingsSection);

            // Register the swagger generator
            services.AddSwaggerGen(c => {
                c.SwaggerDoc(name: "V1", new OpenApiInfo { Title = "My API", Version = "V1" });
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment()) {
                app.UseDeveloperExceptionPage();
            }

            // Enable middleware to serve generated Swagger as a JSON endpoint
            app.UseSwagger();
            // Enable the SwaggerUI
            app.UseSwaggerUI(c => {
                c.SwaggerEndpoint(url: "/swagger/V1/swagger.json", name: "My API V1");
            });

            app.UseHttpsRedirection();
            app.UseRouting();
            app.UseAuthorization();
            app.UseEndpoints(endpoints => {
                endpoints.MapControllers();
            });
        }
    }
}

Step 7 – Add Controller
Now, add the NotificationController.cs file in the Controllers folder
Code for NotificationController.cs file,

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using net_core_api_push_notification_demo.Models;
using net_core_api_push_notification_demo.Services;

namespace net_core_api_push_notification_demo.Controllers
{
    [Route("api/notification")]
    [ApiController]
    public class NotificationController : ControllerBase
    {
        private readonly INotificationService _notificationService;
        public NotificationController(INotificationService notificationService)
        {
            _notificationService = notificationService;
        }

        [Route("send")]
        [HttpPost]
        public async Task<IActionResult> SendNotification(NotificationModel notificationModel)
        {
            var result = await _notificationService.SendNotification(notificationModel);
            return Ok(result);
        }
    }
}

Step 8 – Running Web API
Now, press F5 to start debugging the Web API project, if everything is OK, we’ll get the following output in the browser.


Now, we will call the send notification API and check whether the notification is sent or not. As per the below screenshot, enter all properties details and hit the Execute button. If everything is fine, then the value of isSuccess property is true and the value of the message property is “Notification sent successfully”.

Wow, here is the notification!



European ASP.NET Core Hosting :: JWT Authentication In ASP.NET Core

clock May 3, 2021 06:48 by author Peter

JWT in ASP.NET Core
JWT (JSON web token) has become more and more popular in web development. It is an open standard which allows transmitting data between parties as a JSON object in a secure and compact way. The data transmitting using JWT between parties are digitally signed so that it can be easily verified and trusted.

In this article, we will learn how to setup JWT with ASP.NET core web application. We can create an application using Visual Studio or using CLI (Command Line Interface).

    dotnet new webapi -n JWTAuthentication   

Above command will create an ASP.NET Web API project with the name "JWTAuthentication" in the current folder.
 
The first step is to configure JWT based authentication in our project. To do this, we need to register a JWT authentication schema by using "AddAuthentication" method and specifying JwtBearerDefaults.AuthenticationScheme. Here, we configure the authentication schema with JWT bearer options.
    public void ConfigureServices(IServiceCollection services)    
    {    
        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)    
        .AddJwtBearer(options =>    
        {    
            options.TokenValidationParameters = new TokenValidationParameters    
            {    
                ValidateIssuer = true,    
                ValidateAudience = true,    
                ValidateLifetime = true,    
                ValidateIssuerSigningKey = true,    
                ValidIssuer = Configuration["Jwt:Issuer"],    
                ValidAudience = Configuration["Jwt:Issuer"],    
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))    
            };    
        });    
        services.AddMvc();    
    }   


In this example, we have specified which parameters must be taken into account to consider JWT as valid. As per our code,  the following items consider a token valid:

    Validate the server (ValidateIssuer = true) that generates the token.
    Validate the recipient of the token is authorized to receive (ValidateAudience = true)
    Check if the token is not expired and the signing key of the issuer is valid (ValidateLifetime = true)
    Validate signature of the token (ValidateIssuerSigningKey = true)
    Additionally, we specify the values for the issuer, audience, signing key. In this example, I have stored these values in appsettings.json file.

AppSetting.Json

    {    
      "Jwt": {    
        "Key": "ThisismySecretKey",    
        "Issuer": "Test.com"    
      }    
    }   

The above-mentioned steps are used to configure a JWT based authentication service. The next step is to make the authentication service is available to the application. To do this, we need to call app.UseAuthentication() method in the Configure method of startup class. The UseAuthentication method is called before UseMvc method.

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)    
    {    
        app.UseAuthentication();    
        app.UseMvc();    
    }

Generate JSON Web Token
I have created a LoginController and Login method within this controller, which is responsible to generate the JWT. I have marked this method with the AllowAnonymous attribute to bypass the authentication. This method expects the Usermodel object for Username and Password.
 
I have created the "AuthenticateUser" method, which is responsible to validate the user credential and returns to the UserModel. For demo purposes, I have returned the hardcode model if the username is "Peter". If the "AuthenticateUser" method returns the user model, API generates the new token by using the "GenerateJSONWebToken" method.
 
Here, I have created a JWT using the JwtSecurityToken class. I have created an object of this class by passing some parameters to the constructor such as issuer, audience, expiration, and signature.
 
Finally, JwtSecurityTokenHandler.WriteToken method is used to generate the JWT. This method expects an object of the JwtSecurityToken class.
    using Microsoft.AspNetCore.Authorization;    
    using Microsoft.AspNetCore.Mvc;    
    using Microsoft.Extensions.Configuration;    
    using Microsoft.IdentityModel.Tokens;    
    using System;    
    using System.IdentityModel.Tokens.Jwt;    
    using System.Security.Claims;    
    using System.Text;    
        
    namespace JWTAuthentication.Controllers    
    {    
        [Route("api/[controller]")]    
        [ApiController]    
        public class LoginController : Controller    
        {    
            private IConfiguration _config;    
        
            public LoginController(IConfiguration config)    
            {    
                _config = config;    
            }    
            [AllowAnonymous]    
            [HttpPost]    
            public IActionResult Login([FromBody]UserModel login)    
            {    
                IActionResult response = Unauthorized();    
                var user = AuthenticateUser(login);    
        
                if (user != null)    
                {    
                    var tokenString = GenerateJSONWebToken(user);    
                    response = Ok(new { token = tokenString });    
                }    
        
                return response;    
            }    
        
            private string GenerateJSONWebToken(UserModel userInfo)    
            {    
                var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));    
                var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);    
        
                var token = new JwtSecurityToken(_config["Jwt:Issuer"],    
                  _config["Jwt:Issuer"],    
                  null,    
                  expires: DateTime.Now.AddMinutes(120),    
                  signingCredentials: credentials);    
        
                return new JwtSecurityTokenHandler().WriteToken(token);    
            }    
        
            private UserModel AuthenticateUser(UserModel login)    
            {    
                UserModel user = null;    
        
                //Validate the User Credentials    
                //Demo Purpose, I have Passed HardCoded User Information    
                if (login.Username == "Peter")    
                {    
                    user = new UserModel { Username = "Peter", EmailAddress = "[email protected]" };    
                }    
                return user;    
            }    
        }    
    }   


Once, we have enabled the JWT based authentication, I have created a simple Web API method that returns a list of value strings when invoked with an HTTP GET request. Here, I have marked this method with the authorize attribute, so that this endpoint will trigger the validation check of the token passed with an HTTP request.
 
If we call this method without a token, we will get 401 (UnAuthorizedAccess) HTTP status code as a response. If we want to bypass the authentication for any method, we can mark that method with the AllowAnonymous attribute.
 
To test the created Web API, I am Using Fiddler. First, I have requested to "API/login" method to generate the token. I have passed the following JSON in the request body.
    {"username": "Peter", "password": "password"}

As a response, we will get the JSON like the following,
    {    
        "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJKaWduZXNoIFRyaXZlZGkiLCJlbWFpbCI6InRlc3QuYnRlc3RAZ21haWwuY29tIiwiRGF0ZU9mSm9pbmciOiIwMDAxLTAxLTAxIiwianRpIjoiYzJkNTZjNzQtZTc3Yy00ZmUxLTgyYzAtMzlhYjhmNzFmYzUzIiwiZXhwIjoxNTMyMzU2NjY5LCJpc3MiOiJUZXN0LmNvbSIsImF1ZCI6IlRlc3QuY29tIn0.8hwQ3H9V8mdNYrFZSjbCpWSyR1CNyDYHcGf6GqqCGnY"    
    }  

Now, we will try to get the list of values by passing this token into the authentication HTTP header. Following is my Action method definition.
    [HttpGet]    
    [Authorize]    
    public ActionResult<IEnumerable<string>> Get()    
    {    
        return new string[] { "value1", "value2", "value3", "value4", "value5" };    
    }  

    Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJKaWduZXNoIFRyaXZlZGkiLCJlbWFpbCI6InRlc3QuYnRlc3RAZ21haWwuY29tIiwiRGF0ZU9mSm9pbmciOiIwMDAxLTAxLTAxIiwianRpIjoiYzJkNTZjNzQtZTc3Yy00ZmUxLTgyYzAtMzlhYjhmNzFmYzUzIiwiZXhwIjoxNTMyMzU2NjY5LCJpc3MiOiJUZXN0LmNvbSIsImF1ZCI6IlRlc3QuY29tIn0.8hwQ3H9V8mdNYrFZSjbCpWSyR1CNyDYHcGf6GqqCGnY 

Handle Claims with JWT
Claims are data contained by the token. They are information about the user which helps us to authorize access to a resource. They could be Username, email address, role, or any other information. We can add claims information to the JWT so that they are available when checking for authorization.
 
In the above example, if we want to pass the claims to our token then the claim information needs to add GenerateJSONWebToken method of Login controller. In the following example, I have added a username, email address, and date of joining as claimed into the token.

    private string GenerateJSONWebToken(UserModel userInfo)    
    {    
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));    
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);    
        
        var claims = new[] {    
            new Claim(JwtRegisteredClaimNames.Sub, userInfo.Username),    
            new Claim(JwtRegisteredClaimNames.Email, userInfo.EmailAddress),    
            new Claim("DateOfJoing", userInfo.DateOfJoing.ToString("yyyy-MM-dd")),    
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())    
        };    
        
        var token = new JwtSecurityToken(_config["Jwt:Issuer"],    
            _config["Jwt:Issuer"],    
            claims,    
            expires: DateTime.Now.AddMinutes(120),    
            signingCredentials: credentials);    
        
        return new JwtSecurityTokenHandler().WriteToken(token);    
    }   

The claims are an array of key-value pair. The keys may be values of a JwtRegisteredClaimNames structure (it provides names for public standardized claims) or custom name (such as DateOfJoining in above example).
 
This claims can be used to filter the data. In the following example, I have to change the list of values if the user spends more than 5 years with the company.
    [HttpGet]    
    [Authorize]    
    public ActionResult<IEnumerable<string>> Get()    
    {    
        var currentUser = HttpContext.User;    
        int spendingTimeWithCompany = 0;    
        
        if (currentUser.HasClaim(c => c.Type == "DateOfJoing"))    
        {    
            DateTime date = DateTime.Parse(currentUser.Claims.FirstOrDefault(c => c.Type == "DateOfJoing").Value);    
            spendingTimeWithCompany = DateTime.Today.Year - date.Year;    
        }    
        
        if(spendingTimeWithCompany > 5)    
        {    
            return new string[] { "High Time1", "High Time2", "High Time3", "High Time4", "High Time5" };    
        }    
        else    
        {    
            return new string[] { "value1", "value2", "value3", "value4", "value5" };    
        }    
    }   

JWT is very famous in web development. It is an open standard that allows transmitting data between parties as a JSON object in a secure and compact way. In this article, we will learn how to generate and use JWT with ASP.NET core application.



European ASP.NET Core Hosting :: Working With AutoMapper

clock December 22, 2020 09:10 by author Peter

This article provides some guidelines on how to use AutoMapper in C#.
We have come across a lot of situations when we need to copy data from one object to another. Normally, we follow the below mentioned approach to achieve this. Let’s say we have two classes declared as below,

First Class:
    public class UserObject  
       {  
           public int Id;  
           public string Name;  
           public string Address;  
       }  

Second Class:
    public class UserAnotherObject  
    {  
        public int Id;  
        public string Name;  
        public string Address;  
    }
 

Now, if an object of the first class have some data in it and we want to copy that data to an object of the second class, we would be using the following approach for this:
    UserObject uObj = new UserObject();                         //an object of First Class  
               UserAnotherObject uAnotherObj = new UserAnotherObject();    //an object of Second   
      
               uAnotherObj.Id = uObj.Id;  
               uAnotherObj.Name = uObj.Name;  
               uAnotherObj.Address = uObj.Address;   


This is undoubtedly a tedious and repetitive task in case the object has a lot of properties.

Now, to overcome this task we can use a Nuget Package called the Auto-Mapper. Its working is quite simple to explain, in that it creates copy of one object into another but a bit automatically on the basis of Datatypes and names of the properties.

In order to use AutoMapper we would have to install it first from the Nuget Package Manager as pictorially explained below:

Step 1: Go to Manage NuGet Packages.. option in the project.
 

Step 2: Search and install AutoMapper to your project.

Step 3: Include the required Library on the page.
    using AutoMapper.Mappers;   

Now that this been done, we create a Mapper between the UserObject class and UserAnotherObject class. This will be done using the following line.

    AutoMapper.Mapper.CreateMap<UserObject, UserAnotherObject>();   

After creating this Mapper, let’s put some data into the object of the class UserObject that we made above,
    uObj.Id = 123;  
    uObj.Name = "Peter";  
    uObj.Address = "London";  


Now that we have the data, let’s copy the data from the object of UserObject class into the object of class UserAnotherObject using the AutoMapper that we just created above.
    uAnotherObj = AutoMapper.Mapper.Map<UserAnotherObject>(uObj);   

This copies the complete data from the uObj object to uAnotherObj object.

Note:
We need to keep it in mind that Auto Mapper copies data to properties in the target object with the same name as the properties name in the source object. The name should be the same but NOT CASE-SENSITIVE i.e. the name in source object can be “id” and that in the target object can be “ID”.



European ASP.NET Core Hosting :: ASP.NET Core 2.0 Structured Logging

clock December 15, 2020 07:28 by author Peter

In this tutorial, I will show you how to work with structured logging in ASP.NET Core and Serilog. Let's start with logging, add NuGet packages:
    Serilog.AspNetCore
    Serilog.Sinks.Literate
    Serilog.Sinks.Seq


In Program.cs, configure Serilog using its LoggerConfiguration class and storing an instance of ILogger (returned by CreateLogger) in Serilog’s static Log class.
    public static void Main(string[] args)  
          {  
              Log.Logger = new LoggerConfiguration()  
                          .WriteTo.LiterateConsole()  
                          .CreateLogger();  
      
             BuildWebHost(args).Run();  
          }  
      
          public static IWebHost BuildWebHost(string[] args) =>  
              WebHost.CreateDefaultBuilder(args)  
                  .UseStartup<Startup>()  
                  .UseSerilog()  
                  .Build();  


Using the ILogger is the same process as described in our previous post, however, with Serilog we can do structured logging.
    public async Task Invoke(HttpContext context)  
        {  
            var message = new  
            {  
                GreetingTo = "James Bond",  
                GreetingTime = "Morning",  
                GreetingType = "Good"  
            };  
            this.logger.LogInformation("Inoke executing {@message}", message);  
      
            await context.Response.WriteAsync("Hello Logging!");  
      
            this.logger.LogInformation(  
                "Inoke executed by {developer} at {time}", "Tahir", DateTime.Now);  
        }

Running the application will show messages in Console window.

Structured logging is a technique to include semantic information as part of the messages being logged. This helps ‘machine readability’ of these messages and tools can be written to analyze raw log messages and produce interesting information.

Serilog uses message template, similar to string.Format() in .NET. Few interesting aspects of template syntax are,
    Use {} to enclose property names e.g. {developer} in above solution. These will be stored as metadata and can be queried using structured data storage (e.g. Seq, Azure).
    Use @ to preserve object structure e.g. in solution above the anonymous object is serialized into JSON representation.

Enrichers
In Serilog, enrichers are used to attach information to every log event that can then be used by structured data storage (e.g. Seq, Azure) for viewing and filtering. A simple way to do this is by using .Enrich.WithProperty() when configuring Serilog,
    Log.Logger = new LoggerConfiguration()  
                               .Enrich.WithProperty("ApiVersion", "1.2.5000")  
                               .WriteTo.LiterateConsole()  
                               .CreateLogger();  
As we saw in the previous post, a category can be attached to the logged messages, which normally is the fully qualified name of the class. This information could be used by structured data storage (e.g. Seq, Azure). Serilog provides this mechanism by attaching Context via ForContext() method,
    Log.Logger = new LoggerConfiguration()  
                        .Enrich.WithProperty("ApiVersion", "1.2.5000")  
                        .WriteTo.LiterateConsole()  
                        .CreateLogger()  
                        .ForContext<HelloLoggingMiddleware>();  


Sinks
Sinks in Serilog refer to destination of log messages e.g. file, database or console (in our example). There are several sinks available (refer to link below). I’ll use Seq as an example sink to show how all the metadata we’ve added is available in a structured storage,
    Log.Logger = new LoggerConfiguration()  
                              .Enrich.WithProperty("ApiVersion", "1.2.5000")  
                              .WriteTo.LiterateConsole()  
                              .WriteTo.Seq("http://localhost:5341")  
                              .CreateLogger()  
                              .ForContext<HelloLoggingMiddleware>(); 

Notice how data we added via enricher, context and custom object appears as key/value pairs. This can now be used for filtering data and creating dashboards within Seq.



European ASP.NET Core Hosting :: How to Write Testable Code in .NET?

clock December 8, 2020 08:15 by author Peter

In this article, I give a brief introduction to writing testable code. Although I have described and used samples in the context of .NET, the high-level principles of writing testable code applies to most of the programming language.  
 
What is Testable Code?
Testable code refers to the loosely coupled code where code does not directly depend on internal/external dependencies, so we can easily replace the real dependencies (sometimes referred to as real service) with mock services in test cases. For example, if my code calls a method GetProductInfo() which is connecting to a real database, fetching the product information, and returning to the main method. To test my main method functionality without actually connecting to the real database, I can write a test that uses a mock service to get product data.
 
While it might seem a little confusing at this point, it is actually very simple when you see a working example of it.
 
Why is it important to write testable code?
Writing testable code is crucial, as it helps you to identify and resolve the potential problems/ bugs in the early development stage instead of getting issues in UAT or production when working with real services. Also, testing the fake services is fast compared to testing real services. For example, connecting to a real database is more time consuming than testing with fake data in mock service.
 
How to write testable code
Writing testable code is all about dependency management. If we are writing code using the SOLID principles, then our code will already be loosely coupled and in compliance with testing standards. While writing testable code, our main objective is to identify the dependencies and moving the instantiation of those dependencies outside of our code. When we create an object of the class using a new keyword inside the current class, then this class directly depends on the class whose object we are creating. For example, in the below code ProcessProduct class creating the object of DBService class. Hence DBService class is the dependency and ProcessProduct class depends directly on DBService.
    class Product  
        {  
            public int Id { get; set; }  
            public string Name { get; set; }  
            public string Category { get; set; }  
            public float Price { get; set; }  
      
        }  
        class ProcessProduct  
        {  
            public void DisplayProduct()  
            {  
                DBService dbService = new DBService();  
                var product = dBService.getProduct();  
                Console.WriteLine($" Product Name: { product.Name } Category: { product.Category } Price: { product.Price }");  
            }  
              
        }  
      
        class DBService  
        {  
            public Product getProduct() {  
                throw new NotImplementedException("Get product from database");  
            }  
        }  


To make this code loosely coupled, we will use a very popular design pattern called dependency injection. There are several ways to implement dependency injection which is itself a very wide topic. So to keep this article simple, I will use one of the ways to implement dependency injection-  Dependency injection using Constructor.
 
In this method, instead of creating the object of DBService inside ProcessProduct, we will inject the object through the constructor of the dependent class and save it in a private variable as shown in the below code:
    class Product  
    {  
        public int Id { get; set; }  
        public string Name { get; set; }  
        public string Category { get; set; }  
        public float Price { get; set; }  
      
    }  
    class ProcessProduct  
    {  
        private IDBservice _dbService;  
      
        public ProcessProduct(IDBservice dbService)  
        {  
            _dbService = dbService;  
        }  
        public void DisplayProduct()  
        {  
            var product = _dbService.getProduct();  
            Console.WriteLine($" Product Name: { product.Name } Category: { product.Category } Price: { product.Price }");  
        }  
          
    }  
      
    interface IDBservice {  
         Product getProduct();  
    }  
      
    class DBService : IDBservice  
    {  
        public Product getProduct() {  
            throw new NotImplementedException("Get product from database");  
        }  
    }  


We have also created an interface IDBService in the above example and declared the object of DBService using this interface. By using this interface, we allow any class’object that implements the IDBService interface to inject through the constructor.
 
Below is an example of passing a mock class object for testing.
    class ProcessProduct  
        {  
            private IDBservice _dbService;  
      
            public ProcessProduct(IDBservice dbService)  
            {  
                _dbService = dbService;  
            }  
            public void DisplayProduct()  
            {  
                var product = _dbService.getProduct();  
                Console.WriteLine($" Product Name: { product.Name } Category: { product.Category } Price:  { product.Price }");  
            }  
      
        }  
      
        interface IDBservice {  
            Product getProduct();  
        }  
      
        class MockDBService : IDBservice  
        {  
            public Product getProduct()  
            {  
                return new Product()  
                {  
                    Id = 2124,  
                    Name = "Eggs",  
                    Category = "Food",  
                    Price = 2.23m  
                };  
      
            }  
      
        }  
      
        class TestProcessProduct  
        {  
            void Test()  
            {  
                ProcessProduct processProduct = new ProcessProduct(new MockDBService());  
                processProduct.DisplayProduct();  
            }  
        }  


In this example, we want to test the ProcessProduct class to display product information without actually connecting to the real database. To achieve this, instead of injecting the DBService class object we are injecting the MockDBService class object. And because of dependency injection, we do not need to do any changes in the ProcessProduct class. Hence, this code is loosely coupled and testable.

 



European ASP.NET Core Hosting :: Remoting in .NET

clock November 24, 2020 08:30 by author Peter

Distributed computing is an integral part of almost every software development. Before .Net Remoting, DCOM was the most used method of developing distributed application on Microsoft platform. Because of object oriented architecture, .NET Remoting replaces DCOM as .Net framework replaces COM.
 
Benefits of Distributed Application Development
 
Fault Tolerance: Fault tolerance means that a system should be resilient when failures within the system occur.
 
Scalability: Scalability is the ability of a system to handle increased load with only an incremental change in performance.
 
Administration: Managing the system from one place.
 
In brief, .NET remoting is an architecture which enables communication between different application domains or processes using different transportation protocols, serialization formats, object lifetime schemes, and modes of object creation. Remote means any object which executes outside the application domain. The two processes can exist on the same computer or on two computers connected by a LAN or the Internet. This is called marshalling (This is the process of passing parameters from one context to another.), and there are two basic ways to marshal an object:
 
Marshal by value: the server creates a copy of the object passes the copy to the client.
 
Marshal by reference: the client creates a proxy for the object and then uses the proxy to access the object.
 
Comparison between .NET Remoting and Web services


Architecture

Remote objects are accessed thro channels. Channels are Transport protocols for passing the messages between Remote objects. A channel is an object that makes communication between a client and a remote object, across app domain boundaries. The .NET Framework implements two default channel classes, as follows:
 
HttpChannel: Implements a channel that uses the HTTP protocol. TcpChannel: Implements a channel that uses the TCP protocol (Transmission Control Protocol). Channel take stream of data and creates package for a transport protocol and sends to other machine. A simple architecture of .NET remoting is as in Fig 1.


As Fig.1 shows, Remoting system creates a proxy for the server object and a reference to the proxy will be returned to the client. When client calls a method, Remoting system sends request thro the channel to the server. Then client receives the response sent by the server process thro the proxy.
 
Example
Let us see a simple example which demonstrates .Net Remoting. In This example the Remoting object will send us the maximum of the two integer numbers sent.
Creating Remote Server and the Service classes on Machine 1: Please note for Remoting support your service (Remote object) should be derived from MarshalByRefObject.
    using System;  
    using System.Runtime.Remoting.Channels; //To support and handle Channel and channel sinks  
    using System.Runtime.Remoting;  
    using System.Runtime.Remoting.Channels.Http; //For HTTP channel  
    using System.IO;  
    namespace ServerApp {  
      public class RemotingServer {  
        public RemotingServer() {  
          //  
          // TODO: Add constructor logic here  
          //  
        }  
      }  
      //Service class  
      public class Service: MarshalByRefObject {  
        public void WriteMessage(int num1, int num2) {  
          Console.WriteLine(Math.Max(num1, num2));  
        }  
      }  
      //Server Class  
      public class Server {  
        public static void Main() {  
          HttpChannel channel = new HttpChannel(8001); //Create a new channel  
          ChannelServices.RegisterChannel(channel); //Register channel  
          RemotingConfiguration.RegisterWellKnownServiceType(typeof Service), "Service", WellKnownObjectMode.Singleton);  
        Console.WriteLine("Server ON at port number:8001");  
        Console.WriteLine("Please press enter to stop the server.");  
        Console.ReadLine();  
      }  
    }  
    }


Save the above file as ServerApp.cs. Create an executable by using Visual Studio.Net command prompt by, csc /r:system.runtime.remoting.dll /r:system.dll ServerApp.cs
 
A ServerApp.Exe will be generated in the Class folder.
 
Run the ServerApp.Exe will give below message on the console
 
Server ON at port number:8001
 
Please press enter to stop the server.
 
In order to check whether the HTTP channel is binded to the port, type http://localhost:8001/Service?WSDL in the browser. You should see a XML file describing the Service class.
 
Please note before running above URL on the browser your server (ServerApp.Exe should be running) should be ON.
 
Creating Proxy and the Client application on Machine 2
 
SoapSuds.exe is a utility which can be used for creating a proxy dll.
 
Type below command on Visual studio.Net command prompt.
 
soapsuds -url:http://< Machine Name where service is running>:8001/Service?WSDL -oa:Server.dll
 
This will generates a proxy dll by name Server.dll. This will be used to access remote object.
 
Client Code
    using System;  
    using System.Runtime.Remoting.Channels; //To support and handle Channel and channel sinks  
    using System.Runtime.Remoting;  
    using System.Runtime.Remoting.Channels.Http; //For HTTP channel  
    using System.IO;  
    using ServerApp;  
    namespace RemotingApp {  
      public class ClientApp {  
        public ClientApp() {}  
        public static void Main(string[] args) {  
          HttpChannel channel = new HttpChannel(8002); //Create a new channel  
          ChannelServices.RegisterChannel(channel); //Register the channel  
          //Create Service class object  
          Service svc = (Service) Activator.GetObject(typeof(Service), "http://<Machine name where Service running>:8001/Service"); //Localhost can be replaced by  
          //Pass Message  
          svc.WriteMessage(10, 20);  
        }  
      }  
    }


Save the above file as ClientApp.cs. Create an executable by using Visual Studio.Net command prompt by, csc /r:system.runtime.remoting.dll /r:system.dll ClientrApp.cs
 
A ClientApp.Exe will be generated in the Class folder. Run ClientApp.Exe , we can see the result on Running ServerApp.EXE command prompt.
 
In the same way we can implement it for TCP channel also.



ASP.NET Core Hosting - HostForLIFE.eu :: Progressive Retry for Network Calls

clock March 18, 2020 11:55 by author Peter

In today's mobile world, many calls across the internet or network could fail for many reasons. Some of the reasons could be the service is busy, the network is slow and many more. For these types of calls, it’s advisable to retry the call if there is an error. The current code base I am working on connects to Salesforce to retrieve and update data. Salesforce is one of those backend services that could experience these types of issues.

To help with these types of network issues, I have added a new method to my open-source code called ProgressiveRetry(). This method will try the call a given number of times. If there is an error, it will wait for a given milliseconds that increases with each error. Below is the code for this call.

    /// <summary> 
    /// Progressive retry for a function call. 
    /// </summary> 
    /// <param name="operation">The operation to perform.</param> 
    /// <param name="retryCount">The retry count (default 3).</param> 
    /// <param name="retryWaitMilliseconds">The retry wait milliseconds (default 100).</param> 
    /// <returns>System.Int32.</returns> 
    public static int ProgressiveRetry(Action operation, byte retryCount = 3,  
                                       int retryWaitMilliseconds = 100) 
    { 
        Encapsulation.TryValidateParam<ArgumentNullException>(operation != null); 
        Encapsulation.TryValidateParam<ArgumentOutOfRangeException>(retryCount > 0); 
        Encapsulation.TryValidateParam<ArgumentOutOfRangeException>(retryWaitMilliseconds > 0); 
      
        var attempts = 0; 
      
        do 
        { 
            try 
            { 
                attempts++; 
     
                 operation(); 
                return attempts; 
     
            } 
            catch (Exception ex) 
            { 
                if (attempts == retryCount) 
                { 
                    throw; 
                } 
      
                Debug.WriteLine(ex.GetAllMessages()); 
      
                Task.Delay(retryWaitMilliseconds * attempts).Wait(); 
                   } 
            } while (true); 
        } 
    } 


Here is example code on how to use ProgressiveRetry().
    var result = false; 
    var count = ExecutionHelper.ProgressiveRetry(() => 
    { 
        result = NetworkHelper.IsHostAvailable("wordpress.com"); 
    } 
    , retryCount: 3, retryWaitMilliseconds: 225); 
    Console.WriteLine($"Host available {result}. Tried call {count} times.");

HostForLIFE.eu ASP.NET Core Hosting

European best, cheap and reliable ASP.NET hosting with instant activation. HostForLIFE.eu is #1 Recommended Windows and ASP.NET hosting in European Continent. With 99.99% Uptime Guaranteed of Relibility, Stability and Performace. HostForLIFE.eu security team is constantly monitoring the entire network for unusual behaviour. We deliver hosting solution including Shared hosting, Cloud hosting, Reseller hosting, Dedicated Servers, and IT as Service for companies of all size.



ASP.NET Core Hosting - HostForLIFE.eu :: Using Redis To Delay Execution In ASP.NET Core

clock March 3, 2020 11:07 by author Peter

Due to some business requirements, many operations should not begin to execute right away;  they should begin after some seconds, minutes or hours. For example, say there is a task, and it contains two steps. When we finish the first step, the second step should begin after 5 minutes.
 

How can we solve this problem?
Thread.Sleep() and Task.Delay() is a very easy solution that we can use. But it may block our applictions.
 
And in this article, I will introduce a solution based on keyspace notifications of Redis.
 
Here we will use expired events to do it, but this solution also has some limitations , because it may have a significant delay. That means that delay of execution may have smoe error, and will not be very accurate!
 
Let's take a look at this solution.
 
Set Up Redis
Keyspace notifications is a feature available since 2.8.0, so the version of Redis should not be less than 2.8.0.
 
We should modify an important configuration so that we can enable this feature.
    ############################# Event notification ############################## 
    
    # Redis can notify Pub/Sub clients about events happening in the key space. 
    # This feature is documented at http://redis.io/topics/notifications 
    # 
    # ......... 
    # 
    #  By default all notifications are disabled because most users don't need 
    #  this feature and the feature has some overhead. Note that if you don't 
    #  specify at least one of K or E, no events will be delivered. 
    notify-keyspace-events "" 


The default value of notify-keyspace-events is empty, we should modify it to Ex.
    notify-keyspace-events "Ex" 

Then we can startup the Redis server.

Create Project
Create a new ASP.NET Core Web API project and install CSRedisCore.
    <Project Sdk="Microsoft.NET.Sdk.Web"> 
     
      <PropertyGroup> 
        <TargetFramework>netcoreapp3.1</TargetFramework> 
      </PropertyGroup> 
     
      <ItemGroup> 
        <PackageReference Include="CSRedisCore" Version="3.4.1" /> 
      </ItemGroup> 
     
    </Project> 

Add an interface named ITaskServices and a class named TaskServices.
    public interface ITaskServices 
    { 
        void SubscribeToDo(string keyPrefix); 
     
        Task DoTaskAsync(); 
    } 
     
    public class TaskServices : ITaskServices 
    { 
        public async Task DoTaskAsync() 
        { 
            // do something here 
            // ... 
     
            // this operation should be done after some min or sec 
            var taskId = new Random().Next(1, 10000); 
            int sec = new Random().Next(1, 5); 
     
            await RedisHelper.SetAsync($"task:{taskId}", "1", sec); 
            await RedisHelper.SetAsync($"other:{taskId + 10000}", "1", sec); 
        } 
     
        public void SubscribeToDo(string keyPrefix) 
        { 
            RedisHelper.Subscribe( 
                ("__keyevent@0__:expired", arg => 
                    { 
                        var msg = arg.Body; 
                        Console.WriteLine($"recive {msg}"); 
                        if (msg.StartsWith(keyPrefix)) 
                        { 
                            // read the task id from expired key 
                            var val = msg.Substring(keyPrefix.Length); 
                            Console.WriteLine($"Redis + Subscribe {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} begin to do task {val}"); 
                        } 
                    }) 
            ); 
        } 
    } 


As you can see, we set a redis key with expiration. The expiration is the delay time.
 
For the delay execution, we can find it in SubscribeToDo method. It subscribes a channel named __keyevent@0__:expired.
 
When a key is expired, redis server will publish a message to this channel, and subscribers will receive it.
 
After reciving the notification, the client will begin to do the job.
 
Before the client receives the notification, the delay job will not be executed, so that it can help us to do the job for a delay.
 
Here is the entry of this operation.
    [ApiController] 
    [Route("api/tasks")] 
    public class TaskController : ControllerBase 
    { 
        private readonly ITaskServices _svc; 
     
        public TaskController(ITaskServices svc) 
        { 
            _svc = svc; 
        } 
     
        [HttpGet] 
        public async Task<string> Get() 
        { 
            await _svc.DoTaskAsync(); 
            System.Console.WriteLine("done here"); 
            return "done"; 
        } 
    } 


Put the subscriber to a BackgroundService, so that it can run in the background.
    public class SubscribeTaskBgTask : BackgroundService 
    { 
        private readonly ILogger _logger; 
        private readonly ITaskServices _taskServices; 
     
        public SubscribeTaskBgTask(ILoggerFactory loggerFactory, ITaskServices taskServices) 
        { 
            this._logger = loggerFactory.CreateLogger<RefreshCachingBgTask>(); 
            this._taskServices = taskServices; 
        } 
     
        protected override Task ExecuteAsync(CancellationToken stoppingToken) 
        { 
            stoppingToken.ThrowIfCancellationRequested(); 
     
            _taskServices.SubscribeToDo("task:"); 
     
            return Task.CompletedTask; 
        } 
    } 


At last, we should register the above services in startup class.
    public class Startup 
    { 
        // ... 
         
        public void ConfigureServices(IServiceCollection services) 
        { 
            var csredis = new CSRedis.CSRedisClient("127.0.0.1:6379"); 
            RedisHelper.Initialization(csredis); 
     
            services.AddSingleton<ITaskServices, TaskServices>(); 
            services.AddHostedService<SubscribeTaskBgTask>(); 
     
            services.AddControllers(); 
        } 
    } 


Here is the result after running this application.

HostForLIFE.eu ASP.NET Core Hosting

European best, cheap and reliable ASP.NET hosting with instant activation. HostForLIFE.eu is #1 Recommended Windows and ASP.NET hosting in European Continent. With 99.99% Uptime Guaranteed of Relibility, Stability and Performace. HostForLIFE.eu security team is constantly monitoring the entire network for unusual behaviour. We deliver hosting solution including Shared hosting, Cloud hosting, Reseller hosting, Dedicated Servers, and IT as Service for companies of all size.



About HostForLIFE.eu

HostForLIFE.eu 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 2016 Hosting, ASP.NET Core 2.2.1 Hosting, ASP.NET MVC 6 Hosting and SQL 2017 Hosting.


Tag cloud

Sign in