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 :: Minimal APIs In .NET 6

clock May 24, 2022 07:50 by author Peter

Today we will look at minimal APIs, a new feature in .NET 6. In the past, we created controllers in order to create an API which could be consumed by our clients. In .NET 6, a new feature called Minimal APIs has been introduced. This allows us to create an API with minimum code and plumbing. In fact, it is created directly in the Program.cs file. In .NET 6, the Startup.cs file is also not required anymore, and all initial plumbing is done inside the Program.cs file. We will create a simple Minimal API. So, let us begin.
Creating Minimal APIs

Let us start by opening Visual Studio 2022. I use the community edition which is free to download and use.

Select “Create a new project”.


Select “ASP.NET Core Web API”.


 

Remember to uncheck the highlighted option to use minimal APIs


Open the “Program.cs” file and update as below,

var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment()) {
    app.UseSwagger();
    app.UseSwaggerUI();
}
app.UseHttpsRedirection();
var summaries = new [] {
    "Freezing",
    "Bracing",
    "Chilly",
    "Cool",
    "Mild",
    "Warm",
    "Balmy",
    "Hot",
    "Sweltering",
    "Scorching"
};
app.MapGet("/weatherforecast", () => {
    var forecast = Enumerable.Range(1, 5).Select(index => new WeatherForecast(DateTime.Now.AddDays(index), Random.Shared.Next(-20, 55), summaries[Random.Shared.Next(summaries.Length)])).ToArray();
    return forecast;
}).WithName("GetWeatherForecast");
app.MapGet("/welcomemessage", () => {
    return "Welcome to Minimal APIs";
}).WithName("GetWelcomeMessage");
app.Run();
internal record WeatherForecast(DateTime Date, int TemperatureC, string ? Summary) {
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

Running the application
We now run the application and see the swagger page as below,

Here, we can try both the minimal APIs we have in our code.


 

In this article, we looked at creating a minimal API, a new feature in .NET 6. The example here was a simple one but it gives an idea of the concept and more complex APIs can be added. Probably for more complex APIs, refactoring into a separate file is a good idea. Happy coding!

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.

 



European ASP.NET Core Hosting :: Log Correlation In Microservices

clock May 17, 2022 07:45 by author Peter

Logging is one of the most important factors to trace any issue in the system. Multiple requests will reach the system at the same time. Each request will have its own way. In Microservices architecture the complexity will be more. The same request will get forwarded to multiple systems. Each layer in each system will add some logs to trace the issue. But we need to link all the logs belonging to the request. Then only we identify how the request is behaving on each system. To Link all the logs into a single link, that is why correlation is required.

There are multiple frameworks available to correlate all the logs.

Azure is providing azure app insights to link and showcase all the requests into a single pipeline. Kibana and Serilog also provide to link the logs. Here we are going to see to link the logs without any third-party components.
Setup the CorrelationId

The request will originate from any client application or any subsystem. Each request can be identified by its reuestId. So the origin system can send its requestId or we can create the Id in case the origin didn't send it.

public class CorrelationHeaderMiddleware {
    private readonly RequestDelegate _next;
    public CorrelationHeaderMiddleware(RequestDelegate next) {
        _next = next;
    }
    public async Task InvokeAsync(HttpContext context) {
        var correlationHeaders = context.Request.Headers["CorrelationId"];
        string correlationId;
        if (correlationHeaders.Any()) {
            correlationId = correlationHeaders[0];
        } else {
            correlationId = Guid.NewGuid().ToString();
            context.Request.Headers.Add("CorrelationId", correlationId);
        }
        context.Items["CorrelationId"] = correlationId;
        await _next(context);
    }
}

We have created new middleware that will fetch the correlation from the httpcontext or it will create the new id and set it into the request. Middleware is added into the request pipeline. All the requests will be processed and they will be added with CorrelationId.
Setup Logs

The request will get into multiple flows and add its logs. While adding the logs it will send the information alone to the log. While saving the logs we are fetching the CorrelationId from the request and adding it with the logs. So all the logs will get added with CorrelationId. After that, we can store the logs based on our format.

public class Logger: ILogType {
    private readonly ILogger < Logger > _logger;
    private readonly IHttpContextAccessor _httpContextAccessor;
    public Logger(ILogger < Logger > logger, IHttpContextAccessor httpContextAccessor) {
        _logger = logger;
        _httpContextAccessor = httpContextAccessor;
    }
    private string GetCorrelationId() {
        return _httpContextAccessor.HttpContext.Items["CorrelationId"] as string;
    }
    public void ProcessError(string message) {
        _logger.LogError(GetCorrelationId() + ": " + message);
    }
    public void ProcessLogs(string message) {
        _logger.LogInformation(GetCorrelationId() + ": " + message);
    }
    public void ProcessWarning(string message) {
        _logger.LogWarning(GetCorrelationId() + ": " + message);
    }
}


The request needs to be forwarded to multiple systems to complete it. To send the request to multiple subsystems, we need to use any protocol like HTTP or AMQP. In this case, we need to attach the CorrelationId as a header with the corresponding protocol. The corresponding subsystem will read the id and logs the stuff.

All the log data will be get stored in the same or multiple systems. But CorrelationId will be the key to linking all the logs. Using this id we can fetch the request flow order. It will help to understand the system's flow as well as to trace any issue in the system's behavior. Adding more logs will increase the system space. We need to ensure the log type on each log also.

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.

 



European ASP.NET Core Hosting :: How To Migrate From .NET Core 3.1 To .NET 6.0?

clock May 9, 2022 07:54 by author Peter


.Net 6 is an LTS (Long Tern Support) Version. It will be supported for three years. It is the latest long-term support release. The previous version, .Net Core 3.1 support will be finalized in December 2022, and support for .Net 5 will be ended May 2022. This article describes how to upgrade the solution from .Net Core 3.1 to .NET 6.0 with an example of Console application and .Net Core 3.1 class library project. Upgrading console applications and class library project is almost similar. However, there is some difference between Upgrading ASP.NET Core Web app.

Prerequisites
    Visual Studio 2022

Step 1 - Upgrade the Target Framework
Right-click on the project then go to properties and change the target.

Then select the target framework to .NET 6.0 as depicted below and save it.

Alternatively from project.csproj file you can change the target Framework from netcore3.1 to net 6.0 as shown below.

<PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>

Step 2 - Updating Package references
Update Package references if there are any. Go to Project.csproj file and upgrade packages as shown below.


For an instance upgrade the package Microsoft.AspNetCore.JsonPatch and Microsoft.EntityFrameworkCore.Tools and so on from version 3.1.6 to 6.0.0 as illustrated below.

<ItemGroup>
-    <PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="3.1.6" />
-    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.6" />
-    <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="3.1.6" />
-    <PackageReference Include="System.Net.Http.Json" Version="3.2.1" />
+    <PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="6.0.0" />
+    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.0" />
+    <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="6.0.0" />
+    <PackageReference Include="System.Net.Http.Json" Version="6.0.0" />
</ItemGroup>

Step 3 - Delete obj and bin folder

You may need to delete the existing bin and obj folder. Go to the respective project directory and delete those folders. Additionally, you can delete the NuGet cache by running the below command.
dotnet nuget locals --clear all

Step 4 - Build the solution

Then build the solution and see whether there are errors or your app is built successfully. If there are errors based on an error message correct the code and rebuild the solution. On successful build of the application, your app is upgraded to the .NET 6.0.

The above three steps are required to follow to upgrade the class library and console application to migrate from .NET Core 3.1 to .NET 6.0.

On the other hand, to update the Asp.NET Core 3.1 and Blazor application you need to follow more steps in addition to the above three.

Following are some changes you need to consider for upgrading ASP.NET Core 3.1 web application to .NET6

