Category: .NET Core

Deploy .NET Core +SqlServer application to Ubuntu

Tools used:

SSH –  Windows Native

DOS – Windows Native

SSMS – download https://docs.microsoft.com/en-us/sql/ssms/download-sql-server-management-studio-ssms?view=sql-server-ver15

FileZilla – download https://filezilla-project.org/download.php?platform=win64

How I did :

SSH : Install SqlServer: https://docs.microsoft.com/en-us/sql/linux/quickstart-install-connect-ubuntu?view=sql-server-ver15

SSMS: Copy database with SSMS export data

DOS : Windows Compile application for Linux: dotnet publish -r linux-x64

Filezilla: Transfer with FileZilla all files: manual

SSH -Change permission chmod +777 <application name>  – to can execute

SSH –execute:  sudo ./<application name>–urls http://<ip>:8090

Note: Pay attention to Environment Variables on Linux – it did not work . See  https://docs.microsoft.com/en-us/dotnet/api/system.environment.getenvironmentvariables?view=netcore-2.0 . It did not work for me with Export.

Maybe it is better to configure as Linux Service, as in

https://irina.codes/net-api-as-a-linux-service/

https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-apache?view=aspnetcore-5.0

Add

Environment=ASPNETCORE_URLS=http://asdasd:port/

Also, for editing in ssh , sudo nano name.of.the.service

Also, to start , sudo systemctl start name.of.the.service

Also, to start at startup, sudo systemctl enable name.of.the.service

ASP.NET Core WebAPI should/ (must?) have

I am trying to have an extensive list about what an ASP.NET Core WebAPI project should / must have . After al, can be easy integrated into a VS project – or a wizard. I have put into 3 categories:

  1. Development – you need at development time.
  2. Testing – needed at testing time
  3. Production – needed in production

