Category: .NET Core

Repairing the build and versioning–part 15

Now , after copy the plugins, the build is not working.

Thnking about the build events, I realize that to find same copy command on Linux and Windows is difficult – so Windows it is.

( and, BTW , In LInux the macro $(SolutionDir ) does not work use  repoRoot

https://docs.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?view=vs-2019

https://github.com/Microsoft/msbuild/issues/2397

)

Switch windows-latest on https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idruns-on 

Now tests are failing – repairing the tests.

I realize that I cannot build just the csproj – the solution sln knows the order in which to build the projects- but the .csproj does not. So now building the sln and then publish

Now giving error in Github, but not on local. More, there was a not repeatable . I figured was because of the parallel builds so this is the final line

dotnet build –configuration Release InfoValutar/InfoValutar.sln /m:1

Now it works!

The only programming thing that I have done is to add versioning , https://github.com/microsoft/aspnet-api-versioning/wiki/How-to-Version-Your-Service . I do tend to say that the URL is a Unique Identifier – so I want the versioning be part of the URL.

As advised by https://github.com/microsoft/aspnet-api-versioning/wiki/Versioning-via-the-URL-Path

I added the pacakage Microsoft.AspNetCore.Mvc.Versioning and

services.AddApiVersioning();

and on the controller

[ApiVersion( “1.0” )]

[Route(“api/v{version:apiVersion}/[controller]/[action]”)]

and now I can access

api/v1.0/TodayRates/banks

And one hour passes...

(This is the result of 1 hour per day auto-challenge as a full cycle developer for an exchange rates application)
NrPost 
1Start
2Reading NBR from internet
3Source control and build
4Badge and test
5CI and action
6Artifacts and dotnet try
7Docker with .NET Try
8ECB
9Intermezzo - Various implementations for programmers
10Intermezzo - similar code - options
11Plugin implementation
12GUI for console
13WebAPI
14Plugin in .NET Core 3
15Build and Versioning
16Add swagger
17Docker - first part
18Docker - second part
19Docker - build Azure

Plugin copy and caching- part 14

I have had 2 problems to solve

  1. How to put the plugins for the console and web api ?
  2. How to cache the data  ? ( plugins / exchange rates)

 

The solution for the first problem should be easy for the programmer PC, and for the deploying the application ( console/ web api/ etc )There are several solutions :

  1. Each plugin knows where to copy the dll ( console/web api)
  2. The application ( console / webapi) knows a special place from which to copy the plugin definition

 

I tend to agree with both – but mostly with the second. So … the idea is as follows:

The plugin builds and copy the output to a special folder, named “plugins” ( what a surprise, righyt?)

The console /webapi copies the folder plugins under his directory

 

The only problem here is that we should ensure that plugins are build first, even if the application does NOT have reference to them.

For this, there is a special menu in VS for solution, namely “build order” – you go to this –and after to the second tab, project dependencies. For the console and web I select as dependency the two loading projects – InfoValutarNBR and InfoValutarECB. The changes are reflected into the sln file

I could have done this on just the InfoValutarLoadingLibs project – but maybe I can have another way to load the libraries ( not so much for the moment )

So now I begin to modify what happens

after build events for InfoValutarNBR and InfoValutarECB

xcopy “$(TargetDir)*.*” “$(SolutionDir)plugins\$(ProjectName)” /E /F /I /Y /R

and pre build events for InfoValutarDOS and InfoValutarWebAPI.

Now, there are several things to do:

  1. Add to .gitignore the plugins folder to not put into the source control( ok)
  2. Write some documentation about the process
  3. Think about linux/docker compiling – there is a xcopy there  ?

 

About the documentation –  I just did. More, I create a InfoValutarPluginStarter , in order for others to copy this project and make a new plugin.

Realized also that the WebAPI could be simpler

[HttpGet(“{bank}”)]
public IAsyncEnumerable<ExchangeRates> Rates([FromRoute] string bank)

