Category: .NET Core

HealthCheckVersion–documentation- part 2

Written the documentation at https://github.com/ignatandrei/HealthCheckVersion . Put also here to have it.

HealthCheckVersion

What it is

This NuGet package will report via Health Check the version of the starting assembly . Also, it should report his own version – but not direct .

It will be easy to demo via Xabaril

Why

To know from first hand if the application works and , if it does, to see fast his versin

How to use

Add the NuGet package HealthCheckVersion.

In the Startup.cs

services.AddHealthChecks()
    .AddCheck<HealthCheckVersion>("HealthCheckVersion");

Navigate to the HealthCheck JSON and see the reporting of the version of the starting assembly and of the own HealthCheckVersion assembly.

And that will be all!

(Maybe later I will think about a configuration for this – in order to customize the reporting)

Tests

The application has also tests to see if it works. I will test just the obtaining the version of the starting assembly and HealthCheckVersion own assembly.

The first test will test the functions

GetStartingAssemblyInformation

GetHCVAssemblyInformation

 

HealthCheckVersion

Health Check for Version
 NameLink
1Idea and Githubhttp://msprogrammer.serviciipeweb.ro/2020/07/20/healthcheckversion-idea-part-1/
2Documentationhttp://msprogrammer.serviciipeweb.ro/2020/07/21/healthcheckversiondocumentation-part-2/
3Testshttp://msprogrammer.serviciipeweb.ro/2020/07/22/hcv-adding-testspart-3/
4Codehttp://msprogrammer.serviciipeweb.ro/2020/07/23/hcv-adding-codepart-4/
5Test Applicationhttp://msprogrammer.serviciipeweb.ro/2020/07/27/hcvadding-test-applicationpart-5/
6CIhttp://msprogrammer.serviciipeweb.ro/2020/07/28/hcvautomatic-cipart-6/
7NuGethttp://msprogrammer.serviciipeweb.ro/2020/07/29/hcvpreparing-for-nugetpart-7/
8PostMortemhttp://msprogrammer.serviciipeweb.ro/2020/07/30/hcv-postmortempart-8/

HealthCheckVersion–- idea – part 1

The health Check from Microsoft ( https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks ) and from Xabaril ( https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks ) are pretty impressive. ( I have done this before with a simple ping action  – but this is better)

I was thinking about making a simple HealthCheck – that reports the version of executing  starting assembly ( And his own version – to know if it is outdated )

I will try to make it first Documentation  , then Test, then Implementation, then CI , then CD – and that will be all

It is a simple application.- just curious how it will take to have it done.

The first step is to create a repository for my application –  it is at https://github.com/ignatandrei/HealthCheckVersion – and can be downloaded from https://github.com/ignatandrei/HealthCheckVersion/releases/tag/firstVersion

HealthCheckVersion

Health Check for Version
 NameLink
1Idea and Githubhttp://msprogrammer.serviciipeweb.ro/2020/07/20/healthcheckversion-idea-part-1/
2Documentationhttp://msprogrammer.serviciipeweb.ro/2020/07/21/healthcheckversiondocumentation-part-2/
3Testshttp://msprogrammer.serviciipeweb.ro/2020/07/22/hcv-adding-testspart-3/
4Codehttp://msprogrammer.serviciipeweb.ro/2020/07/23/hcv-adding-codepart-4/
5Test Applicationhttp://msprogrammer.serviciipeweb.ro/2020/07/27/hcvadding-test-applicationpart-5/
6CIhttp://msprogrammer.serviciipeweb.ro/2020/07/28/hcvautomatic-cipart-6/
7NuGethttp://msprogrammer.serviciipeweb.ro/2020/07/29/hcvpreparing-for-nugetpart-7/
8PostMortemhttp://msprogrammer.serviciipeweb.ro/2020/07/30/hcv-postmortempart-8/

 

Generating outdated, licenses and thanks with .NET Core tools