Note: All three are needed to be developed  !

  1. Development – Visibility  – see definition of the API
  2. Development  – Authorization + Authentication
  3. Development-Problem Details –  flow the
    errors as JSON text , keep status code
  4. Testing – test your application in CI mode
    • Production – Versioning  API endpoints
      • Production – CORS
      • Production – WhiteBox Monitoring
      • Production –BlackBox Monitoring and Observability – read about RED and USE
      • Production –Rate limit – do not allow insane usage
      • Production –Caching data – to return latest values
      • Production  – Status of the system

      Nice to have:

      1. Graph with endpoints: https://andrewlock.net/adding-an-endpoint-graph-to-your-aspnetcore-application/ – read about https://en.wikipedia.org/wiki/DOT_(graph_description_language) 
      2. Versioning  end points- see what version is your program: http://msprogrammer.serviciipeweb.ro/2019/02/18/identify-version-for-application-and-components-for-backend-net-core-and-frontendangularpart-2-backend/
      3. Not yet discovered, but should have endpoints for environment, settings json and others.
      4. Formatters: BSON, CSV, YAML: https://github.com/WebApiContrib/WebAPIContrib.Core

      ASP.NET Core : Add controllers at runtime and detecting changes done by others

      Part 1 Adding controllers at runtime

      Adding controllers at runtime in ASP.NET Core involves the ApplicationPartManager and IActionDescriptorChangeProvider. Let’s say that we hardcode the creation of the controller

       

      private Assembly CreateController(string name)
               {
                   
                   string code = new StringBuilder()
                       .AppendLine("using System;")
                       .AppendLine("using Microsoft.AspNetCore.Mvc;")
                       .AppendLine("namespace TestBlocklyHtml.Controllers")
                       .AppendLine("{")
                       .AppendLine("[Route(\"api/[controller]\")]")
                       .AppendLine("[ApiController]")
                       .AppendLine(string.Format("public class {0} : ControllerBase", name))
                   
                       .AppendLine(" {")
                       .AppendLine("  public string Get()")
                       .AppendLine("  {")
                       .AppendLine(string.Format("return \"test - {0}\";", name))
                       .AppendLine("  }")
                       .AppendLine(" }")
                       .AppendLine("}")
                       .ToString();
      
                  var codeString = SourceText.From(code);
                   var options = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp7_3);
      
                  var parsedSyntaxTree = SyntaxFactory.ParseSyntaxTree(codeString, options);
      
                  var references = new MetadataReference[]
                   {
                       MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
                       MetadataReference.CreateFromFile(typeof(Console).Assembly.Location),
                       MetadataReference.CreateFromFile(typeof(System.Runtime.AssemblyTargetedPatchBandAttribute).Assembly.Location),
                       MetadataReference.CreateFromFile(typeof(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo).Assembly.Location),
                       MetadataReference.CreateFromFile(typeof(RouteAttribute).Assembly.Location),
                       MetadataReference.CreateFromFile(typeof(ApiControllerAttribute).Assembly.Location),
                       MetadataReference.CreateFromFile(typeof(ControllerBase).Assembly.Location),
                   };
      
                  var codeRun = CSharpCompilation.Create("Hello.dll",
                       new[] { parsedSyntaxTree },
                       references: references,
                       options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary,
                           optimizationLevel: OptimizationLevel.Release,
                           assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default));
                   using (var peStream = new MemoryStream())
                   {
                       if (!codeRun.Emit(peStream).Success)
                       {
                           return null;
                       }
                       return Assembly.Load(peStream.ToArray());
                   }
      
      
      
              }
      
      
      

       

      Then we will load into ApplicationParts

      [HttpGet]
              public string AddRuntimeController([FromServices] ApplicationPartManager partManager, [FromServices]MyActionDescriptorChangeProvider provider)
              {
                  string name = "andrei" + DateTime.Now.ToString("yyyyMMddHHmmss");
                  var ass = CreateController(name);
                  
                  if (ass != null)
                  {
                      partManager.ApplicationParts.Add(new AssemblyPart(ass));
                      // Notify change
                      provider.HasChanged = true;
                      provider.TokenSource.Cancel();
                      return "api/"+ name;
                  }
                  throw new Exception("controller not generated");
              }
      
      

      and the code for is

      public class MyActionDescriptorChangeProvider : IActionDescriptorChangeProvider
          {
              public static MyActionDescriptorChangeProvider Instance { get; } = new MyActionDescriptorChangeProvider();
      
              public CancellationTokenSource TokenSource { get; private set; }
      
              public bool HasChanged { get; set; }
      
              public IChangeToken GetChangeToken()
              {
                  TokenSource = new CancellationTokenSource();
                  return new CancellationChangeToken(TokenSource.Token);
              }
          }
      

      and it is added to the DI services by

      services.AddSingleton<IActionDescriptorChangeProvider>(MyActionDescriptorChangeProvider.Instance);
      services.AddSingleton(MyActionDescriptorChangeProvider.Instance);
                  
      

      Usually , you do not need Part 2 – if you do not construct something like Blockly for .NET Core (https://netcoreblockly.herokuapp.com/), that needs to see controllers added  by the application

      Part 2  Detecting controller added at runtime by others

      You usually do not need this – Part 1 is enough.  I need because I do construct something like Blockly for .NET Core (https://netcoreblockly.herokuapp.com/), that needs to see controllers added  by the application.

      So we need ActionDescriptorCollectionProvider –  read the remarks at https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.infrastructure.actiondescriptorcollectionprovider?view=aspnetcore-3.1 . We can obtain by DI from IActionDescriptorCollectionProvider and convert to ActionDescriptorCollectionProvider

      The code is

      //in the class , obtained by DI from IActionDescriptorCollectionProvider 
      private readonly ActionDescriptorCollectionProvider cp;
      
      internal void registerCallback()
              {
                  cp.GetChangeToken().RegisterChangeCallback(a =>
                  {
                       //a is your class because of this parameter below
      
                      //do your work
                      s.registerCallback();
                  }, this);
      
              }
      
      

      ( All code is taken from https://github.com/ignatandrei/netcoreblockly)

      Integrating SPA ( Angular, React, Vue) with .NET Core for production

      If you have a .NET Core WebAPI application ( that only serves data ) and  a SPA application ( Angular, Vue,  React) you can have just one application following this 2 steps:

      1.  In .NET Core add in Startup.cs ( with using Microsoft.AspNetCore.Builder from Microsoft.AspNetCore.StaticFiles )

      app.UseDefaultFiles();
      app.UseStaticFiles();

      app.UseRouting();

      app.UseAuthorization();

      app.UseEndpoints(endpoints =>
                   {
                      

                  // If you know the endpoints, put it here

                 //endpoints.MapFallbackToFile(“dbodepartment/{**slug}”,”/index.html”);
                               
                       endpoints.MapControllers();
                       endpoints.MapFallbackToFile(“{**slug}”, “/index.html”);
      });

      2. Compile the SPA to index.html, deploy in wwwroot with the js and css

      ( yes, you should modify the endpoint for data when compiling to / –  for Angular see my previous post , http://msprogrammer.serviciipeweb.ro/2020/09/14/angular-base-href-for-web-desktop-mobile-and-paths-for-services/ )

      HCV- Postmortem–part 8

      As I look below , the code is a small part of what has been done – documentation, test, CI /CD

      The src folder has this number of lines / words ( obtained with Powershell

      Get-ChildItem -r “src” |?{!$_.PSIsContainer} | Select-Object { Get-Content $_.FullName  | Measure-Object -Line -Word  | Select-Object  -Property Lines,Words }

       

      @{Lines=6; Words=6}
      @{Lines=20; Words=112}
      @{Lines=36; Words=154}
      @{Lines=8; Words=39}
      @{Lines=36; Words=131}
      @{Lines=27; Words=53}
      @{Lines=35; Words=135}
      @{Lines=33; Words=86}
      @{Lines=24; Words=84}
      @{Lines=9; Words=14}
      @{Lines=10; Words=16}
      @{Lines=18; Words=78}
      @{Lines=16; Words=31}
      @{Lines=24; Words=45}
      @{Lines=63; Words=143}
      @{Lines=11; Words=41}
      @{Lines=35; Words=86}
      @{Lines=37; Words=65}
      @{Lines=50; Words=99}
      @{Lines=15; Words=30}

      ( And does not count the yaml for CI )

      That counts for 513 lines and 1448 words…

      So the code is a small part od the application …

       

      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/

      HCV–preparing for NuGet–part 7

      Preparing a dll to be deployed to NuGet mneans to me:

      1. XML documentation

      2. Treat Warnings as errors

      3. Adding sources to the nuget package

      4. Versioning the package, adding description, licence and so on

      5. Generating CD in artifacts

      6. Deploying to NuGet

      7. Adding Badge with Nuget and Licence to the readme

       

      Where are the modifications:

      1. In the .csproj – adding description
      2. In the docker yaml – running dotnet pack
      3. In the batch file that run the docker – collecting pkg folder with nuget package
      4. In the CI yaml file for Github – collecting nuget package to the artifacts
      5. In the readme  – using badge from shields.io

       

      The modifications are at https://github.com/ignatandrei/HealthCheckVersion/tree/DeployNuGet

      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/

      HCV–automatic CI–part 6

      I want the tests to be run automatically . And have a code coverage report. So Docker will be generating the report ( with coverlet.console and dotnet-reportgenerator-globaltool ). The GitHub Actions will be responsible with running this every time. The docs folder will be responsible of hosting it .

      The code for docker resides in  \src\copyFilesToDocker.txt

      FROM mcr.microsoft.com/dotnet/core/sdk:3.1
      ENV NODE_ROOT usr/app/
      WORKDIR $NODE_ROOT

      COPY HealthCheckVersion .
      RUN dotnet tool install –global coverlet.console –version 1.7.2
      RUN dotnet tool install  -g dotnet-reportgenerator-globaltool –version 4.6.1
      RUN dotnet test TestHealthCheckVersion/TestHealthCheckVersion.csproj –logger trx  –logger “console;verbosity=normal” –collect “Code coverage”
      ENV PATH=”${PATH}:/root/.dotnet/tools”
      RUN coverlet TestHealthCheckVersion/bin/Debug/netcoreapp3.1/TestHealthCheckVersion.dll –target “dotnet” –targetargs “test TestHealthCheckVersion/TestHealthCheckVersion.csproj –configuration Debug –no-build”  –format opencover –exclude “[xunit*]*” –exclude “[*]NetCoreTestProject*”
      RUN reportgenerator “-reports:coverage.opencover.xml” “-targetdir:coveragereport” “-reporttypes:HTMLInline;HTMLSummary;Badges”
      CMD tail -f /dev/null

      The running  the docker is in \src\testNetCoreWin.bat

      cls
      ECHO please be aware of absolute path here %cd%
      docker build -t testcinetcore -f copyFilesToDocker.txt .
      docker run -d –name citest  testcinetcore
      docker cp citest:/usr/app/coveragereport .
      docker container kill citest
      docker container rm citest

      The code for Github CI is in \.github\workflows\docker-image.yml

      name: GenerateCodeCoverage

      on:

      push:

      branches: [ master ]

      pull_request:

      branches: [ master ]

      jobs:

      build:

      runs-on: ubuntu-latest

      steps:

      – uses: actions/checkout@v2

      – name: Build the Docker image for code coverage

      run: |

      cd src

      chmod +777 ./testNetCoreWin.bat

      ./testNetCoreWin.bat

      ls -lh

      echo ‘copy’

      cp -r  ./coveragereport/* ../docs/coveragereport/

      rm -R ./coveragereport/

      echo ‘in docs/coveragereport’

      ls -lh ../docs/coveragereport/

      echo ‘here’

      ls -lh

      – 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 code coverage” -a –allow-empty

      #shell: cmd

      – name: Push changes

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

      with:

      github_token: ${{ secrets.GITHUB_TOKEN }}

      And now I can update readme with CI badge and code coverage test and badge

       

      You can see the modifications at https://github.com/ignatandrei/HealthCheckVersion/tree/CI

      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/

      HCV–adding test application–part 5

      Now it is time to see a test application. For this I create a new .NET Core 3.1 ASP.NET application, add Xabaril ( to see the end result ) and add reference to my project. The Startup is:

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Reflection;
      using System.Threading.Tasks;
      using HCVersion;
      using HealthChecks.UI.Client;
      using Microsoft.AspNetCore.Builder;
      using Microsoft.AspNetCore.Diagnostics.HealthChecks;
      using Microsoft.AspNetCore.Hosting;
      using Microsoft.AspNetCore.HttpsPolicy;
      using Microsoft.AspNetCore.Mvc;
      using Microsoft.Extensions.Configuration;
      using Microsoft.Extensions.DependencyInjection;
      using Microsoft.Extensions.Hosting;
      using Microsoft.Extensions.Logging;
      
      namespace NetCore31HC
      {
          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)
              {
                  string name = Assembly.GetExecutingAssembly().GetName().Name;
                  services.AddControllers();
                  services.AddHealthChecks()
                      .AddCheck<HealthCheckVersion>(name);
                  services
                      .AddHealthChecksUI(setup =>
                       {
                           setup.AddHealthCheckEndpoint("All", $"/hc");
                       })
                      .AddInMemoryStorage()
                      ;
              }
      
              // 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.UseHttpsRedirection();
      
                  app.UseRouting();
      
                  app.UseAuthorization();
      
                  app.UseEndpoints(endpoints =>
                  {
                      endpoints.MapControllers();
                      endpoints.MapHealthChecks("/hc",new HealthCheckOptions()
                      {
                          Predicate= _=> true,
                          ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
                      });
                      endpoints.MapHealthChecksUI();
                  });
              }
          }
      }
      
      

       

       

      And so I figure out the fact that I should not hardcode the name of the HealthCheck , but to put dinamically.  So I change also the readme file to acknowledge that.

      Code at https://github.com/ignatandrei/HealthCheckVersion/releases/tag/testApp

      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/

      HCV- adding code–part 4

      Now it is time to write some code .  However, the implementation should be in a different class than the health check itself – to can be tested.

      The code is as follows:

      using System;
      using System.Collections.Generic;
      using System.Reflection;
      using System.Runtime.CompilerServices;
      
      [assembly: InternalsVisibleTo("TestHealthCheckVersion")]
      
      namespace HCVersion
      {
          internal class HCV
          {
              static Version versionEntry;
              static Version versionHCV;
              static HCV()
              {
                  var ass = Assembly.GetEntryAssembly();
                  versionEntry = ass.GetName().Version;
                  versionHCV = Assembly.GetExecutingAssembly().GetName().Version;
      
              }
              public string GetStartingAssemblyInformation()
              {
                  return versionEntry.ToString();
              }
              public string GetHCVAssemblyInformation()
              {
                  return versionHCV.ToString();
              }
          }
      }
      
      

      The internals visible to is for running tests successfully.

      The code for getting the real Health Check is

      using Microsoft.Extensions.Diagnostics.HealthChecks;
      using System;
      using System.Collections.Generic;
      using System.Dynamic;
      using System.Threading;
      using System.Threading.Tasks;
      
      namespace HCVersion
      {
          public class HealthCheckVersion : IHealthCheck
          {
              public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
              {
                  var hcv = new HCV();
                  var res = HealthCheckResult.Healthy(hcv.GetStartingAssemblyInformation(),
                      new Dictionary<string, object>()
                      {
                          {"Entry",hcv.GetStartingAssemblyInformation() },
                          {"HCV",hcv.GetHCVAssemblyInformation() }
                      });
      
                  return Task.FromResult( res);
              }
          }
      }
      
      

      You will find the code at https://github.com/ignatandrei/HealthCheckVersion/releases/tag/testWorks

      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/

      HCV- adding tests–part 3

      Now it is time to add tests . Even if will not compile, the tests should show how the application behaves. Here are the tests:

       

      using System;
      using Xunit;
      
      namespace TestHealthCheckVersion
      {
          public class TestHCV
          {
              [Fact]
              public void TestStartAssemblyVersion()
              {
                  #region Arrange
                  var hcv = new HCV();
                  #endregion
                  #region Act
                  string v = hcv.GetStartingAssemblyInformation();
                  #endregion
                  #region Assert
                  Assert.Equal("1.0.0.0", v);
                  #endregion
              }
              [Fact]
              public void TestHCVAssemblyVersion()
              {
                  #region Arrange
                  var hcv = new HCV();
                  #endregion
                  #region Act
                  string v = hcv.GetHCVAssemblyInformation();
                  #endregion
                  #region Assert
                  Assert.Equal("1.0.0.0", v);
                  #endregion
              }
          }
      }
      
      

      The code ( again, even if not compile ) it is at https://github.com/ignatandrei/HealthCheckVersion/releases/tag/addingTests

       

      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/

      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.