And also that I should throw another exception if Bank is null or empty:

if(string.IsNullOrWhiteSpace(bank))
throw new ArgumentNullException($”{nameof(bank)} cannot be empty”);

 

For docker compiling – next time

And one hour passes...

(This is the result of 1 hour per day auto-challenge as a full cycle developer for an exchange rates application)
NrPost 
1Start
2Reading NBR from internet
3Source control and build
4Badge and test
5CI and action
6Artifacts and dotnet try
7Docker with .NET Try
8ECB
9Intermezzo - Various implementations for programmers
10Intermezzo - similar code - options
11Plugin implementation
12GUI for console
13WebAPI
14Plugin in .NET Core 3
15Build and Versioning
16Add swagger
17Docker - first part
18Docker - second part
19Docker - build Azure

Creating the WebAPI–part 13

I am now tired of continuously improving the console application – so now some work that others can see really.  Creating the WebAPI is simple as a project  – and I was amazed to see a WeatherForecast controller.

I need just to be able to see the today exchange rates via API . So created a TodayRates , modify the launchSettings.json in order to start with my controller.

I need the plugin that loads to be a global dependency. So I add as a Singletion in the services

services.AddSingleton< LoadExchangeProviders>(new LoadExchangeProviders(“plugins”));

and then I inject into the controller constructor ( I can do in the method also, but many methods needs the same dependency )

public TodayRates(ILogger<TodayRates> logger, LoadExchangeProviders prov)
{
_logger = logger;
_prov = prov;
}

 

Now I need a method to see what are the banks / plugins that are loaded . Linq to the rescue :

 

[HttpGet]
public IEnumerable<string> Banks()
{
return _prov
.LoadExchange()
.Select(it=>it.Bank)
.ToArray();
}

Now we should load the data from the provider to show actual rates

public async IAsyncEnumerable<ExchangeRates> Rates(string bank)
{
var provBank = _prov
.LoadExchange()
.FirstOrDefault(it => string.Equals(bank, it.Bank, StringComparison.InvariantCultureIgnoreCase));

switch (provBank)
{
case null:
yield return null;
break;
default:
{
await foreach (var data in provBank.GetActualRates())
{
yield return data;
}

break;
}
}

}

 

All good. There are  3 problems:

  1. How to put the plugins for the console and web api ?
  2. How to cache the data  ? ( plugins / exchange rates)
  3. Should we throw an exception when a bank does not exists, instead of returning null ?

 

Thinking of this code, I realized that I begin to put logic into controllers. So – let’s move from the controller to the business.

For example, now I have this:

[HttpGet]
public IEnumerable<string> Banks()
{
return _prov.Banks();
}

 

And this

[HttpGet]
public IAsyncEnumerable<ExchangeRates> Rates(string bank)
{
return _prov.Rates(bank);
}

So now I can change in the Business Layer without problems.

Looking at the Console Application vs  WebAPI, I realized that are different. The console is loading the whole data from the providers – the WebAPI is loading on demand.  We could do for the console the same behavior  – but the console is loading very fast all data.

For the answer at

Should we throw an exception when a bank does not exists, instead of returning null ?

– seems to be yes.

So instead of

case null:
yield return null;
I put

case null:
throw new ArgumentOutOfRangeException(nameof(bank),$”cannot find {bank}”);

 

For the 2 questions, the answer next time.

 

And one hour passes...

(This is the result of 1 hour per day auto-challenge as a full cycle developer for an exchange rates application)
NrPost 
1Start
2Reading NBR from internet
3Source control and build
4Badge and test
5CI and action
6Artifacts and dotnet try
7Docker with .NET Try
8ECB
9Intermezzo - Various implementations for programmers
10Intermezzo - similar code - options
11Plugin implementation
12GUI for console
13WebAPI
14Plugin in .NET Core 3
15Build and Versioning
16Add swagger
17Docker - first part
18Docker - second part
19Docker - build Azure

Documentation and refactoring for plugins and wrong code coverage – part 12