Last time (http://msprogrammer.serviciipeweb.ro/2020/06/08/net-core-local-tools/) I have discussed about local tools . Now it is time to show something in practice, beside code coverage ( detailed http://msprogrammer.serviciipeweb.ro/2019/12/09/code-coveragepart-25/ and video https://youtu.be/JvahoA0WWms  ) ,

Let’ make something simple: generate outdated packages list, licenses and thanks.

I will use this packages

“dotnet-project-licenses”: {

“version”: “2.1.0”,

“commands”: [

“dotnet-project-licenses”

]

},

“dotnetthx”: {

“version”: “0.2.0”,

“commands”: [

“dotnet-thx”

]

},

“dotnet-depends”: {

“version”: “0.4.0”,

“commands”: [

“dotnet-depends”

]

},

“dotnet-outdated”: {

“version”: “2.11.0”,

“commands”: [

“dotnet-outdated”

]

}

And I will make a github action to run  to generate the outdated, licenses and thanks

name: .NET Core

on:

push:

branches: [ master ]

jobs:

build:

runs-on: windows-latest

steps:

– uses: actions/checkout@v2

– name: Setup .NET Core

uses: actions/setup-dotnet@v1

with:

dotnet-version: 3.1.101

– name: Install dependencies

run: |

        cd src

        cd NetCore2Blockly

        dotnet restore

        echo ‘done restore’

        dotnet tool restore –add-source https://myget.org/F/natemcmaster/api/v3/index.json

        dotnet dotnet-project-licenses  -j  -i .

        echo ‘done project licences’

        dotnet dotnet-thx > thanks.txt

        echo ‘done thanks’

        dotnet dotnet-outdated -o outdated.csv -of csv

        echo ‘done outdated’

        dotnet pwsh copyToRoot.ps1

        cd ..

        cd ..

        echo ‘back to source dir’

– name: test

run: echo done

– name: run commit

run: |

        git config –local user.email “action@github.com”

        git config –local user.name “GitHub Action”

        git add –all

        git status

        git commit -m “generate thanks, outdate, licences” -a –allow-empty

shell: cmd

– name: Push changes

uses: ad-m/github-push-action@master

with:

github_token: ${{ secrets.GITHUB_TOKEN }}

The powershell file that copies everything is here:

Move-Item .\licenses.json ..\..\licencesNetCore.json

Write-Host ‘copied licenses.json ‘

Move-Item .\outdated.csv ..\..\outdatedNetCore.csv

Write-Host ‘copied outdated.csv ‘

Move-Item .\thanks.txt ..\..\thanksNetCore.txt

Write-Host ‘copied thanks.txt ‘

And  you can see the result at

https://github.com/ignatandrei/NETCoreBlockly/blob/master/thanksNetCore.txt

https://github.com/ignatandrei/NETCoreBlockly/blob/master/outdatedNetCore.csv

https://github.com/ignatandrei/NETCoreBlockly/blob/master/licencesNetCore.json

You can find also a list of tools here: https://github.com/natemcmaster/dotnet-tools

.NET Core local tools

( Video at https://youtu.be/iHLRBxi4S7c )

.NET Core has the concept of “local tools”  – that means, tools for a solution / project. That is different from “global tools”  by the fact that you can have it registered for the solution in a \.config\dotnet-tools.json file. You can read about those at https://docs.microsoft.com/en-us/dotnet/core/tools/local-tools-how-to-use

But I want to show you my local tools. First , the json:

{

“version”: 1,

“isRoot”: true,

“tools”: {

“dotnet-property”: {

“version”: “1.0.0.11”,

“commands”: [

“dotnet-property”

]

},

“powershell”: {

“version”: “7.0.0”,

“commands”: [

“pwsh”

]

},

“xunit-cli”: {

“version”: “0.1.3”,

“commands”: [

“xunit”

]

},

“coverlet.console”: {

“version”: “1.7.0”,

“commands”: [

“coverlet”

]

},

“dotnet-reportgenerator-globaltool”: {

“version”: “4.5.0”,

“commands”: [

“reportgenerator”

]

},

“dotnet-aop”: {

“version”: “2020.2.17.1904”,

“commands”: [

“dotnet-aop”

]

},

“loxsmoke.mddox”: {

“version”: “0.5.1”,

“commands”: [

“mddox”

]

},

“dotnet-project-licenses”: {

“version”: “1.1.1”,

“commands”: [

“dotnet-project-licenses”

]

},

“dotnetthx”: {

“version”: “0.2.0”,

“commands”: [

“dotnet-thx”

]

},

“dotnet-depends”: {

“version”: “0.4.0”,

“commands”: [

“dotnet-depends”

]

},

“dotnet-outdated”: {

“version”: “2.10.0”,

“commands”: [

“dotnet-outdated”

]

}

}

}

Now, how I use for code coverage:

dotnet tool restore –add-source https://myget.org/F/natemcmaster/api/v3/index.json

pwsh ./setVersion.ps1

dotnet coverlet bin\$(buildConfiguration)\netcoreapp3.1\CLITests.dll –target “dotnet” –targetargs “test –no-build –configuration $(buildConfiguration)” –exclude ‘[*Test*]*’ –format opencover  –output $(Build.ArtifactStagingDirectory)\testResults\coverlet.xml

dotnet reportgenerator “-reports:$(Build.ArtifactStagingDirectory)\testResults\coverlet.xml” “-targetdir:$(Build.ArtifactStagingDirectory)\testResults” “-reporttypes:Cobertura;HtmlSummary;Badges;HtmlInline_AzurePipelines”

In this way I can have set version and running tests and see code coverage in Azure Devops: https://dev.azure.com/ignatandrei0674/WebAPI2CLI/_build?definitionId=7&_a=summary

( also, for code coverage, see video at  https://youtu.be/JvahoA0WWms  )

You can find also a list of tools here: https://github.com/natemcmaster/dotnet-tools

Embed files for a Nuget ASP.NET Core package

For NetCoreBlockly I have the need to embed some files in the NUGET package (https://www.nuget.org/packages/NetCore2Blockly ) to display a GUI.

After unsuccessful trying to deploy, I  have read https://weblog.west-wind.com/posts/2018/Jan/29/Distributing-Content-and-Showing-a-ReadMe-file-in-a-NET-Core-Nuget-Package .

So I must learn from the experts: e.g. , swagger : https://github.com/domaindrivendev/Swashbuckle.AspNetCore it is displaying the HTML UI.  How it does ? By embedding into the .csproj the index.html : https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/master/src/Swashbuckle.AspNetCore.SwaggerUI/Swashbuckle.AspNetCore.SwaggerUI.csproj

But NetCoreBlockly have more than 1 file – more js, more css … and I want to be done automatically.

So I solved this in 3 steps:

 

  1. I Embed all files from a folder in the nuget csproj

<ItemGroup>
<EmbeddedResource Include=”blocklyFiles\**\*”>
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>

2. In the NuGet I obtain the files as embedded

var manifestEmbeddedProvider =
new ManifestEmbeddedFileProvider(Assembly.GetExecutingAssembly());

3. I generate ASP.NET Core endpoints for each file from the Embed, according to the tree :

private static void mapFile(string dirName, IFileProvider provider, IApplicationBuilder appBuilder)
{
var folder = provider.GetDirectoryContents(dirName);
foreach (var item in folder)
{
    if (item.IsDirectory)
    {
        mapFile(dirName + "/" + item.Name, provider, appBuilder);
        continue;
    }
    string map = (dirName + "/" + item.Name).Substring("blocklyFiles".Length);
    appBuilder.Map(map, app =>
    {
        var f = item;

        app.Run(async cnt =>
        {
            //TODO: find from extension
            //cnt.Response.ContentType = "text/html";
            using var stream = new MemoryStream();
            using var cs = f.CreateReadStream();
            byte[] buffer = new byte[2048]; // read in chunks of 2KB
            int bytesRead;
            while ((bytesRead = cs.Read(buffer, 0, buffer.Length)) > 0)
            {
                stream.Write(buffer, 0, bytesRead);
            }
            byte[] result = stream.ToArray();
            var m = new Memory<byte>(result);
            await cnt.Response.BodyWriter.WriteAsync(m);
        });
    });
}

}        

Optional:

4. Because I need to test , copy the files in the CI from a real ASP.NET Core website to the .csproj with the NUGET package

5. You can replace some tags with your values – or just use a .js file that generates options ( see BlocklyUIOptions  below)

 

If you want to see in Action, browse to https://netcoreblockly.herokuapp.com/blockly.html

If you want to see the code, goto https://github.com/ignatandrei/NETCoreBlockly/blob/master/src/NetCore2Blockly/NetCore2Blockly/ExtensionMethods/CLIExtension.cs and see public static void UseBlocklyUI(this IApplicationBuilder appBuilder, BlocklyUIOptions options =null)

Passing a WebAPI application from .NET Core 2 to .NET Core 3 in 5 +1 (EF) steps

Simple steps:

1. Modiffy in the .csproj  <TargetFramework>netcoreapp2.0</TargetFramework> to <TargetFramework>netcoreapp3.1</TargetFramework> 

2.Delete <PackageReference Include=”Microsoft.AspNetCore.All” Version=”2.0.8″ />

3. Modify

public static IWebHost BuildWebHost(string[] args) =>
public static IHostBuilder CreateHostBuilder(string[] args) =>

WebHost.CreateDefaultBuilder(args)
Host.CreateDefaultBuilder(args)

.UseStartup<Startup>()
.ConfigureWebHostDefaults(webBuilder =>

.Build();

to

public static IHostBuilder CreateHostBuilder(string[] args) =>

WebHost.CreateDefaultBuilder(args)
Host.CreateDefaultBuilder(args)

.UseStartup<Startup>()
.ConfigureWebHostDefaults(webBuilder =>

.Build();
{

webBuilder.UseStartup<Startup>();

});

3.  Modify BuildWebHost(args).Run();  to CreateHostBuilder(args).Build().Run();

4. Modify services.AddMvc();   to

services.AddControllers();

services.AddRouting();

5. Modify  app.UseMvc();  to

app.UseRouting();

app.UseEndpoints(endpoints =>

{

endpoints.MapControllers();

});

6. Optional : modify EF from

<PackageReference Include=”Microsoft.EntityFrameworkCore.SqlServer” Version=”2.1.1″ />
<PackageReference Include=”Microsoft.EntityFrameworkCore.SqlServer” Version=”3.1.3″ />

<PackageReference Include=”Microsoft.EntityFrameworkCore.Tools” Version=”2.1.1″ />
<PackageReference Include=”Microsoft.EntityFrameworkCore.Tools” Version=”3.1.3″>

<PackageReference Include=”Microsoft.VisualStudio.Web.CodeGeneration.Design” Version=”2.1.1″ />

to

<PackageReference Include=”Microsoft.EntityFrameworkCore.SqlServer” Version=”3.1.3″ />

<PackageReference Include=”Microsoft.EntityFrameworkCore.Tools” Version=”2.1.1″ />
<PackageReference Include=”Microsoft.EntityFrameworkCore.Tools” Version=”3.1.3″>

<PackageReference Include=”Microsoft.VisualStudio.Web.CodeGeneration.Design” Version=”2.1.1″ />
<PrivateAssets>all</PrivateAssets>

<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

</PackageReference>

<PackageReference Include=”Microsoft.VisualStudio.Web.CodeGeneration.Design” Version=”3.1.2″

Zip the whole ASP.NET Core application

WebAPI2CLI

This is a part of the series where about how I made the WebAPI2CLI - Execute ASP.NET Core WebAPI from Command Line
Source code on https://github.com/ignatandrei/webAPI2CLI/
1WebAPI2CLI - Description
2WebAPI2CLI- Organization
3WebAPI2CLI - implementing
4WebAPI2CLI - tests
5WebAPI2CLI - Devops and CI/CD
6WebAPI2CLI - documentation
7WebAPI2CLI - Conclusions
8WebAPI2CLI - Zip application

I realized that I should have somehow let the user play with the application from his PC. How can I do that , if the application is on some site  ? Answer: download the whole site as a zip file.

It should be relatively easy  for the user– so an endpoint routing should be used – something like “/zip”

It should be relatively easy  for the programmer – so I figured

app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();

endpoints.MapZip(app); // IApplicationBuilder app
});

The code is also relativeley simple: enumerating recursive file and folders , creating the zip archive in memory, sending to the user

public static void MapZip(this IEndpointRouteBuilder endpoints, IApplicationBuilder app)
{
//see more at
//https://andrewlock.net/converting-a-terminal-middleware-to-endpoint-routing-in-aspnetcore-3/
endpoints.Map(“/zip”, async context =>
{
var response = context.Response;
var name = Assembly.GetEntryAssembly().GetName().Name + “.zip”;
response.ContentType = “application/octet-stream”;
var b = GetZip(app.ApplicationServices.GetService<IWebHostEnvironment>());
//https://github.com/dotnet/aspnetcore/blob/master/src/Mvc/Mvc.Core/src/Infrastructure/FileResultExecutorBase.cs
var contentDisposition = new Microsoft.Net.Http.Headers.ContentDispositionHeaderValue(“attachment”);
contentDisposition.SetHttpFileName(name);
response.Headers[HeaderNames.ContentDisposition] = contentDisposition.ToString();
await response.Body.WriteAsync(b);

});

}

private static Memory<byte> GetZip(IWebHostEnvironment env)
{
//var b = new Memory<byte>(Encoding.ASCII.GetBytes($”{env.ContentRootPath}”));
var firstDir = new DirectoryInfo(env.ContentRootPath);
var nameLength = firstDir.FullName.Length + 1;
using var memoryStream = new MemoryStream();
using var zipToOpen = new ZipArchive(memoryStream, ZipArchiveMode.Create, true);

 

foreach (FileInfo file in firstDir.RecursiveFilesAndFolders().Where(o => o is FileInfo).Cast<FileInfo>())
{
var relPath = file.FullName.Substring(nameLength);
var readmeEntry = zipToOpen.CreateEntryFromFile(file.FullName, relPath);
}
zipToOpen.Dispose();
var b = new Memory<byte>(memoryStream.ToArray());
return b;

}

private static IEnumerable<FileSystemInfo> RecursiveFilesAndFolders(this DirectoryInfo dir)
{
foreach (var f in dir.GetFiles())
yield return f;
foreach (var d in dir.GetDirectories())
{
yield return d;
foreach (var o in RecursiveFilesAndFolders(d))
yield return o;
}
}

 

Some points to be noted:

1. It will work only if the site and user have the same operating system( assuming you have did self contained the .NET Core application)

2. ContentDispositionHeaderValue – exists in 2 libraries

3. I have shameless copy code from StackOverflow and github aspnet

4. I did not put in a separate library, nor I do automatic tests. Sorry, too tired

5. It works only if you put

endpoints.MapZip(app); // IApplicationBuilder app

If not, it does not zip!

6. IEndpointRouteBuilder – it is defined only you have the library .net core  3, not .net standard 2

<TargetFramework>netcoreapp3.1</TargetFramework>

<ItemGroup>
<FrameworkReference Include=”Microsoft.AspNetCore.App” />
</ItemGroup>

Retarder–Fixed and Random strategy and Tests-part 4

Now I want to make a second strategy : the random strategy between a min and a max value. This is only to minimize the impact on the extension that uses the Random – it is good to be hidden inside. For Random Strategy it is better to re-use the Fixed Strategy – and this is the code:

using System;
using System.Threading.Tasks;

namespace NetCoreRetarderCore
{
    public class StrategyAwaitRandom : IStrategyAwait
    {
        StrategyAwaitFixed sf;
        public StrategyAwaitRandom(int min,int max)
        {
            if (min > max)
            {
                var x = min;
                min = max;
                max = min;
            }
            var random = new Random();
            var awaitTime = random.Next(min, max);
            sf = new StrategyAwaitFixed(awaitTime);
        }
        public Task<IStrategyAwait> AwaitDelayAfter()
        {
            return sf.AwaitDelayAfter();
        }

        public Task<IStrategyAwait> AwaitDelayBefore()
        {
            return sf.AwaitDelayBefore();
        }
    }
}

 

Also, I want to make some tests to verify the awaits. So I choose https://github.com/LightBDD/LightBDD and my tests looks like this:

using LightBDD.Framework;
using LightBDD.Framework.Scenarios;
using LightBDD.XUnit2;
using System;
using System.Threading.Tasks;
using Xunit;
[assembly: LightBddScope]

namespace NetCoreRetarderTest
{
    //https://github.com/LightBDD/LightBDD/wiki/Formatting-Parameterized-Steps

    [FeatureDescription(

   @"This will test just how 

will await a determined number of time
")]
    public partial class TestStrategyAwait
    {
        
        [Scenario]

        [Label("wait times fixed")]

        [Trait("Category", "Wait")]
        [InlineData(3)]
        [InlineData(1)]
        [InlineData(0)]
        [InlineData(-1)]

        public async Task WaitTime(int nrSeconds)

        {
           
            await Runner.RunScenarioAsync(

                    _ => When_Choosing_Fixed_Await_For_NRSECONDS_Seconds(nrSeconds),

                    _=> And_Await(),

                    _=> Then_Time_Ellapsed_Will_Be_NRSECONDS_Seconds(Math.Max(nrSeconds, 0))
                    )

                ;

        }
        [Scenario]

        [Label("wait times random")]

        [ScenarioCategory("Wait")]
        [InlineData(3,1)]
        [InlineData(1,2)]
        [InlineData(0,1)]
        [InlineData(-1,5)]

        public async Task WaitTimeRandom(int min,int max)

        {

            await Runner.RunScenarioAsync(

                    _ => When_Choosing_Random_Await_Between_MIN_And_MAX_Seconds(min,max),

                    _ => And_Await(),

                    _ => Then_Time_Ellapsed_Will_Be_Between_MIN_And_MAX_Seconds(Math.Max(min, 0),Math.Max(max,0))
                    )

                ;

        }
    }
}

 
You can find the code at https://github.com/ignatandrei/NetCoreRetarder/commits/version4_tests

Retarder – Making a strategy to wait–part 3

Now I want to make the await configurable. To remind , first is just a random delay ( or static delay). The others are

  1. Delay execution of some endpoints, based on how frequent are their uses

  2. Delay execution based on headers / query string /  routes
  3. Delay execution based on client IP
  4. Delay execution based on the response

The best design pattern for this is Strategy  https://en.wikipedia.org/wiki/Strategy_pattern . Also, because I want to apply multiple await types ( maybe in order, maybe to compose) , a simple Command Pattern will be enough.

So, let’s see how it looks now:

using System.Threading.Tasks;

namespace NetCoreRetarderCore
{
    public interface IStrategyAwait
    {
        Task<IStrategyAwait> AwaitDelay();
    }
}

So now let’s see the implementation of a StrategyAwaitFixed

using System;
using System.Threading.Tasks;

namespace NetCoreRetarderCore
{
    public class StrategyAwaitFixed : IStrategyAwait
    {
        private readonly int milliseconds;

        public StrategyAwaitFixed(int milliseconds)
        {
            this.milliseconds = milliseconds;
        }

        public async Task<IStrategyAwait> AwaitDelay()
        {
            Console.WriteLine($"waiting {milliseconds}");
            
            if (milliseconds < 1)
                return null;

            await Task.Delay(milliseconds);
            
            Console.WriteLine($"finished waiting {milliseconds}");
            
            return null;
        }
    }
}

Now the Retarder Middleware should apply the awaiter one by one

using Microsoft.AspNetCore.Http;
using System;
using System.Threading.Tasks;

namespace NetCoreRetarderCore
{
    public class RetarderMiddleware : IMiddleware
    {
        private IStrategyAwait strategyAwait;

        public RetarderMiddleware(IStrategyAwait strategyAwait)
        {
            this.strategyAwait = strategyAwait;
        }

        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            while (strategyAwait != null)
                strategyAwait = await strategyAwait?.AwaitDelay();
            
            await next(context);
            
        }
    }
}

The code from middleware extension is modified as generating a new random each time:

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Text;

namespace NetCoreRetarderCore
{
    public static class RetarderExtensions
    {
        public static void AddRetarder(this IServiceCollection serviceCollection)
        {
            serviceCollection
                .AddTransient<IStrategyAwait, StrategyAwaitFixed>(
                    it =>
                    {
                        var random = new Random();
                        var awaitMilliseconds = random.Next(1, 1000);
                        return new StrategyAwaitFixed(awaitMilliseconds);
                    }
                );
            serviceCollection.AddTransient<RetarderMiddleware>();
        }
        public static void UseRetarder(this IApplicationBuilder app)
        {
            app.UseMiddleware<RetarderMiddleware>();
        }

    }
}

Full Code Source at https://github.com/ignatandrei/NetCoreRetarder/commits/version3_Making_a_strategy_to_wait

Retarder- reorganizing the project to easy use- part 2

Now it is the moment to start reorganizing the project to be easy to use by other programmers. I want, instead of registering the services manually, to can use .AddRetarder and .UseRetarder. So I create a new project, NetCoreRetarderCore.csproj , and move there the RetarderMiddleware . The only new thing is the extension class RetarderExtensions

 

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Text;

namespace NetCoreRetarderCore
{
    public static class RetarderExtensions
    {
        public static void AddRetarder(this IServiceCollection serviceCollection)
        {
            serviceCollection.AddTransient<RetarderMiddleware>();
        }
        public static void UseRetarder(this IApplicationBuilder app)
        {
            app.UseMiddleware<RetarderMiddleware>();
        }

    }
}

 

This let’s me reorganize the Startup as

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NetCoreRetarderCore;

namespace NetCoreRetarder
{
    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.AddRetarder();
        }
        // 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();
            }

            app.UseRouting();

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

You can find code at https://github.com/ignatandrei/NetCoreRetarder/commits/version2_reorg

Andrei Ignat weekly software news(mostly .NET)

* indicates required

Please select all the ways you would like to hear from me:

You can unsubscribe at any time by clicking the link in the footer of our emails. For information about our privacy practices, please visit our website.

We use Mailchimp as our marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp's privacy practices here.