Sunday, 11 December 2022

Generic Attributes in C#11

 

In C# 11 comes with .Net 7, Attribute is enhanced to use with generic type. This feature provides a convenient way for attributes to avoid System.Type constructor parameter (the old way).

Attributes are metadata extensions that give additional information to the compiler about the elements in the program code at runtime. Attributes are used to impose conditions or to increase the efficiency of a piece of code.

Being a programmer I love this. Here I will try to explain to you how to define Attributes with generic types. To understand the enhancement, let's see how we used to do the Attributes with type in C# 10 or before.

For example, we will create an Asp.Net core web API project template with the target framework net7.0 so the same project will be using the demonstration of old and new syntax.

Here I will be creating an Action Filter called LoggingFilter and will be logging some info before and after the execution of an action.

The above code is straightforward. So now we add this to the service collection.

To use this action filter with an Action method we will use Microsoft.AspNetCore.Mvc.ServiceFilter attribute which is using the old syntax which takes System.Type as a parameter for the constructor and the definition of ServiceFilterAttribute.

Next, we will use the action filter with the Get action method of the WeatherForecastController generated from the template.

Now you run the application and execute the GetWeatherForecast endpoint from the swagger app, you will see the information log in the output window as

Cool, Everything works great but How about Microsoft would have made the ServiceFilterAttribute generic and avoid the type parameter in the constructor? Well, we will do it and will write better code than Microsoft. :) Just kidding. Actually, this is the enhancement done in C#11, thanks to Microsoft for that.

To redefine the ServiceFilterAttribute using the Generic Attribute feature, let’s see the ServiceFilterAttribute code which is available under Microsoft.AspNetCore.Mvc namespace:

As per the above Microsoft code here, ServiceFilter takes Type as a parameter in the constructor which we pass as typeof(LoggingFilter) in the action method. So now we will recreate the ServiceFilterAttribute by defining a generic attribute and the syntax would be as:

public class MyServiceFilterAttribute<T> : Attribute

and the code as:

Wow, no more type parameters and a very convenient way of making the attribute class generic.

Again if you run the application, the logs will be displayed in the output window as with the earlier code.

No difference in functionality but a huge difference in terms of better code.

Here is the full code used for this example:
https://github.com/binodmahto/FunProjects/tree/main/CSharp11WebAPIDemo

To know more about C#11 new features please read through:
https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11

Hope you enjoyed the content, follow me for more like this, and please don’t forget to LIKE it. Happy programming.

Monday, 5 December 2022

CICD best practices

 CICD is the process of automating the building, testing & deploying of your application.

  1. Plan your repo
  2. Choose your tools
  3. Plan your Tests automation
  4. Secure your pipelines and secrets
  5. Pipelines for early-stage verification and deployment
  6. Involve the Team.

CI/CD with GitHub Actions pipeline to run the .Net unit test and publish results

 Writing Tests (Unit Tests, Integration Tests) is not only the best practice but also essential to ensure quality code.

From the DevOps side, it is essential to put a gate to check for successful unit test execution with each pull request or push. So in this article, we will see how to implement a pipeline to run the unit tests and publish the results.

What do we need?

  1. We need a pipeline to be triggered with every pull request for your code repo.
  2. Jobs to run the unit tests and publish the results.

Please follow my other articles to learn more about GitHub Actions: CI/CD with GitHub ActionsCI/CD with GitHub Actions to deploy Applications to Azure App ServiceCI/CD with GitHub Actions to deploy Application to Azure Kubernetes Cluster

Let’s create our pipeline for unit test execution. To do this add a yaml/yml file as .github/workflows/buildAndRunTest.yml in the root project folder and then starts with defining the name & triggering action.

name: Build and run tests

env:
DOTNET_VERSION: '6.0' # set this to the .NET Core version to use
WORKING_DIRECTORY: './src' #define the root folder path

on:
workflow_dispatch: #for manual trigger
pull_request: #trigger action with pull request
branches:
- main #your branch name

Next step, we will define the job and mention the target machine (Linux/windows) then perform the necessary steps for code checkout and setting .net core

jobs:
build:
runs-on: ubuntu-latest #target machine is ubuntu

steps:
- uses: actions/checkout@v2

- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: ${{ env.DOTNET_VERSION }} #reading it from env variable defined above

I’m using “EnricoMi/publish-unit-test-result-action” action from the marketplace with JUnit logger hence we need to install the related package for each test project as the next step.

Xml logger for JUnit v5 compliant xml report when test is running with “dotnet test” or “dotnet vstest”.

