Category: .NET Core

[PostEvent] HackTalks 2019

I have been a presenter and participant at #HackTalks Timisoara. I have seen lots of interesting people that do great stuff. Also lots of programmers interested of how to do better programming. It is a good vibe and lots of knowledge sharing.

As a participant , I can mention:

https://www.facebook.com/hacktalks.ro/posts/823295824708035 : Container Orchestration in the cloud for oncology  – data science and programming together for the greater good

https://www.facebook.com/hacktalks.ro/posts/821329954904622 : Platform revolution in mobility –  very informative and opinionated

 

https://www.facebook.com/hacktalks.ro/posts/820025591701725 : How to #model and #visualize complex conditional logic using a graph that describes a finite state machine in a highly flexible and data-driven way – nice way in JavaScript to describe logic flow with data .

 

My presentation was about Angular and .NET Core Everywhere and you can find the source code at https://github.com/ignatandrei/AngNetCoreDemo

Obtaining list of videos from a youtube playlist with Stankins

Because Stankins has a parser to html, you can have this kind of macro

#see all href

Stankins.HTML.ReceiverHtmlAHref file=https://www.youtube.com/playlist?list=PL4aSKgR4yk4OnmJW6PlBuDOXdYk6zTGps

#retain href column
StankinsObjects.FilterRetainColumnDataContains namecolumn=href stringToHave=PL4aSKgR4yk4OnmJW6PlBuDOXdYk6zTGps

#retain just videos which title contains 5Min

StankinsObjects.FilterRetainColumnDataContains namecolumn=a_text stringToHave=5Min

#remove column a_html

StankinsObjects.FilterRemoveColumn namecolumn=a_html

#transform column to li a href

StankinsObjects.TransformerAddColumnExpressionByTable expression=”‘<li><a href=’+ href+’>’ + a_text+'</a></li>'” newcolumnname=test nametable=TableLinks

#remove column href

StankinsObjects.FilterRemoveColumn namecolumn=href

#remove column a_text

StankinsObjects.FilterRemoveColumn namecolumn=a_text

 

The demo is at

https://azurestankins.azurewebsites.net/recipe/youtubeplaylist

DotNet CLI Tools

There is a revival of CLI tools. And dotnet is going with the wave.

You can find the tools installed with .NET Core here , https://docs.microsoft.com/en-us/dotnet/core/tools/?tabs=netcore2x  and here https://docs.microsoft.com/en-us/dotnet/core/additional-tools/

 

Usually you do not need those – since Visual Studio is good to have all from the GUI.

However, you can make your own tool – and you have the instruction here: https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools-how-to-create

But, before re-inventing the wheel, take a look at the list here https://github.com/natemcmaster/dotnet-tools

I have used for AzureDevOps CI:

  1.  https://github.com/tonerdo/coverlet 
  2. https://github.com/ignatandrei/AOP_With_Roslyn
  3. https://github.com/loresoft/DotNet.Property
  4. https://github.com/SonarSource/sonar-scanner-msbuild
  5. https://github.com/KrystianKolad/DotnetThx
  6. https://github.com/danielpalme/ReportGenerator/
  7. https://github.com/Hubert-Rybak/dotnet-warp
  8. https://github.com/aspnet/AspNetCore/tree/master/src/Tools/dotnet-watch

 

Did you use some CLI tools?

Small Task quiz knowledge