    Minimal hosting and statup.cs file changes
    Note that minimal hosting unifies the Startup.cs and Program.cs to a single Program.cs file. Moreover, the ConfigureServices and Configure are no longer used in .NET6.0.
    Model Binding
    Datetime values are model bound as UTC timezone. For applications build on ASP .NET Core 5 and later, model binding binds the DateTime as UTC timezone whereas, in applications built using ASP.NET Core 3.1 and earlier, Datetime values were model bound as local time and the timezone was determined by the server.
    Docker Image
    If your app uses docker then you need to pull a Docker image that consists of ASP.NET Core 6.0 runtime as well. The below command can be used for that.

docker pull mcr.microsoft.com/dotnet/aspnet:6.0

You can check the details on the below document.

In this article, we have learned how to upgrade the project from .NET Core 3.1 to .NET 6.0 with an example of a console application. This article will be useful while migrating your application from .NET Core 3.1 to .NET 6.0 without encountering any issues. Best of luck to migrate your application before the end of support.

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.



European ASP.NET Core Hosting :: Introduction To Postman

clock April 26, 2022 09:43 by author Peter

In this step-by-step article series, we are going to learn about the Postman. This article covers the following topics:
    What is Postman?
    Features
    How to make a first GET API request?

What is Postman?
It is an API platform to build and use APIs. You can create better or faster APIs with the help of an API repository, tools, intelligence, workspace, and integrations. It is built on open-source technologies.
    API repository – Using the central platform you can easily store, catalog, and collaborate all your API-related stuff like test cases, specifications, documentation, etc.
    Tools – It provides various sets of API tools to accelerate the API lifecycle like design, testing, mocking, documentation, etc.
    Intelligence – It provides an advanced level of intelligence and insights about the API operations alerts, search, security warnings, reporting, etc.
    Workspace – It helps you to organize your API work and collaborate across the world. There are three different workspaces – personal, team and public.
    Integrations – It is a most important tool in the software development pipeline to go with API-first practices. You can integrate postman into the code repositories, CI/CD pipeline, you can build your integrations using the postman API, etc.

So, what are you waiting for go and download the Postman, start designing, testing, and documenting API - https://www.postman.com/downloads/
It comes in two types of versions desktop app and a web version.

Features
It provides a bunch of cool features some are as below:

Request
    Create, send and save REST, SOAP, or GraphQL requests.
    Save request to collections.
    Send a request through a proxy server, etc.

Response

    View status code, response time, headers, and size.
    View response body in raw and pretty view.
    Save response as an example, etc.

Variables
    Built-in support for variables.
    Create and set variables for collections, environments, and global.
    Dynamic variables for dummy data, etc.

Scripts and Postman sandbox

    Write scripts at the collection, folder, or request level.
    Write pre or post-request scripts for after or before the request.
    Use scripts to send the request, etc.

Collaboration
    Create unlimited personal or team workspaces.
    Create a private workspace(Enterprise only).
    You can set roles, and invite members, etc.

Collections
    They are executable API descriptions.
    You can organize and keep track of related requests.
    You can share collections to workspaces, etc.

How to make a first GET API request?
I hope you have installed any one of the postman versions on your system desktop, or web version, follow the below steps to make your first request in postman:

Step 1

Open the Postman app.(Using the desktop version)

Once you open the Postman, you will see the output as above screenshot. This is what the postman UI looks like and you can see the various options such as Sign-in, Create Account, Home, Workspace, Reports, Explore, Collections, API, Environments, etc. these options we are going learn in upcoming articles.

Step 2

Click on the plus icon button as shown below screenshot:

It will open the request pop-up in the same window like the below screenshot:


Step 3
Next, enter the URL for our first GET request as ‘https://api.github.com/users/jsgund’ and click on send button as below screenshot:

Step 4
Once you click on send button you will get the response as below screenshot:

In this example, we are accessing the GitHub to get users to request by id and at the end, you can see my GitHub login id as “jsgund”. In the response, you will get the details of the GitHub login id “jsgund”, properties such as name, id, imageUrl, followers URL, etc.

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.



European ASP.NET Core Hosting :: How To Handle Nullable Reference In .NET 6?

clock April 20, 2022 09:08 by author Peter

As we know there are so many changes in .Net 6 and C# 10. In this article, we will explore one of the changes in C#10 on the writing properties and objects of a class. More details on new features or changes in .NET 6 can be found in the previous article Features of .NET 6.

In .NET 5 and lower versions, we can write properties and objects of class like below.
public class Person {
    public string Name;
    public string Sex;
    public string Address;
    public string Email;
}


However, it has been changed in C# 10. If we write like earlier, the compiler shows a warning like below.

So, to handle this there are different ways. In this article, we will learn those methods to handle the warnings.

Let’s create a console application in .NET6
Create Console Application in .NET 6

Step 1
Open Visual Studio 2022 and click Create a new project.

Step 2
Select Console App and click Next.

Step 3
Give the project name and location of the project.

Step 4
Select framework: .NET 6.0 (Long-term support).

This creates the console app which looks like below.

Default Program.cs file is listed below.

// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");


Now, let’s compile and run the program. when you run it, it will display the “Hello, World!” message. Now, we will move to the main point of this write-up.
Add a New Class

Right-click on project -> add class and give the name of the class as Person.

Person class contains the following properties. Write the below code.

public class Person {
    public string Name;
    public string Sex;
    public string Address;
    public string Email;
    public string Description {
        get;
        set;
    }
}

Then, you will get the warning as depicted below.

Let’s jump to different ways to handle the compiler warning.

Method 1
Changing the project file(project.csproj). You disable or remove the nullable in csproj file so that it will not show the warning message. Right-click on the project and Edit the project as shown below.

Then project file will be opened, and you can simply disable Nullable as illustrated below.

Inside the PropertyGroup change Nullable to disable as shown below or remove that line.
<Nullable>disable</Nullable>

Method 2: Giving default value

You can assign it in different ways:
    a default value
    reasonable default value as a string.Empty or
    “”

as illustrated in the below code.

public class Person {
    public string Name = "Default Value";
    public string Sex = "";
    public string Address = "";
    public string Email = string.Empty;
    public string Description {
        get;
        set;
    } = string.Empty;
}


Method 3
Another way to handle it is to make properties nullable reference type by simply using "?" as demonstrated below.
public class Person {
    public string ? Name;
    public string ? Sex;
    public string ? Address;
    public string ? Email;
    public string ? Description {
        get;
        set;
    }
}


Overall, in .NET 6 we should define the variable, properties, or field explicitly as either nullable or non-nullable with a reference type.

Summary

In this article, I have created a console application in .NET 6 and demonstrated issues with fields or properties if we code as in .NET 5 or lower. Additionally, the article has provided the different methods to handle such situations in .NET 6. Hence, these are the three ways to handle the nullable, non-nullable field and properties in .NET 6. I hope you have got an idea about it.

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.

 



European ASP.NET Core Hosting ::How To Add KendoUI Grid In Angular With .NET Core API With Multilayer Architecture

clock April 19, 2022 08:42 by author Peter

In this part of the article, we learn how to create .Net Core API for the KendoUI Curd Operations. Also here we learn the multilayer architecture of .Net Core API.

Preconditions
    Basic knowledge of Angular CLI
    Basic knowledge of Dot Net core
    Basic knowledge of SQL server
    Bootstrap
    Node.js
    V.S. Code,Visual Studio

We cover the below things