- name: Setup test loggers
shell: pwsh
run: |
foreach ($file in Get-ChildItem -Path .src/tests/*Tests.csproj -Recurse)
{
dotnet add $file package JunitXml.TestLogger --source 'https://api.nuget.org/v3/index.json'
}

From the above code, looping through all test projects inside the src/tests folder.

Next step we will execute the tests with dotnet command.

- name: Run unit tests
shell: bash
working-directory: ./src
run: |
dotnet test myDemoApp.sln --logger 'junit;LogFileName=TestResults.xml' \
--verbosity normal --results-directory ./_test-results

The above code will run all the unit tests in the project and save the result inside the src/_test-results/TestResults.xml file.

If you have categorized tests then you can use the filter option with dotnet test as:
dotnet test — filter “TestCategory=CategoryA”

Finally will publish the results using “EnricoMi/publish-unit-test-result-action” from the marketplace.

 - name: Publish Unit Test Results
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
with:
junit_files: ./src/_test-results/*.xml

We are done so With this if you the actions pipeline you will see the results as:

Note: There are various actions available in the GitHub marketplace to publish your test results, please explore them too.

Hope you enjoyed the content, follow me for more like this, and please don’t forget to LIKE  it. Happy programming.


Monday, 14 November 2022

Dockerize VueJS or Angular application


 

For the past couple of months, I spent my time heavily on infrastructure and CICD DevOps activity to host my applications, web, and backend application on Azure Kubernetes Cluster. Also if you follow me, my last few articles were all about a complete setup overview regarding CICD and security.

This article is about dockerizing a VueJS or Angular web application, though my web application here was on VueJS, the same technique applies to Angular applications to dockerize the application.

Before we begin, we need to take a decision on the server that will serve the application like http-server, Nginx, Apache etc. For development/testing we may use zero-configuration command-line http-server to server our web application but not recommended for production as the documentation says:

So, in my case, I decided to use Nginx and this is all you need to do.

First, we need to create the DockerFile so add a file name “Dockefile” in your project root folder where package.json or config file exists and add the below code:

FROM node:16.15.0-alpine as build
WORKDIR /app

COPY package.*json ./
COPY .npmrc ./
COPY .env.production ./

RUN npm install

COPY . .

WORKDIR /app

RUN npm run build:production
RUN rm -f .npmrc

# production
FROM nginx:stable-alpine as production
COPY ./.nginx/nginx.conf /etc/nginx/nginx.conf

## Remove default nginx index page
RUN rm -rf /usr/share/nginx/html/*

COPY --from=build /app/dist /usr/share/nginx/html
# Expose 8030 this is optional
CMD ["nginx", "-g", "daemon off;"]

From the above file, first, we mention the build server as Node. In my case, I’m using a specific version of Node which you may change based on your need. Next line set the working folder as app, where the dist folder along with other config files will be created.

in the next step, we copied our required config files as package.*json (also includes package.lock.json), .npmrc (which contains token to connect and pull dependencies from my repo), .env files. You may add all your config files needed here to keep the code related to copying config files in one place otherwise in the next step (npm install), only package.json and .npmrc files are required.

So next we did, npm install and copied all (which include dist folder) to the working directory app folder.

In the next step, we build the application using the npm run build command and delete the .npmrc file copied from the first step required for the npm install.

Now we need to set the web server and we set it here as “nginx:stable-alpine”, you may choose other versions too based on your need. Here we need the Nginx configuration file to tell the Nginx server where to start i.e. root, port, no. of worker process, etc. We have not created this file yet so let’s do it by creating a file name nginx.conf with the below code inside the .nginx folder in your project root path as .nginx=>nginx.conf.

worker_processes 4;

events { worker_connections 1024; }

http {
server {
# listen 80; this is optional, it will be default to 80
root /usr/share/nginx/html;
include /etc/nginx/mime.types;

location /appui {
try_files $uri /index.html;
}
}
}

Now we will go back to the Dockerfile and copy this nginx.conf file to the Nginx server inside an etc folder and also remove the default index page from the location usr/share/nginx/html/*

FROM nginx:stable-alpine as production
COPY ./.nginx/nginx.conf /etc/nginx/nginx.conf

RUN rm -rf /usr/share/nginx/html/*

and then we copy all the contents of the dist folder from the build server to Nginx server inside /usr/share/nginx/html and mention the command mentioned below to start the nginx.

COPY --from=build /app/dist /usr/share/nginx/html
# Expose 8030
CMD ["nginx", "-g", "daemon off;"]

Testing

If you have a docker desktop installed, follow the steps below to test.

step 1: run the below command using the command prompt from your project root folder where the package.json file exists.

docker build  -t test/webapp_v1 .

After the successful execution of the above command, you will see the image created in the docker desktop under Images.

step 2: Click on Run and change the port setting from the host server and container as per our docker file and nginx.conf file where our container is exposed through port 8030 and the host server is at port 80. Please refer to the below screenshot:

Now open the browser and type http://localhost:8090/ to browse the application and your web app is up.

Bonus

Please follow my previous articles to push this image to Azure Container Registery and deploy it on Azure Kubernetes Cluster using GitHub Actions. Though these articles are for hosting .net core app but the steps remain the same with little modification as per the web app.

  1. CI/CD with GitHub Actions to deploy Applications to Azure Kubernetes Cluster
  2. Securing secrets with Azure Key Vault for GitHub Actions