The task and the await /async never ceases to amaze me. That because there is so much under covers( see https://channel9.msdn.com/Events/TechDays/Techdays-2014-the-Netherlands/Async-programming-deep-dive )

Let’s say that we have this code that awaits and throws ( or not ) an exception:


        static async Task<bool> test(int delaySeconds, Exception throwEx)
        {
            Console.WriteLine($"enter {delaySeconds} ");
            await Task.Delay(delaySeconds * 1000);
            Console.WriteLine($"finish waiting {delaySeconds} and throw Exception:  {throwEx.Message}");

            if (throwEx != null)
            {
                throw throwEx;//never do this in production
            }
            return true;
        }

First problem:  Error order

What this code will produce as output for the line Console.WriteLine(“err ” + ex.GetType().Name);  ?


            Console.WriteLine("start");
            var t10Ex = test(delaySeconds: 10, throwEx: new ArgumentException("problem with argument"));
            var t19Ex = test(delaySeconds: 19, throwEx: new DllNotFoundException("not found dll"));
            var t100NotEx = test(delaySeconds: 100, throwEx: null);

            try
            {
                await Task.WhenAll(new Task[] { t19Ex, t10Ex, t100NotEx });
            
            }
            catch (Exception ex)
            {
                Console.WriteLine("err " + ex.GetType().Name);

            }
            finally
            {
                Console.WriteLine("in finally");

            }
            Console.WriteLine("waiting in main");
            await Task.Delay(100 * 1000);
            Console.WriteLine("finish");

Answer: err DllNotFoundException
. That is because the await it produce error for the first  item in the array  , not for the first item in chronological order. More, it will wait for all tasks to finish.

Second problem: Grab all  errors

How do you modify the above code to grab all errors ?
Answer: By introducing a new variable, tAll . Attention: the exception is the same ( dllNotFound), however, the whole AggregateException is in tAll.Exception

            Task tAll=null;
            try
            {
                tAll = Task.WhenAll(new Task[] { t19Ex, t10Ex, t100NotEx });
                await tAll;
            
            }
            catch (Exception ex)
            {
                Console.WriteLine("err " + ex.GetType().Name);
                Console.WriteLine(" all exception here:" + tAll.Exception);

            }
            finally
            {
                Console.WriteLine("in finally");

            }

Third problem: Continue With
Let’s modify slightly the code for using ContinueWith. When the “in continue with ” will be displayed ?


            Console.WriteLine("start");
            var t10Ex = test(delaySeconds: 10, throwEx: new ArgumentException("problem with argument"));
            var t20NotEx = test(delaySeconds: 20, throwEx: null);
            var t10ContinueWith = t10Ex.ContinueWith(async (previousTask) =>
            {
                await Task.Delay(25 * 1000);
                Console.WriteLine("in continue with " + previousTask.IsCompletedSuccessfully);

            }).Unwrap();
            try
            {
                await Task.WhenAll(t10Ex, t20NotEx );
                
            
            }
            catch (Exception ex)
            {
                Console.WriteLine("err " + ex.GetType().Name);

            }
            finally
            {
                Console.WriteLine("in finally");

            }
            Console.WriteLine("waiting in main");
            await Task.Delay(100 * 1000);
            Console.WriteLine("finish");

Answer: : AFTER displaying “waiting in main” . Basically, you do not wait for ContinueWith, you wait for the first task in the ContinueWith construct ( t10Ex in our case)

And a question for you : Why do we need Unwrap ? ( Hint: try the code without and see the exception!)

C# and Null object

There is a lot to talk i n programming that null is bad , for example https://www.yegor256.com/2014/05/13/why-null-is-bad.html   and https://www.lucidchart.com/techblog/2015/08/31/the-worst-mistake-of-computer-science/ .

In C# we have a love-hate relationship with null :

Love : Because  the value type / struct cannot be null, C# creators invented Nullable<T> https://docs.microsoft.com/en-us/dotnet/api/system.nullable-1

Hate: Because  reference types / classes can be null, c# 8.0 invented nullable reference types : https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/nullable-reference-types – no doubt from me that will be in the language

I want to add my 2 cents here : Null is sometime good

1. For databases , a null field is different from a 0 field ( see this toilet paper null vs 0 as a reference : https://pbs.twimg.com/media/C7ojxbSVAAEX9Bt.jpg )

2. For finding if a item exists in an array of value types, how do you find that the value that you want to search exists or not with .FirstOrDefault ? If the array is composed of reference, you can compare with null ….

7 Steps from (Angular) WebApplication to Windows Store

I have deployed fairly easy the .NET Core Alphabet applcation to Windows Store . Supposing that you already have registered the name of the application to the windows store (https://partner.microsoft.com/en-us/dashboard/windows/overview )

Those are the steps:

1. Compile the Angular Application ( with ng build –prod –build-optimizer  )

2. Create a new WinJS App for Universal Windows

3. Copy the results ( index.html and js )from the point 1 to a dist folder

4. Save the files with BOM ( either from VS, Save as, then save with encoding, either using dos2unix –m  <namefile>

5. Modify the main.js by adding : window.location.href=’dist/index.html’;    

6. Modify the Visual Assets to the images of your application

7. Right click the application, Store => Create App Package . And deploy to the store

As a Proof of concept, see https://www.microsoft.com/en-us/p/netcorealphabet/9pdx9rv0xhds#activetab=pivot:overviewtab 

Also, all the source code it is at https://github.com/ignatandrei/netCoreAlphabet ( and you may want to contribute, right?)

C# integration testing in AzureDevOps with Docker containers– SqlServer and Cachet example

Every software that we make depends on others. For Stankins , as a general ETL data, it is more important to be tested with real data providers.For example, we may want to take data from Sql Server and send to Cachet . How can we have a SqlServer and a Cachet up and running easy ? The obvious answer our days is Docker.

Let’s see how a test for SqlServer looks

using FluentAssertions;
using Stankins.Alive;
using Stankins.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xbehave;
using Xunit;

namespace StankinsTestXUnit
{
    [Trait("ReceiverSqlServer", "")]
    [Trait("ExternalDependency","SqlServer")]
    public class TestReceiverSqlServer
    {
        [Scenario]
        [Example("Server=(local);Database=master;User Id=SA;Password = <YourStrong!Passw0rd>;")]
        public void TestReceiverDBServer(string connectionString)
        {
            IReceive status = null;
            IDataToSent data = null;
            $"Assume Sql Server instance {connectionString} exists , if not see docker folder".w(() => {

            });
            $"When I create the ReceiverDBServer ".w(() => status = new ReceiverDBSqlServer(connectionString));
            $"and receive data".w(async () =>
            {
                data = await status.TransformData(null);
            });
            $"the data should have a table".w(() =>
            {
                data.DataToBeSentFurther.Count.Should().Be(1);
            });
            $"and the result should be true".w(() =>
            {
                data.DataToBeSentFurther[0].Rows[0]["IsSuccess"].Should().Be(true);
            });


        }
    }
}

and for cachet :



using FluentAssertions;
using Stankins.FileOps;
using Stankins.Interfaces;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Stankins.Rest;
using Xbehave;
using Xunit;
using static System.Environment;
using Stankins.Trello;
using Stankins.Cachet;

namespace StankinsTestXUnit
{
    [Trait("Cachet", "")]
    [Trait("ExternalDependency", "Cachet")]
    public class TestSenderCachet
    {
        [Scenario]
        [Example("Assets/JSON/CachetV1Simple.txt", 3)]
        public void TestSimpleJSON(string fileName,int NumberRows)
        {
            IReceive receiver = null;
           
            IDataToSent data=null;
            var nl = Environment.NewLine;
            $"Given the file {fileName}".w(() =>
            {
                File.Exists(fileName).Should().BeTrue();
            });
            $"When I create the {nameof(ReceiveRest)} for the {fileName}".w(() => receiver = new ReceiveRestFromFile(fileName));
            $"And I read the data".w(async () =>data= await receiver.TransformData(null));
            $"Then should be a data".w(() => data.Should().NotBeNull());
            $"With a table".w(() =>
            {
                data.DataToBeSentFurther.Should().NotBeNull();
                data.DataToBeSentFurther.Count.Should().Be(1);
            });
            $"The number of rows should be {NumberRows}".w(() => data.DataToBeSentFurther[0].Rows.Count.Should().Be(NumberRows));
            $"and now I transform with {nameof(SenderCachet)}".w(async ()=>
                data=await new SenderCachet("http://localhost:8000","5DiHQgKbsJqck4TWhMVO").TransformData(data)
            );

        } 

    }
}

( I have use XBehave for extensions)

Nice and easy , right ? Not so!

For up and running SqlServer I have used a docker compose file

version: '3'
services:
   db:
     image: mcr.microsoft.com/mssql/server
     ports:
       - "1433:1433"
     environment:
       SA_PASSWORD: "<YourStrong!Passw0rd>"
       ACCEPT_EULA: "Y"
     healthcheck:
       test: sqlcmd -S (local) -U SA -P '<YourStrong!Passw0rd>' -Q 'select 1'

and in AzureDevOps yaml start the containers, run the tests, collect the code coverage, stop the containers

docker-compose -f stankinsv2/solution/StankinsV2/StankinsTestXUnit/Docker/docker-sqlserver-instance-linux.yaml up -d  
        

echo 'start regular test'
        
         dotnet build -c $(buildConfiguration) stankinsv2/solution/StankinsV2/StankinsV2.sln
        
         dotnet test stankinsv2/solution/StankinsV2/StankinsTestXUnit/StankinsTestXUnit.csproj --logger trx  --logger "console;verbosity=normal" --collect "Code coverage"
         echo 'coverlet'
         coverlet stankinsv2/solution/StankinsV2/StankinsTestXUnit/bin/$(buildConfiguration)/netcoreapp2.2/StankinsTestXUnit.dll --target "dotnet" --targetargs "test stankinsv2/solution/StankinsV2/StankinsTestXUnit/StankinsTestXUnit.csproj --configuration $(buildConfiguration) --no-build" --format opencover --exclude "[xunit*]*"
        
         echo 'compose down'
         docker-compose -f stankinsv2/solution/StankinsV2/StankinsTestXUnit/Docker/docker-sqlserver-instance-linux.yaml down
        

Easy, right ? That’s because SqlServer is well behaved and has a fully functional image on Docker

That is not so easy with Cachet . Cachet requires configuration – and more, after configuration, it generates a random token for write data  ( http://localhost:8000","5DiHQgKbsJqck4TWhMVO ) .

So it will be a task for docker to export the container and import again  - easy stuff, right ? Again, not.

So I start a small docker container with

docker run -p 8000:8000 –name myCachetContainer -e APP_KEY=base64:ybug5it9Koxwhfi5a6CORbWdpjVqXxkz/Tyj4K45GKc= -e DEBUG=false -e DB_DRIVER=sqlite cachethq/docker

and then browsing to http://localhost:8000 I have configured and grab the token

Now it is time to export :

docker export myCachetContainer -o cachet.tar

And to import as an image

docker import cachet.tar  mycac

And to run the image again

docker run -p 8000:8000  -e APP_KEY=base64:ybug5it9Koxwhfi5a6CORbWdpjVqXxkz/Tyj4K45GKc= -e DEBUG=false -e DB_DRIVER=sqlite cachethq/docker

And the image stopped! After many tries and docker inspect the initial image , I have resulted to

docker run -it -p 8000:8000 -e APP_KEY=base64:ybug5it9Koxwhfi5a6CORbWdpjVqXxkz/Tyj4K45GKc= -e DEBUG=false -e DB_DRIVER=sqlite --workdir /var/www/html --user 1001:1001 mycac "/sbin/entrypoint.sh"

So the workdir, user, and the entry point are not copied into the image and you should do youurself.

The final preparing for CI with Docker for Cachet ? I have docker push myimage to Docker Hub , and I will run it from docker compose.

So now my docker compose with sql server and cachet looks this way

version: '3'
services:
   db:
     image: mcr.microsoft.com/mssql/server
     ports:
       - "1433:1433"
     environment:
       SA_PASSWORD: "<YourStrong!Passw0rd>"
       ACCEPT_EULA: "Y"
     healthcheck:
       test: sqlcmd -S (local) -U SA -P '<YourStrong!Passw0rd>' -Q 'select 1'

  cachet:
     image: ignatandrei/ci_cachet
     ports:
       - "8000:8000"
      
     environment:
       APP_KEY: "base64:ybug5it9Koxwhfi5a6CORbWdpjVqXxkz/Tyj4K45GKc="
       DEBUG: "false"
       DB_DRIVER: "sqlite"
       
     user: "1001"   
     working_dir: "/var/www/html"
     entrypoint: "/sbin/entrypoint.sh"

And I have a nice C# integration tests with Azure Devops, Docker, Sql Server and Cachet ! You can see the code coverage report at https://codecov.io/gh/ignatandrei/stankins/src/master/stankinsv2/solution/StankinsV2/Stankins.Cachet/SenderCachet.cs

.NET Core Alphabet

What I wanted is a simple application ( Web, Mobile, Desktop) that can list , alphabetically, the .NET Core keywords. What is the purpose?

  1. For interviews – suppose you want to test the people knowledge in C#. You start the application( again: Desktop or Web or Mobile) and let the candidate choose a letter. Then you see the keywords for this letter and ask the candidate to explain some of the keywords
  2. For remembering features: there are so many features in .NET language (  https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history ) that for a programmer it is good to know – or to revisit – the features that are in the language.
  3. For contest within programmers  – like the interviews, but for the passionate programmers that want to have an easy way to decide the one with the best memory
  4. Maybe other uses that I do not know  ? Please share in comments

Now with the realization: What I want is the simple application, that has inside the database with keywords and links and any others. From this database, the code sources for the data will be generated and the application(s) will be generated. Also, data should be publicly available to profit from the crowd power –anyone that want to add something can add.

stankins.console execute -o ReceiveRestFromFile -a primaryData/netCoreAlphabet.json -o SenderToTypeScript -a “” -o TransformerConcatenateOutputString -a a.ts -o SenderOutputToFolder -a $(Build.ArtifactStagingDirectory)/data/ -a false

stankins.console execute -o ReceiveRestFromFile -a primaryData/netCoreAlphabet.json -o SenderToRazorFromFile -a primaryData/markdown.txt -o TransformerConcatenateOutputString -a cards.md -o SenderOutputToFolder -a $(Build.ArtifactStagingDirectory)/data/ -a false
 

And to complete all those, it will be put in an AzureDevops pipeline https://github.com/ignatandrei/netCoreAlphabet/blob/master/azure-pipelines.yml 

You can see the result on Android : https://play.google.com/store/apps/details?id=com.github.ignatandrei.netcorealphabet&hl=en  , WebSite: https://ignatandrei.github.io/netCoreAlphabet

Also, if you want , please contribute by making a PR by editing https://github.com/ignatandrei/netCoreAlphabet/blob/master/primaryData/netCoreAlphabet.json or by contributing to enchance the application by solving https://github.com/ignatandrei/netCoreAlphabet/issues

Create a new exception–add fields and/or properties

This post is not about why we need custom exception (https://blogs.msdn.microsoft.com/jaredpar/2008/10/20/custom-exceptions-when-should-you-create-them/ ) . It is (more a rant ) about a specific  item in best practices in Exceptions( https://docs.microsoft.com/en-us/dotnet/standard/exceptions/best-practices-for-exceptions )

It says:

In custom exceptions, provide additional properties as needed

Provide additional properties for an exception (in addition to the custom message string) only when there’s a programmatic scenario where the additional information is useful. For example, the FileNotFoundExceptionprovides the FileName property.

What I want to add : In EVERY exception that you create in code, DEFINE a custom field. It  is useless without !

Why this rant ? In Stankins I have to intercept KeyNotFoundException  and I want to find the Key that was not found ( problem with some dictionary ) to provide it (Yes, it is a flawed design – but this is not the point here) . The problem was the definition:

public class KeyNotFoundException : SystemException, ISerializable
{

public KeyNotFoundException();

public KeyNotFoundException(string message);

public KeyNotFoundException(string message, Exception innerException);

protected KeyNotFoundException(SerializationInfo info, StreamingContext context);
}

See the problem ? No way to find WHAT is the Key that was not found. So I ended up with this code:

name =innerKeyEx.Message;
// The given key 'nameColumn' was not present in the dictionary.
var first=name.IndexOf("'");
var last= name.IndexOf("'",first+1);
name= name.Substring(first+1,last-first-1);

Moral of the post ? Do NOT define a custom Exception without defining a field / property inside!

Dynamic loading controllers in .NET Core

I needed to load dynamically some controllers to the Stankins application .Dynamic like in – written in some text file, then loading them, The best reference was https://www.strathweb.com/2018/04/generic-and-dynamically-generated-controllers-in-asp-net-core-mvc/ . But it was not enough : what I wanted is to write the controller in some text file and compile and load them.

First problem ; What about the dll’s to be loaded  ?

I ended up with this code :

var refs=new List<MetadataReference>();
var ourRefs=Assembly.GetEntryAssembly().GetReferencedAssemblies();

foreach(var item in ourRefs)
{
	var ass=Assembly.Load(item);
	refs.Add(MetadataReference.CreateFromFile(ass.Location));
}
refs.Add(MetadataReference.CreateFromFile(typeof(Attribute).Assembly.Location));
//MetadataReference NetStandard = MetadataReference.CreateFromFile(Assembly.Load("netstandard, Version=2.0.0.0").Location);
MetadataReference NetStandard = MetadataReference.CreateFromFile(Assembly.Load("netstandard").Location);
refs.Add(NetStandard);
refs.Add(MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location) );             

Second problem: where to put the errors ? The simplest option ( not the best one) is to put as comments to the end of the file – at reloading I will find them and correct

using (var ms = new MemoryStream())
{
	var res = compilation.Emit(ms);

	if (!res.Success)
	{           
		string diag=string.Join(Environment.NewLine, res.Diagnostics.Select(it=>it.ToString()));
		File.AppendAllText(fileName,"/*"+diag+"*/");
		return null;
	}
}

( Better – to display the error inline…)

Third problem: The controlers are loaded at initial step of configuring .NET MVC Core 2.x , not later. The assembly loader /unloader will come as a feature at .NET Core 3 – so not yet. I figure up that the best solution is to re-load the application

static CancellationTokenSource cancel ;
        static bool InternalRequestRestart;
        public async static Task Main(string[] args)
        {
            do{
                InternalRequestRestart=false;
                cancel= new CancellationTokenSource();
                await CreateWebHostBuilder(args).Build().RunAsync(cancel.Token);
                await Task.Delay(10);//just waiting some time
                Console.WriteLine("restarting");
            }while(InternalRequestRestart);

        }
        public static void Shutdown()
        {
            InternalRequestRestart=true;
            cancel.Cancel();
        }

 

You can find the code source for loading the controllers at https://github.com/ignatandrei/stankins/blob/master/stankinsv2/solution/StankinsV2/StankinsData/GenericControllerFeatureProvider.cs

You can find the code source for restarting the application at https://github.com/ignatandrei/stankins/blob/master/stankinsv2/solution/StankinsV2/StankinsData/Program.cs

You can test the code at https://azurestankins.azurewebsites.net/receiveData ( add a new controller , hit save and then access the route – or swagger at https://azurestankins.azurewebsites.net/swagger/index.html

– or you can try with Docker with

( for linux containers)

docker run -p 5000:5000 ignatandrei/stankins_linux

( for windows containers)

docker run -p 5000:5000 ignatandrei/stankins_linux

( restart docker if any problems)

and access http://localhost:5000/receiveData

That’s all 😉

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.