I realized that will be no documentation for plugins – until I create one .

Figured also that each plugin should have a name  -to understand from where he grabs data – so some code involved code.

 

Asking when and if the plugins are copied to the application  – not found yet an answer…

 

Now the user can choose what bank he wants to see data ? We could do this for the console application –  and I think that is the moment to play with Icaza  Gui Terminal https://github.com/migueldeicaza/gui.cs  – Created buttons to display plugins (NBR and ECB ). Will not work if many plugins will come – but it is ok for the moment.

Discovered the window for code coverage – read https://github.com/tonerdo/coverlet/blob/master/Documentation/VSTestIntegration.md

Some error when writing the .runsettings file – discovered from command line ( GUI does not show)

Reading the docs , https://docs.microsoft.com/en-us/visualstudio/test/using-code-coverage-to-determine-how-much-code-is-being-tested?view=vs-2019#analyze-code-coverage , it says that is not available for community…

Back to square 1 after 1 hour- just made documentation.

And one hour passes...

(This is the result of 1 hour per day auto-challenge as a full cycle developer for an exchange rates application)
NrPost 
1Start
2Reading NBR from internet
3Source control and build
4Badge and test
5CI and action
6Artifacts and dotnet try
7Docker with .NET Try
8ECB
9Intermezzo - Various implementations for programmers
10Intermezzo - similar code - options
11Plugin implementation
12GUI for console
13WebAPI
14Plugin in .NET Core 3
15Build and Versioning
16Add swagger
17Docker - first part
18Docker - second part
19Docker - build Azure

Loading data from NBR / ECB as plugins–part 11

Modified project – instead of one project that have the NBR and ECB exchange rates,I have now 4
1. Common interfaces / classes

2 NBR loading( first plugin)

3 ECB loading ( second plugin)

4 plugin loading project

 

Going to .NET Standard 2.1 and trying to load the NuGet https://github.com/natemcmaster/DotNetCorePlugins/blob/master/docs/what-are-shared-types.md .

Does not load. Going back to .NET Core 2.0  – some result.

RTFM – not supported .NET Standard  https://github.com/natemcmaster/DotNetCorePlugins/issues/24

So created a .NET Standard dll and changed from

<TargetFramework>netstandard2.0</TargetFramework>

to

<TargetFramework>netcoreapp3.0</TargetFramework>

Copied the code from https://github.com/natemcmaster/DotNetCorePlugins/blob/master/samples/hello-world/HostApp/Program.cs .

Loaded 0 plugins.

Put plugins in plugins directory – again 0 plugins.

Read the code copied – I must create separate folders for each plugin – makes sense, because we can have plugin diamond dependency – see here https://github.com/natemcmaster/DotNetCorePlugins/blob/master/docs/what-are-shared-types.md

Put in separate folder – error in this line

// This assumes the implementation of IPlugin has a parameterless constructor
var plugin = Activator.CreateInstance(pluginType) as BankGetExchange;

 

Apparently , this code does NOT mean to have a parameterless contructor – even if seems from the code


