Create a simple .NET core library

In my last post I summed up my Azure DevOps setup at home. When the machine is running, the application is set up and the services are started the first tasks can be added. In this post I want to show how to create a simple solution that offers the following functionality.

  • A simple library written in C# with basic functionality that can be extended easily.
  • NUnit tests to implement test functionality. This helps improving code quality and prevents the library from getting published with errors.
  • A file with Azure DevOps definitions called “azure-pipelines.yml”. This defines what the Azure DevOps server should do.
  • A “NuGet.config” file to specify the feed the generated artifact (the .nupkg file) gets pushed to.

This can be seen as boilerplate. As soon as the library project is set up, the builds run through and the tests are executed everything else is copy, paste and good old programming. This means: The pipeline and deployment setup is implemented with a very simple projects which reduces the possible sources of errors and it can easily be tested. As soon as this is done the real development can start with little to no errors on the DevOps side. The following script just needs .NET core installed or at least the “dotnet” executable in the PATH. It creates the the solution including a library- and a test project. Additionally a example NuGet.config and a azure-pipeline.yml definition are created.

@echo off

set SolutionName=Example.Solution
set LibraryName=Example.Library
set TestsName=Example.Tests
set DevOpsUrl=https://devops.example.com
set DevOpsSpace=MySpace
set DevOpsProj=MyProj
set NuGetBaseUrl=%DevOpsUrl%/%DevOpsSpace%/%DevOpsProj%
set RepoAlias=MyRepo

dotnet new sln -o %SolutionName%
dotnet new classlib -o %SolutionName%\%LibraryName%
dotnet new nunit -o %SolutionName%\%TestsName%

dotnet sln %SolutionName%\%SolutionName%.sln^
	add %SolutionName%\%LibraryName%\%LibraryName%.csproj
	
dotnet sln %SolutionName%\%SolutionName%.sln^
	add %SolutionName%\%TestsName%\%TestsName%.csproj

dotnet add %SolutionName%\%TestsName%\%TestsName%.csproj^
	reference %SolutionName%\%LibraryName%

set CLASSFILE1=%SolutionName%\%LibraryName%\Class1.cs

echo using System;>%CLASSFILE1%
echo namespace Example.Library>>%CLASSFILE1%
echo {>>%CLASSFILE1%
echo     public class Class1>>%CLASSFILE1%
echo     {>>%CLASSFILE1%
echo         public static int GetValue() { return 4711; }>>%CLASSFILE1%
echo     }>>%CLASSFILE1%
echo }>>%CLASSFILE1%

set UNITTEST1=%SolutionName%\%TestsName%\UnitTest1.cs

echo using NUnit.Framework;>%UNITTEST1%
echo using Example.Library;>>%UNITTEST1%
echo namespace Example.Tests>>%UNITTEST1%
echo {>>%UNITTEST1%
echo     public class UnitTest1>>%UNITTEST1%
echo     {>>%UNITTEST1%
echo         [SetUp]>>%UNITTEST1%
echo         public void Setup()>>%UNITTEST1%
echo         {>>%UNITTEST1%
echo         }>>%UNITTEST1%
echo         [Test]>>%UNITTEST1%
echo         public void Test1()>>%UNITTEST1%
echo         {>>%UNITTEST1%
echo             Assert.AreEqual(4711, Class1.GetValue());>>%UNITTEST1%
echo         }>>%UNITTEST1%
echo     }>>%UNITTEST1%
echo }>>%UNITTEST1%

set NUGETCONFIG=%SolutionName%\NuGet.config

echo ^<?xml version="1.0" encoding="utf-8"?^>>%NUGETCONFIG%
echo ^<configuration^>>>%NUGETCONFIG%
echo   ^<packageSources^>>>%NUGETCONFIG%
echo     ^<clear /^>>>%NUGETCONFIG%
echo     ^<add key="%RepoAlias%" value="%NuGetBaseUrl%/_packaging/%RepoAlias%/nuget/v3/index.json" /^>>>%NUGETCONFIG%
echo   ^</packageSources^>>>%NUGETCONFIG%
echo ^</configuration^>>>%NUGETCONFIG%