    Create Dot Net Core API
    Create Angular application
    Angular Routing
    Kendo UI setup
    Bind KendoUI grid

Add this script to the database to create table.

Create Database KendoUIDb
CREATE TABLE [dbo].[Users](
    [UserId] [int] primary key IDENTITY(1,1) NOT NULL,
    [UserName] [nvarchar](56) NOT NULL,
    [FullName] [nvarchar](200) NULL,
    [EmailId] [nvarchar](200) NULL,
    [Contactno] [nvarchar](10) NULL,
    [Password] [nvarchar](200) NULL,
    [Createdby] [int] NULL,
    [CreatedDate] [datetime] NULL,
    [Status] [bit] NULL,
    [imagename] [varchar](100) NULL
)


Now let's create a .Net API Project in visual studio using the following steps.

We have to create four libraries mentioned in the below image.

Now we add the references of project for interconnect.


Now we will create the following files according to the below images.


Now add the following packages from nuget package manager.

Now add the below code in the Custom.cs file.

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.FileProviders;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using KendoApi.ViewModels;
namespace KendoApi.Common {
    public class Custom {
        public static string UploadImage(UsersViewModel users, IHostingEnvironment _environment, string routePath = "") {
            string filesName = "";
            if (users.file != null) {
                //Getting FileName
                var fileName = Path.GetFileName(users.file.FileName);
                //Assigning Unique Filename (Guid)
                var myUniqueFileName = Convert.ToString(Guid.NewGuid());
                //Getting file Extension
                var fileExtension = Path.GetExtension(fileName);
                // concatenating  FileName + FileExtension
                var newFileName = String.Concat(myUniqueFileName, fileExtension);
                filesName = newFileName;
                // Combines two strings into a path.
                var filepath = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "Images")).Root + $ @ "\{newFileName}";
                using(FileStream fs = System.IO.File.Create(filepath)) {
                    users.file.CopyTo(fs);
                    fs.Flush();
                }
            } else {
                if (users.imagename == null) {
                    filesName = "";
                } else {
                    filesName = users.imagename != "" ? users.imagename.Replace("http://" + routePath + "/images/", "") : null;
                }
            }
            return filesName;
        }
    }
}

Now add the below code in the CustomExceptionFilterAttribute.cs file.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Routing;
namespace KendoApi.Common {
    public class CustomExceptionFilterAttribute: ExceptionFilterAttribute {
        private readonly IHostingEnvironment _hostingEnvironment;
        public CustomExceptionFilterAttribute(IHostingEnvironment hostingEnvironment) {
            _hostingEnvironment = hostingEnvironment;
        }
        public override void OnException(ExceptionContext context) {
            string strLogText = "";
            Exception ex = context.Exception;
            context.ExceptionHandled = true;
            var objClass = context;
            strLogText += "Message ---\n{0}" + ex.Message;
            if (context.HttpContext.Request.Headers["x-requested-with"] == "XMLHttpRequest") {
                strLogText += Environment.NewLine + ".Net Error ---\n{0}" + "Check MVC Ajax Code For Error";
            }
            strLogText += Environment.NewLine + "Source ---\n{0}" + ex.Source;
            strLogText += Environment.NewLine + "StackTrace ---\n{0}" + ex.StackTrace;
            strLogText += Environment.NewLine + "TargetSite ---\n{0}" + ex.TargetSite;
            if (ex.InnerException != null) {
                strLogText += Environment.NewLine + "Inner Exception is {0}" + ex.InnerException;
                //error prone
            }
            if (ex.HelpLink != null) {
                strLogText += Environment.NewLine + "HelpLink ---\n{0}" + ex.HelpLink; //error prone
            }
            StreamWriter log;
            string timestamp = DateTime.Now.ToString("d-MMMM-yyyy", new CultureInfo("en-GB"));
            string errorFolder = Path.Combine(_hostingEnvironment.WebRootPath, "ErrorLog");
            if (!System.IO.Directory.Exists(errorFolder)) {
                System.IO.Directory.CreateDirectory(errorFolder);
            }
            // ReSharper disable once ConvertIfStatementToConditionalTernaryExpression
            if (!File.Exists($ @ "{errorFolder}\Log_{timestamp}.txt")) {
                log = new StreamWriter($ @ "{errorFolder}\Log_{timestamp}.txt");
            } else {
                log = File.AppendText($ @ "{errorFolder}\Log_{timestamp}.txt");
            }
            var controllerName = (string) context.RouteData.Values["controller"];
            var actionName = (string) context.RouteData.Values["action"];
            // Write to the file:
            log.WriteLine(Environment.NewLine + DateTime.Now);
            log.WriteLine("------------------------------------------------------------------------------------------------");
            log.WriteLine("Controller Name :- " + controllerName);
            log.WriteLine("Action Method Name :- " + actionName);
            log.WriteLine("------------------------------------------------------------------------------------------------");
            log.WriteLine(objClass);
            log.WriteLine(strLogText);
            log.WriteLine();
            // Close the stream:
            log.Close();
            if (!_hostingEnvironment.IsDevelopment()) {
                // do nothing
                return;
            }
            var result = new RedirectToRouteResult(new RouteValueDictionary {
                {
                    "controller",
                    "Errorview"
                }, {
                    "action",
                    "Error"
                }
            });
            // TODO: Pass additional detailed data via ViewData
            context.Result = result;
        }
    }
}