public class GetNBRExchange: BankGetExchange
{
private readonly HttpClient httpClient;

public GetNBRExchange(HttpMessageHandler handler = null)
{
if (handler != null)
httpClient = new HttpClient(handler, disposeHandler: false);
else
httpClient = new HttpClient();
}

 

So small modification to preserve tests and default code:


public class GetNBRExchange: BankGetExchange
{
private readonly HttpClient httpClient;
public GetNBRExchange() : this(null)
{

}
public GetNBRExchange(HttpMessageHandler handler)
{
if (handler != null)
httpClient = new HttpClient(handler, disposeHandler: false);
else
httpClient = new HttpClient();
}

Now it works …

You can find modifications here

https://github.com/ignatandrei/InfoValutar/commit/0e4cf6b83bb5f4cbab71e9e8c96ae719771fcae1 – total 13 changed files….

And one hour passes...

(This is the result of 1 hour per day auto-challenge as a full cycle developer for an exchange rates application)
NrPost 
1Start
2Reading NBR from internet
3Source control and build
4Badge and test
5CI and action
6Artifacts and dotnet try
7Docker with .NET Try
8ECB
9Intermezzo - Various implementations for programmers
10Intermezzo - similar code - options
11Plugin implementation
12GUI for console
13WebAPI
14Plugin in .NET Core 3
15Build and Versioning
16Add swagger
17Docker - first part
18Docker - second part
19Docker - build Azure

Intermezzo–program based on experience–part 9

This post will be somewhat related to https://www.smart-jokes.org/programmer-evolution.html – but it makes total sense for me.

Let’s take what we achieved until now: We have 2 national banks –  and we gather exchange rates from there. What if we want to add more banks  -or let other programmers to add their dll’s to our software to just display the exchange rates from other banks ?

Let’s say now that the requirements are to to display the exchange rates for 2 national banks into a console program .  The manager says that later we may add other banks to load exchange rates . I will try to explain some differences how the code is created, depending on the experience.

Junior programmer: – all the code inside


int Main(){

// code for load and display the first bank exchange rates

// code for load and display the second bank exchange rates

}

Internship programmer: – create classes


//create  2 classes ( first bank, second bank ) and a Load method

int Main(){

var firstBank = new FirstBank();

var data = firstBank.Load();

DisplayData(data);

//same for second bank

}

Seasoned Programmer: – creates interfaces


//add to the 2 classes an interface , loading into a list, loading data for all the list, display

int Main(){

var l = new List<IExchangeRates[]>;
{
new GetFirstBank()),
new GetSecondBank()),

};

foreach(var rates in l){

var data= rates.Load();

DisplayData(data);

}

 

Until now, it is business as usual. But, from now on, there are many things that can be done , depending on programmer experience and will to do things ( until some error arrives and there is an official task for it) :