set AZUREPIPELINE=%SolutionName%\azure-pipelines.yml

echo trigger:>%AZUREPIPELINE%
echo - master>>%AZUREPIPELINE%
echo.>>%AZUREPIPELINE%
echo variables:>>%AZUREPIPELINE%
echo   Major: '1'>>%AZUREPIPELINE%
echo   Minor: '0'>>%AZUREPIPELINE%
echo   Patch: '0'>>%AZUREPIPELINE%
echo   BuildConfiguration: 'Release'>>%AZUREPIPELINE%
echo.>>%AZUREPIPELINE%
echo pool:>>%AZUREPIPELINE%
echo   name: 'Default'>>%AZUREPIPELINE%
echo.>>%AZUREPIPELINE%
echo steps:>>%AZUREPIPELINE%
echo.>>%AZUREPIPELINE%
echo - script: dotnet build>>%AZUREPIPELINE%
echo   displayName: 'Build library'>>%AZUREPIPELINE%
echo.>>%AZUREPIPELINE%
echo - script: dotnet test>>%AZUREPIPELINE%
echo   displayName: 'Test library'>>%AZUREPIPELINE%
echo.>>%AZUREPIPELINE%
echo - script: dotnet publish -c $(BuildConfiguration)>>%AZUREPIPELINE%
echo   displayName: 'Publish library'>>%AZUREPIPELINE%
echo.>>%AZUREPIPELINE%
echo - script: dotnet pack -c $(buildConfiguration) -p:PackageVersion=$(Major).$(Minor).$(Patch)>>%AZUREPIPELINE%
echo   displayName: 'Pack library'>>%AZUREPIPELINE%
echo.>>%AZUREPIPELINE%
echo - script: dotnet nuget push --source "%RepoAlias%" --api-key az .\%LibraryName%\bin\$(buildConfiguration)\*.nupkg --skip-duplicate>>%AZUREPIPELINE%
echo   displayName: 'Push library'>>%AZUREPIPELINE%

set TASKSJSON=%SolutionName%\%LibraryName%\.vscode\tasks.json
mkdir %SolutionName%\%LibraryName%\.vscode

echo {>>%TASKSJSON%
echo     "version": "2.0.0",>>%TASKSJSON%
echo     "tasks": [>>%TASKSJSON%
echo         {>>%TASKSJSON%
echo             "label": "build",>>%TASKSJSON%
echo             "command": "dotnet",>>%TASKSJSON%
echo             "type": "process",>>%TASKSJSON%
echo             "args": [>>%TASKSJSON%
echo                 "build",>>%TASKSJSON%
echo                 "${workspaceFolder}/%LibraryName%/%LibraryName%.csproj",>>%TASKSJSON%
echo                 "/property:GenerateFullPaths=true",>>%TASKSJSON%
echo                 "/consoleloggerparameters:NoSummary">>%TASKSJSON%
echo             ],>>%TASKSJSON%
echo             "problemMatcher": "$msCompile">>%TASKSJSON%
echo         }>>%TASKSJSON%
echo     ]>>%TASKSJSON%
echo }>>%TASKSJSON%

In the folder containing the solution (*.sln file) the following commands can be executed and produce the corresponding output.

dotnet build
dotnet test
dotnet publish
dotnet pack

I recommend working with Visual Studio Code. Support for .NET is great and it is highly configurable. The library project contains a folder “.vscode” with a “tasks.json” file. This file contains a single build definition. The definition object in the array “tasks” can be copied and modified to support different task types like “test”, “publish” and “pack” directly from Visual Studio Code. They can be run by pressing [Crtl]+[Shift]+[P] and selecting the following:

A defined task can the be selected and is executed in the terminal:

I hope this script helps to create projects. Writing the correct pipeline definitions and configs took me some time to learn. Now the projects are easily created.