Now add the below code in the UserController.cs file.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using KendoApi.Common;
using KendoApi.Interface;
using KendoApi.Models;
using KendoApi.ViewModels;
using System.Web;
using Microsoft.AspNetCore.Hosting;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using AutoMapper;
namespace KendoApi.Controllers {
    //[Authorize]
    [Route("api/[controller]")]
    [ApiController]
    public class UserController: ControllerBase {
        private readonly IUsers _users;
        private readonly IHostingEnvironment _environment;
        private readonly IHttpContextAccessor _httpContextAccessor;
        public UserController(IUsers users, IHostingEnvironment environment, IHttpContextAccessor httpContextAccessor) {
            _users = users;
            _environment = environment;
            _httpContextAccessor = httpContextAccessor;
        }
        // GET: api/User
        [HttpGet]
        public IEnumerable < Users > Get() {
            var path = _httpContextAccessor.HttpContext.Request.Host.ToString();
            var UserData = _users.GetAllUsers(path);
            return UserData;
        }
        // GET: api/User/5
        [HttpGet]
        [Route("GetByUserName")]
        public Users GetByUserName(string username = "") {
            var path = _httpContextAccessor.HttpContext.Request.Host.ToString();
            var UserData = _users.GetByUserName(username, path);
            return UserData;
        }
        // GET: api/User/5
        [HttpGet("{id}", Name = "GetUsers")]
        public Users Get(int id) {
                var UserData = _users.GetUsersbyId(id);
                var password = EncryptionLibrary.DecryptText(UserData.Password);
                return UserData;
            }
            [HttpPost]
        public IActionResult Post([FromForm] UsersViewModel users) {
            var path = _httpContextAccessor.HttpContext.Request.Host.ToString();
            var Result = Custom.UploadImage(users, _environment, path);
            string imagepath = "http://" + path + "/images/";
            var _rootPath = _environment.WebRootPath;
            if (ModelState.IsValid) {
                if (_users.CheckUsersExits(users.UserName)) {
                    var response = new HttpResponseModel() {
                        StatusCode = (int) HttpStatusCode.Conflict,
                            data = ""
                    };
                    return Ok(response);
                } else {
                    var userId = this.User.FindFirstValue(ClaimTypes.Name);
                    var tempUsers = AutoMapper.Mapper.Map < Users > (users);
                    tempUsers.UserName = users.UserName;
                    tempUsers.imagename = users.file != null ? users.file.FileName : "";
                    tempUsers.CreatedDate = DateTime.Now;
                    tempUsers.imagename = Result;
                    tempUsers.Createdby = Convert.ToInt32(userId);
                    tempUsers.Password = EncryptionLibrary.EncryptText(users.Password);
                    _users.InsertUsers(tempUsers);
                    var response = new HttpResponseModel() {
                        StatusCode = (int) HttpStatusCode.OK,
                            data = imagepath + tempUsers.imagename
                    };
                    return Ok(response);
                }
            } else {
                var Results = ModelState.Values.ToList()[0].Errors.Count().ToString();
                var response = new HttpResponseModel() {
                    StatusCode = (int) HttpStatusCode.BadRequest,
                        data = ""
                };
                return Ok(Results);
            }
        }
        // PUT: api/User/5
        [HttpPut("{id}")]
        public IActionResult Put(int id, [FromForm] UsersViewModel users) {
            if (ModelState.IsValid) {
                var path = _httpContextAccessor.HttpContext.Request.Host.ToString();
                string imagepath = "http://" + path + "/images/";
                var Result = Custom.UploadImage(users, _environment, path);
                users.Password = EncryptionLibrary.EncryptText(users.Password);
                var tempUsers = AutoMapper.Mapper.Map < Users > (users);
                tempUsers.UserId = id;
                tempUsers.CreatedDate = DateTime.Now;
                tempUsers.imagename = Result;
                _users.UpdateUsers(tempUsers);
                var response = new HttpResponseModel() {
                    StatusCode = (int)(HttpStatusCode.OK),
                        data = imagepath + tempUsers.imagename
                };
                return Ok(response);
            } else {
                var response = new HttpResponseModel() {
                    StatusCode = (int) HttpStatusCode.BadRequest,
                        data = ""
                };
                return Ok(response);
            }
        }
        // DELETE: api/ApiWithActions/5
        [HttpDelete("{id}")]
        public HttpResponseMessage Delete(int id) {
            var result = _users.DeleteUsers(id);
            if (result) {
                var response = new HttpResponseMessage() {
                    StatusCode = HttpStatusCode.OK
                };
                return response;
            } else {
                var response = new HttpResponseMessage() {
                    StatusCode = HttpStatusCode.BadRequest
                };
                return response;
            }
        }
    }
}

Now add the below code in the MappingProfile.cs file.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using KendoApi.Models;
using KendoApi.ViewModels;

namespace KendoApi.Mappings
{
    public class MappingProfile : Profile
    {
        public MappingProfile()
        {

            CreateMap<UsersViewModel, Users>()
                .ForMember(dest => dest.UserName, opt => opt.MapFrom(src => src.UserName))
                .ForMember(dest => dest.Contactno, opt => opt.MapFrom(src => src.Contactno))
                .ForMember(dest => dest.EmailId, opt => opt.MapFrom(src => src.EmailId))
                .ForMember(dest => dest.FullName, opt => opt.MapFrom(src => src.FullName))
                .ForMember(dest => dest.Password, opt => opt.MapFrom(src => src.Password))
                .ForMember(dest => dest.UserId, opt => opt.MapFrom(src => src.Id))
                .ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status));
        }
    }
}

Now add the below code in the AppSettings.cs file.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace KendoApi.Models {
    public class AppSettings {
        public string Secret {
            get;
            set;
        }
    }
}

Now add the below code in the AppSettings.cs file.