  1. How to handle errors ( Polly ? )
  2. How to load different libraries that contains our interface ( writing code that loads directly a list with all classes / DI / reflection / config files / plugin loader )
  3. Think about performance ( loading data in parallel / caching )
  4. Add Tests + code coverage
  5. CI / CD

( if I do miss something, please put on comments)

 

What is my point here ? Is that I expect any programmer to go very fast to the level of Seasoned Programmer. But , after that, the knowledge of each programmer is relevant about how he does the points above ( or, if he does not …)

And one hour passes...

(This is the result of 1 hour per day auto-challenge as a full cycle developer for an exchange rates application)
NrPost 
1Start
2Reading NBR from internet
3Source control and build
4Badge and test
5CI and action
6Artifacts and dotnet try
7Docker with .NET Try
8ECB
9Intermezzo - Various implementations for programmers
10Intermezzo - similar code - options
11Plugin implementation
12GUI for console
13WebAPI
14Plugin in .NET Core 3
15Build and Versioning
16Add swagger
17Docker - first part
18Docker - second part
19Docker - build Azure

European Central Bank- part 8

Because I have had last time a flop, now I decide to get the European Central Bank rates.

Going to https://www.ecb.europa.eu/stats/policy_and_exchange_rates/euro_reference_exchange_rates/html/index.en.html show the XML –  but not the XSD.

So now it is about how to transform from XML to C# classes –  and I remembered a discussion with https://siderite.dev/ – Visual Studio does now how to paste from XML to classes.  ( Edit=>PasteSpecial +> Paste XML as classes. Ensure that you have not space on the first line…)

Copy and paste fron BNR class, modify the URL to https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml

Now the program.cs looks like this

 


var nbr = new GetNBRExchange();
var list = nbr.GetActualRates();
await foreach (var e in list)
{
     Console.WriteLine($"1 {e.ExchangeFrom} = {e.ExchangeValue} {e.ExchangeTo}");
}

var ecb = new GetECBExchange();
list = ecb.GetActualRates();
await foreach (var e in list)
{
     Console.WriteLine($"1 {e.ExchangeFrom} = {e.ExchangeValue} {e.ExchangeTo}");
}

See the similarities ?

It is time for some interfaces . The simplest one is


public interface BankGetExchange
{
IAsyncEnumerable<ExchangeRates> GetActualRates();
}

And now the Program.cs looks like

static async Task Main(string[] args)
{

await ShowValues(new GetNBRExchange());
await ShowValues(new GetECBExchange());
}
public static async Task ShowValues(BankGetExchange bank)
{
var list = bank.GetActualRates();
await foreach (var e in list)
{
Console.WriteLine($"1 {e.ExchangeFrom} = {e.ExchangeValue} {e.ExchangeTo}");
}
}

and seems better now!

What can be improved ?  Well , to process both in parallel ( now they are processed one after another)
Another condition is not to display intermixed. So the code could stay like

So I ended with the following:

using InfoValutarShared;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System.Xml.Serialization;

namespace InfoValutarDOS
{
    class Program
    {
        static async Task Main()
        {
            var l = new List<Task<ExchangeRates[]>>
            {
                Rates(new GetNBRExchange()),
                Rates(new GetECBExchange()),

            };
            
            while (l.Count > 0)
            {
                var x = l.ToArray();

                var data = await Task.WhenAny(x);
                ShowValues(await data);
                l.Remove(data);
            }
        }
        public static async Task<ExchangeRates[]> Rates(BankGetExchange bank)
        {
            var list = bank.GetActualRates();
            return await list.ToArrayAsync();
            
                        
        }
        public static void ShowValues(ExchangeRates[] list)
        {
            foreach (var e in list)
            {
                Console.WriteLine($"1 {e.ExchangeFrom} = {e.ExchangeValue} {e.ExchangeTo}");
            }
        }


    }
}
And one hour passes...

(This is the result of 1 hour per day auto-challenge as a full cycle developer for an exchange rates application)
NrPost 
1Start
2Reading NBR from internet
3Source control and build
4Badge and test
5CI and action
6Artifacts and dotnet try
7Docker with .NET Try
8ECB
9Intermezzo - Various implementations for programmers
10Intermezzo - similar code - options
11Plugin implementation
12GUI for console
13WebAPI
14Plugin in .NET Core 3
15Build and Versioning
16Add swagger
17Docker - first part
18Docker - second part
19Docker - build Azure

Try to show the result online in docker with dotnet try- part 7

Trying to put the dotnet try in docker

Each line from here is taking time – save this as docker.console file

FROM mcr.microsoft.com/dotnet/core/sdk:3.0
RUN dotnet tool install -g –add-source “https://dotnet.myget.org/F/dotnet-try/api/v3/index.json” dotnet-try
ENV PATH=”$PATH:/root/.dotnet/tools”
#ENV ASPNETCORE_URLS=http://+:5000
CMD dotnet try –verbose –port 443
EXPOSE 443

 

I can run this by

docker build -f ./console.docker . -t drop
docker run –rm -it -p 5000:5000/tcp drop:latest

However , I cannot find a way to see the output of the dotnet try in the browser.

( Problem show below) .Raised a feature request

https://github.com/dotnet/try/issues/590

So spent time, but not happy ending
And one hour passes...

(This is the result of 1 hour per day auto-challenge as a full cycle developer for an exchange rates application)

NrPost 
1Start
2Reading NBR from internet
3Source control and build
4Badge and test
5CI and action
6Artifacts and dotnet try
7Docker with .NET Try
8ECB
9Intermezzo - Various implementations for programmers
10Intermezzo - similar code - options
11Plugin implementation
12GUI for console
13WebAPI
14Plugin in .NET Core 3
15Build and Versioning
16Add swagger
17Docker - first part
18Docker - second part
19Docker - build Azure

Show the software- artifacts and dotnettry –part 6

So I was about artifacts – what if I put the console as an artifact , to can show the work to other people ?  Read about artifacts at Github Actions at https://help.github.com/en/github/automating-your-workflow-with-github-actions/persisting-workflow-data-using-artifacts . Seems easy.  One indentation problem later and I have a 34 MB application that is attached to the current action and it is listing the exchange rates . Good.

Now , after I have a working copy, I want to write all the ideas that this exchange rates application can do – put those on GitHub Issues –  see https://github.com/ignatandrei/InfoValutar/issues

Let’s try this:  https://github.com/ignatandrei/InfoValutar/issues/3 . : I want to others to play with my application – and dotnetry is an obvious choice.

Created a ConsoleDOS.md with the following

# Console

“`cs –source-file ./../InfoValutar/InfoValutarDOS/Program.cs –project ./../InfoValutar/InfoValutarDOS/InfoValutarDOS.csproj

“`

Running dotnet try in the folder  shows the file, however , when  running the program, error occurs

Program.cs(18,13): error CS8652: The feature ‘async streams’ is currently in Preview and *unsupported*. To use Preview features, use the ‘preview’ language version.

What? The project is compiling ok…

Trying to run the latest version of dotnet try

dotnet tool install -g –add-source “https://dotnet.myget.org/F/dotnet-try/api/v3/index.json” dotnet-try

and running my project again shows a different error:

 

Unhandled Exception: Unhandled exception. System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
 ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
 ---> System.ComponentModel.Win32Exception (0x80090304): The Local Security Authority cannot be contacted
   --- End of inner exception stack trace ---

Now it is time to solve this!

Apparently, reading again https://bnro.ro/Cursurile-pietei-valutare-in-format-XML-3424.aspx shows that needs TLS1.2

So this is the solution:


ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;

And one hour passes...

(This is the result of 1 hour per day auto-challenge as a full cycle developer for an exchange rates application)
NrPost 
1Start
2Reading NBR from internet
3Source control and build
4Badge and test
5CI and action
6Artifacts and dotnet try
7Docker with .NET Try
8ECB
9Intermezzo - Various implementations for programmers
10Intermezzo - similar code - options
11Plugin implementation
12GUI for console
13WebAPI
14Plugin in .NET Core 3
15Build and Versioning
16Add swagger
17Docker - first part
18Docker - second part
19Docker - build Azure

Running CI tests- part 5

Now I want that every time I commit the project , the tests should be run. I do not want all the test, but just the ones without external dependencies.

For that , I decorate with

[Trait(“External”, “0”)]

and

[Trait(“External”, “1”)]

the ones that are using just local resources, respectively the internet.

I modify the dotnetcore.yml from Github to run the test by adding

– name: run tests

run: dotnet test –filter “External=0” InfoValutar/InfovalutarTest/InfovalutarTest.csproj

( be aware that VS does not work well with Github actions – I made from Github desktop)

( encounter an error because Data vs data  – Linux is case sensitive –  Windows is spoiling me)

Trying now to find how to put an issue automatically when something is wrong

Found https://github.com/technote-space/toc-generator  – good to have. No work from the first try. Modifying the yml to support more. Does not work, no matter what I do… Remains in the project- maybe latter.

Not found anything  similar. Thinking better , this is something that should be at the Action / Job level, not adding specific step. Searching at Settings / Actions, Settings / Notifications – nothing.

Searching the documentation – nothing ( GitHub actions still in beta) . However, found something interesting ; any action has artifacts.

Good . But time is up….

And one hour passes...

(This is the result of 1 hour per day auto-challenge as a full cycle developer for an exchange rates application)
NrPost 
1Start
2Reading NBR from internet
3Source control and build
4Badge and test
5CI and action
6Artifacts and dotnet try
7Docker with .NET Try
8ECB
9Intermezzo - Various implementations for programmers
10Intermezzo - similar code - options
11Plugin implementation
12GUI for console
13WebAPI
14Plugin in .NET Core 3
15Build and Versioning
16Add swagger
17Docker - first part
18Docker - second part
19Docker - build Azure

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.