{
    "AppSettings": {
        "Secret": "6XJCIEJO41PQZNWJC4RR"
    },
    "Logging": {
        "LogLevel": {
            "Default": "Warning"
        }
    },
    "AllowedHosts": "*",
    "ConnectionStrings": {
        "DatabaseConnection": "Data Source=DESKTOP-13P092J\\SA; UID=sa; Password=sa123;Database=KendoUIDb;"
    }
}

Now add the below code in the Startup.cs file.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AutoMapper;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using KendoApi.Common;
using KendoApi.Concrete;
using KendoApi.Interface;
using KendoApi.Mappings;
using KendoApi.Models;
namespace KendoApi {
    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.Configure < CookiePolicyOptions > (options => {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });
            #region MyRegion
            var connection = Configuration.GetConnectionString("DatabaseConnection");
            services.AddDbContext < DatabaseContext > (options => options.UseSqlServer(connection, b => b.UseRowNumberForPaging()));
            var appSettingsSection = Configuration.GetSection("AppSettings");
            services.Configure < AppSettings > (appSettingsSection);
            // configure jwt authentication
            var appSettings = appSettingsSection.Get < AppSettings > ();
            var key = Encoding.ASCII.GetBytes(appSettings.Secret);
            services.AddAuthentication(x => {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(x => {
                x.RequireHttpsMetadata = false;
                x.SaveToken = true;
                x.TokenValidationParameters = new TokenValidationParameters {
                    ValidateIssuerSigningKey = true,
                        IssuerSigningKey = new SymmetricSecurityKey(key),
                        ValidateIssuer = false,
                        ValidateAudience = false
                };
            });
            services.AddSingleton < IConfiguration > (Configuration);
            services.AddTransient < IUsers, UsersConcrete > ();
            services.AddSingleton < IActionContextAccessor, ActionContextAccessor > ();
            services.AddScoped < IUrlHelper > (implementationFactory => {
                var actionContext = implementationFactory.GetService < IActionContextAccessor > ().ActionContext;
                return new UrlHelper(actionContext);
            });
            #endregion
            services.AddSingleton < IHttpContextAccessor, HttpContextAccessor > ();
            services.AddHttpContextAccessor();
            // Start Registering and Initializing AutoMapper
            Mapper.Initialize(cfg => cfg.AddProfile < MappingProfile > ());
            services.AddAutoMapper();
            // End Registering and Initializing AutoMapper
            services.Configure < FormOptions > (options => {
                options.ValueCountLimit = 200; //default 1024
                options.ValueLengthLimit = 1024 * 1024 * 100; //not recommended value
                options.MultipartBodyLengthLimit = long.MaxValue; //not recommended value
            });
            services.AddMvc(options => {
                options.Filters.Add(typeof(CustomExceptionFilterAttribute));
            }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1).AddJsonOptions(options => {
                options.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver();
            });
            services.AddCors(options => {
                options.AddPolicy("CorsPolicy", builder => builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader().AllowCredentials().WithExposedHeaders("X-Pagination"));
            });
        }
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
            if (env.IsDevelopment()) {
                app.UseDeveloperExceptionPage();
            } else {
                app.UseExceptionHandler("/Home/Error");
            }
            app.UseStaticFiles();
            app.UseCookiePolicy();
            app.UseAuthentication();
            app.UseCors("CorsPolicy");
            app.UseMvc(routes => {
                routes.MapRoute(name: "default", template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

Now add the below code in the DatabaseContext.cs file.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using KendoApi.Models;
namespace KendoApi.Concrete {
    public class DatabaseContext: DbContext {
        public DatabaseContext(DbContextOptions < DatabaseContext > options): base(options) {}
        public DbSet < Users > Users {
            get;
            set;
        }
    }
}

Now add the below code in the UsersConcrete.cs file.

using System;
using System.Collections.Generic;
using KendoApi.Models;
using KendoApi.ViewModels;
namespace KendoApi.Interface {
    public interface IUsers {
        bool InsertUsers(Users user);
        bool CheckUsersExits(string username);
        Users GetUsersbyId(int userid);
        bool DeleteUsers(int userid);
        bool UpdateUsers(Users role);
        List < Users > GetAllUsers(string rootpath = "");
        bool AuthenticateUsers(string username, string password);
        Users GetByUserName(string username, string rootpath);
    }
}

Now add the below code in the Users.cs file.

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace KendoApi.Models {
    [Table("Users")]
    public class Users {
        [Key]
        public int UserId {
            get;
            set;
        }
        public string UserName {
            get;
            set;
        }
        public string FullName {
            get;
            set;
        }
        public string EmailId {
            get;
            set;
        }
        public string Contactno {
            get;
            set;
        }
        public string Password {
            get;
            set;
        }
        public int ? Createdby {
            get;
            set;
        }
        public string imagename {
            get;
            set;
        }
        public DateTime ? CreatedDate {
            get;
            set;
        }
        public bool Status {
            get;
            set;
        }
    }
}

Now add the below code in the HttpResponseModel.cs file.

using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace KendoApi.ViewModels {
    public class HttpResponseModel {
        public string data {
            get;
            set;
        }
        public int StatusCode {
            get;
            set;
        }
    }
}

Now add the below code in the UsersViewModel.cs file.

using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace KendoApi.ViewModels {
    public class UsersViewModel {
        public int Id {
            get;
            set;
        }
        public int UserId {
            get;
            set;
        }
        //[Required]
        public string UserName {
            get;
            set;
        }
        // [Required]
        public string FullName {
            get;
            set;
        }
        // [Required]
        public string EmailId {
            get;
            set;
        }
        // [Required]
        public string Contactno {
            get;
            set;
        }
        //   [Required]
        public string Password {
            get;
            set;
        }
        public bool Status {
            get;
            set;
        }
        [NotMapped]
        public IFormFile file {
            get;
            set;
        }
        public string imagename {
            get;
            set;
        }
        //     public List<IFormFile> files { get; set; }
    }
}

We have completed the API part, now in the next part we will work on binding the KendoUI Grid with API.

Summary
In this article, we learned how to create .Net core multilayer architecture for API with curd operations.



European ASP.NET Core Hosting :: Implement And Register Dependency Injection In ASP.NET Core/.NET 6

clock April 12, 2022 07:49 by author Peter

.NET core support build-in dependency injection(DI) which we can achieve by the below approaches:
    Constructor injection
    Property injection
    Method injection

In this article, we will learn

    Inject the dependency injection using first method(Constructor Injection)
    Register the interfaces and classes in the container class.

To start, few things are required:

    Visual studio 22
    .NET 6 (.NET Core)
    Swagger for testing the API

Before start, I would like to explain few points about the .NET 6 (.NET core new Version 6)

    On the creation of the Web API .NET Core project, Startup class is not present.
    Startup class gets merged with Program class.
    All configurations and services will be configured in the Program class.
    Build-in support of Swagger. (Tick the checkbox of "Enable OpenAPI support" while creating the project)

Let's go and start to implement the multiple interface in the .NET 6/.NET Core.

    Search and select an ASP .NET Core Web API project from the create a new project tab.
    Click next and add the project name
    Select .NET 6.0 as framework and click on the check box of "Enable OpenAPI support" as its build-in feature of swagger for testing the API.

Once the project gets created, then move it to the next step.

STEP 1 - Created interfaces – IEmployeeDetails and IDepartmentDetails

namespace Business;

public interface IEmployeeDetails
{
    public List<Employee> GetEmployee();
}
public interface IDepartmentDetails
{
    List<Department> getDepartmentDetails();
}

STEP 2 - Create service and implement the interface in the classes as below:

namespace Business;
public class EmployeeService : IEmployeeDetails, IDepartmentDetails
{
    public List<Employee> GetEmployee()
    {
        var employees = new List<Employee>()
        {
            new Employee()
            {
                Id = 1,
                Title = "Mr",
                Name = "Simon",
                Age = 32,
                EmailId = "[email protected]",
                MobileNumber= "12346",
                Address = "Pune",
                Pincode =   411057

            },
            new Employee()
            {
                Id = 2,
                Name = "David",
                Age = 35,
                EmailId = "[email protected]",
                MobileNumber= "654323456",
                Address = "Mumbai",
                Pincode =   221011
            },
            new Employee()
            {
                Id = 3,
                Title = "Mr",
                Name = "Peter",
                Age = 29,
                EmailId = "[email protected]",
                MobileNumber= "54323456",
                Address = "Lucknow",
                Pincode =   221100

            }
        };
        return employees;
    }

    public List<Department> getDepartmentDetails()
    {
        var departmentList = new List<Department>()
        {
            new Department()
            {
                DepartmentId = "D001",
                DepartmentHead = "Mr. Davis",
                DepartmentName = "IT"
            }

        };
        return departmentList;
    }

    public IEnumerable<Employee> SaveEmpAsList(Employee request)
    {
        List<Employee> emp = new List<Employee>();
        emp.Add(request);
        return emp;
    }

    public Employee GetOneEmployee()
    {
        Employee employee = new Employee()
        {
            Id = 3,
            Title = "Mr",
            Name = "Peter",
            Age = 29,
            EmailId = "[email protected]",
            MobileNumber = "54323456",
            Address = "Lucknow",
            Pincode = 221100

        };
        return employee;
    }
}

STEP 3 - Need to call the business logic in the controller. For this we need to inject the dependency in the controller layer using Constructor injection 

[Route("api/[controller]")]
[ApiController]
public class EmployeeController : ControllerBase
{
    private readonly IEmployeeDetails _employeeService;
    private readonly IDepartmentDetails _departmentService;
       public EmployeeController(IEmployeeDetails employeeService,
        IDepartmentDetails departmentService)
    {
        _employeeService = employeeService;
        _departmentService = departmentService;
    }
}

STEP 4 - Calling the service using the injector
[Route("GetEmp")]
[HttpGet]
public IEnumerable<Employee> GetEmployeeList1()
{
    var res = _employeeService.GetEmployee();
    return res;
}

[Route("GetDepartment")]
[HttpGet]
//[Authorize]
public IEnumerable<Department> GetDepartment()
{
    var res = _departmentService.getDepartmentDetails();
    return res;
}

NOTE: Try to run the code, you will get the run time exception.

    "System.InvalidOperationException: Unable to resolve service for type 'Business.IDepartmentDetails' while attempting to activate 'POCAutomapperWithSql.Controllers.EmployeeController'. "

As we haven't registered the interface in the container class (Program.cs). For registering the interface and classes, you need to go in the Program class (As Startup class is no more with .NET 6) and use these methods i.e "AddScoped" || "AddTransient" || "AddSingleton"  as it defines the lifetime of the services.

STEP 5 - Go to Program class and register it.

// Register interface and class which we injected

// Register interface and classes
builder.Services.AddScoped<IEmployeeDetails, EmployeeService>();
builder.Services.AddScoped<IDepartmentDetails, EmployeeService>();

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.




European ASP.NET Core Hosting :: Generate CSV Using CsvHelper

clock April 11, 2022 08:48 by author Peter

There are many approaches to generate CSV files from the database data. One simple way is to use string builder and just append the comma separated values from database with header in the first row. But this approach has some drawbacks as the plain strings don't handle commas in the data strings and also some speacial characters. This is where the CSV helper (https://www.nuget.org/packages/CsvHelper/) nugget package comes in handy.

But nonetheless if your data has no commas/ string data and you don't want external nugget packages to generate CSV then using string builder is preferred.

Generate CSV using String builder
private static void DownloadValidations(IList<Validation> validations )
{
    var stringBuilder = new StringBuilder();    stringBuilder.AppendLine("ValidationId,ValidationTimeStamp,UserId,ImageId,ImageDate,ImageSource,Crop,CropTypeId,ImageLocation(x:y),Country,CountryIsoCode, ImageUrl");
    foreach (var validation in validations)
    {
        var image = validation.Image;
        var row =
            $"{validation.Id},{validation.CreationTime:dd/MM/yyyy},{validation.User.Id},{image.Id},{image.Date:dd/MM/yyyy},{image.ImageSource},{validation.Classification.Name},{validation.Classification.Id},{$"{image.Location.X}:{image.Location.Y},{image.Country.Name},{image.Country.Code},{image.Url}"}";
        stringBuilder.AppendLine(row);
    }
    File.WriteAllText(".\\validations.csv", stringBuilder.ToString());
}

public class Validation
{
    public virtual User User { get; set; }
    public virtual Image Image { get; set; }
    public virtual Classification Classification { get; set; }
    public string Platform { get; set; } // browser or mobile etc
    public double TimeNeeded { get; set; }  // comes from importer tool?
    public bool IsCorrect { get; set; }  // is classification correct?
    public string Version { get; set; } //app versions
    public string IpAddress { get; set; } // device Ip
}


Generate CSV using CSVHelper
public void GenerateCsv(IList<survey> surveys )
{
    var memoryStream = new MemoryStream();
    using (var streamWriter = new StreamWriter(memoryStream, Encoding.UTF8))
    {
        var csvWriter = new CsvSerializer(streamWriter, CultureInfo.InvariantCulture);
        await csvWriter.WriteAsync(new[]
        {
            "Id", "CreationTime", "CreatorId", "StorageType", "x_lat", "y_long",  "capacity",
            "lifetime", "location",
            "locationOther",
            "food", "foodOther", "Protected", "ProtectedRemarks", "PercentConsumed", "PercentSold",
            "Storageduration", "Storagedurationremarks", "owner",
            "ownerOther", "differences", "improvements", "remarks"
        });
        await csvWriter.WriteLineAsync();
        foreach (var survey in surveys)
        {

            await csvWriter.WriteAsync(new[]
            {
                $"{survey.Id}", $"{survey.CreationTime:s}", $"{survey.CreatorId}", $"{survey.StorageType}",
                $"{survey.Location.Coordinate.X}", $"{survey.Location.Coordinate.Y}",
                $"{extraData.Capacity}", $"{extraData.Lifetime}", $"{extraData.Location:G}",
                $"\"{extraData.LocationOther}\"", $"{extraData.Food:G}", $"\"{extraData.FoodOther}\"",
                $"{extraData.Protected:G}", $"\"{extraData.ProtectedRemarks}\"",
                $"{extraData.PercentConsumed}", $"\"{extraData.PercentSold}\"",
                $"{extraData.Storageduration}", $"\"{extraData.Storagedurationremarks}\"",
                $"{extraData.Owner:G}", $"\"{extraData.OwnerOther}\"", $"\"{extraData.Differences}\"",
                $"\"{extraData.Improvements}\"", $"\"{extraData.Remarks}\""
            });
            await csvWriter.WriteLineAsync();
        }
        await streamWriter.FlushAsync();
    }
    File.WriteAllText(".\\surveys.csv", stringBuilder.ToString());
}

public class survey
{
    public long SurveyId { get; set; }
    public DateTime CreatedOn { get; set; }
    public Geometry Location { get; set; }
    public double UserLat { get; set; }
    public double UserLng { get; set; }
    public string Crop { get; set; }
    public string CropOther { get; set; }
    public string Phenology { get; set; }
    public string PhenologyOther { get; set; }
    public string Damage { get; set; }
    public string DamageOther { get; set; }
    public string Manage { get; set; }
    public string ManageOther { get; set; }
    public string Remarks { get; set; }
    public string[] Images { get; set; }
    public int PlantHeight { get; set; }
    public string DateObservation { get; set; }
    public string DateSurveyCreation { get; set; }
    public string PolygonWkt { get; set; }
}


The CSVHelper can handle strings with commas and all special characters. So based on your need if there is a requirement choose this nugget package.
Conclusion

If you need a very simple CSV generator then go for stringbuilder and append your data to generate CSV string and save it to a file

If you need a more advanced CSV generator which can handle any sort of strings, and don't mind external nugget packages then go for csvhelper, which has matured over the years and is open source.

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.

 



European ASP.NET Core Hosting :: Customize Swagger UI In ASP.NET Web API Restful Service

clock April 1, 2022 08:56 by author Peter

Swagger UI is a very powerful documentation tool for Restful services. It is a framework for describing and consuming RESTful APIs. It keeps the documentation system of methods, parameters, and models tightly integrated into the server code. We can add Swashbuckle to any Web API project, and then we can easily customize the generated specification along with the UI (swagger-ui). Swagger UI uses Swashbuckle to displays the documentation. The Swashbuckle has few extension points that we can use to customize the look and feel.
Description

In this article, I will show the steps for customizing the Swagger UI.

  • Add XML Comments/Description for GET() methods
  • Customize the Swagger UI Using Style Sheet
  • Customize the Swagger UI Using Javascript
Things you can customize in Swashbuckle are shown below,
    Style Sheet
    Javascript
    HTML
    Submit methods, Boolean values, etc

Steps to be followed,
If you open SwaggerConfig.cs file under App_Start folder you can see that all the configuration that is related to the swagger is present. Swagger Ui has some feature and facility to read XML comment from the methods like GET() and GET(ID).

Step 1
Go to EmployeeController.cs and the XML comments for GET() methods as shown below. For that just type 3 forward slash (or simply slash) /// and it will auto create the XML section to write your own comments in Summary and Param section.

For GET() Method

For GET(ID) Method

Step 2
We need one more setting and for that right click on project and go to properties. Next step is select Build and choose the XML documentation file, Then save those changes.

Step 3
Go to SwaggerConfig.cs file under App_Start folder and add the following code.

Here WebAPIProj.XML is name of the Web API project. So, put your own project name.

Step 4
Uncomment the below line of code in SwaggerConfig.cs file.


c.IncludeXmlComments(GetXmlCommentsPath());

Build your solution after these changes are made in SwaggerConfig.cs file and Run it.

OUTPUT
Here we can see the XML comments in GET() methods are shown below.


Also, we can see the Employee ID as description is added in param section of GET(ID) method in EmployeeController.


Customize the Swagger UI Using Style Sheet

Step 5
Here we can customize the Swagger UI as well using Stylesheet. I have added one style sheet file named SwaggerStyle.css under Content folder.
Right click on SwaggerStyle.css file and select Embedded Resource option for Build Action as shown below.


Here I added CSS class in SwaggerStyle.css file as shown below.

.swagger-section #header {
    background-color: #ffd800;
    padding: 14px;
}

This class has two properties for background color and padding to header.

Step 6
Then I will add this SwaggerStyle.css file reference in SwaggerConfig.cs file. Uncomment the below line of code.


Modify this line of code as shown below.
c.InjectStylesheet(thisAssembly, "WebAPIProj.Content.SwaggerStyle.css");

Here I have mentioned the full name of CSS file that is under WebAPIProj / Content / SwaggerStyle.css.

OUTPUT
Here, we can see the background color of header is changed and the padding properties as well.


Like this we can customize the whole Swagger UI as per requirement.

Customize the Swagger UI Using Javascript
Here I added a JavaScript file named SwaggerScript.js under Content folder.


Right click on SwaggerScript.js file and select Embedded Resource option for Build Action as shown below.



Here I added javascript code in SwaggerScript.js file as shown below.
$(document).ready(function () {
    alert("Swagger Script Alert Added.");
});

Step 7
Then I will add this SwaggerScript.js file reference in SwaggerConfig.cs file. Uncomment the below line of code.

Modify this line of code as shown below.
c.InjectJavaScript(thisAssembly, "WebAPIProj.Content.SwaggerScript.js");

Here I have mentioned the full name to JS file that is under WebAPIProj/Content/SwaggerScript.js.

OUTPUT

Here, we can see that javascript alert message when the Swagger UI loads as shown below.

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.


 



European ASP.NET Core Hosting :: Options Pattern In .NET 6.0

clock March 28, 2022 08:09 by author Peter

Reading from configuration file is one of the most common requirements when it comes to software development. With options pattern in .NET this can be achieved in an elegant manner using the options interfaces. The various options interfaces exposed in .NET enables mapping configuration settings to strongly typed classes that can be accessed across various service lifetimes. In this article, we will explore the different ways to implement options pattern across transient, scoped, and singleton service lifetimes.
Setup

Create an ASP.NET WebAPI 6.0 app and add the following configuration setting in the appsettings.json file
"Units": {
  "Temp": "Celsius",
  "Distance": "Miles"
}

Create a UnitOptions class corresponding to the setting created in the previous step
public class UnitOptions
{
    public string Temp { get; set; } = String.Empty;
    public string Distance { get; set; } = String.Empty;
}


Bind the UnitOptions class to the corresponding section in appsettings.json by registering configuration instance in Program.cs (If you are using previous version of .NET, add the following line in Startup.cs)
builder.Services.Configure<UnitOptions>(builder.Configuration.GetSection("Units"));

IOptions
IOptions is singleton and hence can be used to read configuration data within any service lifetime. Being singleton, it cannot read changes to the configuration data after the app has started.

To demonstrate this let’s create a transient service to read the unit options from the configuration file using IOptions interface as follows:-

public interface ITransientService
{
    UnitOptions GetUnits();
}

public class TransientService : ITransientService
{
    private readonly UnitOptions _unitOptions;

    public TransientService(IOptions<UnitOptions> unitOptions)
    {
        _unitOptions = unitOptions.Value;
    }
    public UnitOptions GetUnits()
    {
        return _unitOptions;
    }
}


Add the transient service to DI container in Program.cs

builder.Services.AddTransient<ITransientService, TransientService>();

Hook the service to the controller
[Route("api/[controller]")]
[ApiController]
public class OptionsDemoController : ControllerBase
{
    private readonly ITransientService _transientService;

    public OptionsDemoController(TransientService transientService)
    {
        _transientService = transientService;
    }

    [HttpGet]
    [Route("/units/transient")]
    public IActionResult GetUnitsTransient() => Ok(_transientService.GetUnits());
}

Run the app and hit the controller action. You should be able to see the values being fetched from the configuration file.

While the app is still running, change the value of distance unit from ‘Miles’ to ‘Kilometres’ in the appsettings.json file and hit the same API controller action again. The response does not change. This is because IOptions cannot read changes to the config data while the app is still running.

Revert changes to the appsettings.json file before proceeding with next steps.

IOptionsSnapshot

IOptionsSnapshot is scoped and hence it can be used only with transient and scoped service lifetimes. Being scoped, it can recompute config data for each request.

Create a scoped (or transient) service with an injected IOptionsSnapshot instance as follows:-
public interface IScopedService
{
    UnitOptions GetUnits();
}


public class ScopedService : IScopedService
{
    private readonly UnitOptions _unitOptions;

    public ScopedService(IOptionsSnapshot<UnitOptions> unitOptions)
    {
        _unitOptions = unitOptions.Value;
    }

    public UnitOptions GetUnits()
    {
        return _unitOptions;
    }
}


Add the scoped service to DI container in Program.cs
builder.Services.AddScoped<IScopedService, ScopedService>();

Hook the service to the controller
[Route("api/[controller]")]
[ApiController]
public class OptionsDemoController : ControllerBase
{
    private readonly IScopedService _scopedService;
    private readonly ITransientService _transientService;

    public OptionsDemoController(ITransientService transientService, IScopedService scopedService)
    {
        _transientService = transientService;
        _scopedService = scopedService;
    }

    [HttpGet]
    [Route("/units/scoped")]
    public IActionResult GetUnitsScoped() => Ok(_scopedService.GetUnits());

    [HttpGet]
    [Route("/units/transient")]
    public IActionResult GetUnitsTransient() => Ok(_transientService.GetUnits());
}


Run the app and hit the controller action. You should be able to see the values being fetched from the configuration file.


While the app is still running, change the value of distance unit from ‘Miles’ to ‘Kilometres’ in the appsettings.json file and hit the same API controller action again. The response reflects the changes to the config data.

If you try to add IOptionsSnapshot to any singleton service, you would encounter a runtime exception because of the service lifetime mismatch.

Revert changes to the appsettings.json file before proceeding with next steps.

IOptionsMonitor

IOptionsMonitor is singleton and hence can be used to read configuration data in any service lifetime. However, as opposed to IOptions, it can retrieve current config data at any time.

Create a singleton service with an injected IOptionsMonitor instance as follows:-
public interface ISingletonService
{
    UnitOptions GetUnits();
}

public class SingletonService : ISingletonService
{
    private readonly IOptionsMonitor<UnitOptions> _unitOptions;

    public SingletonService(IOptionsMonitor<UnitOptions> unitOptions)
    {
        _unitOptions = unitOptions;
    }
    public UnitOptions GetUnits()
    {
        return _unitOptions.CurrentValue;
    }
}

Add the service to DI container in Program.cs
builder.Services.AddSingleton<ISingletonService, SingletonService>();

Hook the service to controller
[Route("api/[controller]")]
[ApiController]
public class OptionsDemoController : ControllerBase
{
    private readonly ITransientService _transientService;
    private readonly IScopedService _scopedService;
    private readonly ISingletonService _singletonService;

    public OptionsDemoController(ITransientService transientService, IScopedService scopedService, ISingletonService singletonService)
    {
        _transientService = transientService;
        _scopedService = scopedService;
        _singletonService = singletonService;
    }

    [HttpGet]
    [Route("/units/transient")]
    public IActionResult GetUnitsTransient() => Ok(_transientService.GetUnits());


    [HttpGet]
    [Route("/units/scoped")]
    public IActionResult GetUnitsScoped() => Ok(_scopedService.GetUnits());

    [HttpGet]
    [Route("/units/singleton")]
    public IActionResult GetUnitsSingleton() => Ok(_singletonService.GetUnits());
}

Run the app and hit the controller action. You should be able to see the values being fetched from the configuration file.


While the app is still running, change the value of distance unit from ‘Miles’ to ‘Kilometres’ in the appsettings.json file and hit the same API controller action again. The response reflects the changes to the config data.


The options pattern provides us with various options to read the config data using strongly types classes. Depending upon service lifetime and recomputation requirements of the config data, one can use IOptions, IOptionsSnapshot, and IOptionsMonitor interfaces to read config data. Prefer using the options pattern over other methods to read config data.

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

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