YAML Builds in Azure DevOps – A Continuous Integration Scenario

Azure DevOps has released YAML builds. Truthfully, I’m very excited about this. YAML builds greatly changes the landscape of DevOps practices on both CI and CD forefront. At least as of writing of this post, Microsoft has full support on YAML builds.

YAML Based Builds through Azure DevOps

Azure DevOps, particularly the build portion of the service, really encourages using YAML. You can tell this by creating a new build definition and the first option under templates is YAML. However, if you’re like me and were used to the UI, or you’re just completely new to Azure DevOps, it could be a bit confusing. In a nutshell, here’s a very simple comparison why I encourage using YAML builds:

NON YAML Based Builds (UI Generated and Managed):

  • JSON Based
  • Not Focused on “Build as Code”
  • No source control versioning
  • Shared steps require more management in different projects
  • Very UI driven and once created, the challenge is to validate changes made (without proper versioning)

YAML Based Builds (Code Based Syntax):

  • Modern way of managing builds that’s common in open source community
  • Focused on “Build as Code” since it’s part of the Application Git Branch
  • Shared Steps and Templates across different repo’s. It’s easier to centralize common steps such as Quality, Security and other utility jobs.
  • Version Control!!! If you have a big team, you don’t want to keep creating builds for your branch strategy. The build itself is also branched
  • Keeps the developer in the same experience.
  • A step towards “Documentation As Code”. Yes, we can use Comments!!!

For more info on Azure DevOps YAML builds, see: Azure DevOps YAML Schema

For YAML specific information, see the following:
https://yaml.org/

Better Read: Relation to JSON:
https://yaml.org/spec/1.2/spec.html#id2759572

YAML’s indentation-based scoping makes ideal for programmers (comments, references, etc.…)

In this post, I’ll provide some sample build YAMLs towards a CI pipeline from developer’s perspective. Platforms used:

  • Application Development Framework – .Net Core 2.2
  • Hosting Environment – Docker Container

The web application is a simple Web API that is used to listen on Azure DevOps service hook events. There are associated Unit Tests that validates changes on the API so a typical process would comprise of:

  1. Build the application
  2. Run quality checks against the application (Unit tests, Code Coverage thresholds, etc…)
  3. If successfully, publish the appropriate artifacts to be used in the next phase (CD – Continuous Deployment)

Taking the above context, we’ll be:

  1. Building the .Net Core Web API (DotNetCoreBuildAndPublish.yml)
  2. Run Quality Checks against the Web Api (DotNetCoreQualitySteps.yml)
  3. Create a Docker Container for the Web Api (DockerBuildAndPublish.yml)

Job 1: Building the .Net Core Web API

parameters:
  Name: ''
  BuildConfiguration: ''
  ProjectFile: ''  

steps:
- task: DotNetCoreCLI@2
  displayName: 'Restore DotNet Core Project'
  inputs:
    command: restore
    projects: ${{ parameters.ProjectFile}}

- task: DotNetCoreCLI@2
  displayName: 'Build DotNet Core Project'
  inputs:
    projects: ${{ parameters.ProjectFile}}
    arguments: '--configuration ${{ parameters.BuildConfiguration }}'
    
- task: DotNetCoreCLI@2
  displayName: 'Publish DotNet Core Artifacts'
  inputs:
    command: publish
    publishWebProjects: false
    projects: ${{ parameters.ProjectFile}}
    arguments: '--configuration ${{ parameters.BuildConfiguration }} --output $(build.artifactstagingdirectory)'
    zipAfterPublish: True

- task: PublishBuildArtifacts@1
  displayName: 'Publish Artifact'
  inputs:
    PathtoPublish: '$(build.artifactstagingdirectory)'
    ArtifactName: ${{ parameters.name }}_Package
  condition: succeededOrFailed()

This YAML is straightforward, it uses Azure DevOps tasks to call .Net Core CLI and passes CLI arguments such as restore and publish. This is the most basic YAML for a .Net Core app. Also, notice the parameters section? These are the parameters needed to be passed by the calling app (Up Stream Pipeline)

CHEAT!!! So, if you’re also new to YAML builds, Microsoft has made it easier to transition from JSON to YAML. Navigate to an existing build definition, click on the job level node (not steps) then click on “View As YAML”. This literally takes all your build steps and translates them into YAML format. Moving forward, use this feature and set parameters for shared your YAML steps.

Job 2: Run Quality Checks against the Web Api

This job essentially executes any quality checks for the application. In this case both Unit Test and Code Coverage Thresholds. Again, calling existing pre-build tasks available in Azure DevOps

parameters:
  Name: ''
  BuildConfiguration: ''
  TestProjectFile: ''
  CoverageThreshold: ''

steps:
- task: DotNetCoreCLI@2
  displayName: 'Restore DotNet Test Project Files'
  inputs:
    command: restore
    projects: ${{ parameters.TestProjectFile}}

- task: DotNetCoreCLI@2
  displayName: 'Test DotNet Core Project'
  inputs:
    command: test
    projects: ${{ parameters.TestProjectFile}}
    arguments: '--configuration ${{ parameters.BuildConfiguration }} --collect "Code coverage"'

- task: mspremier.BuildQualityChecks.QualityChecks-task.BuildQualityChecks@5
  displayName: 'Checke Code Coverage'
  inputs:
    checkCoverage: true
    coverageFailOption: fixed
    coverageThreshold: ${{ parameters.CoverageThreshold }}

Job 3: Create a Docker Container for the Web Api

parameters:
  Name: ''
  dockerimagename: ''
  dockeridacr: '' #ACR Admin User
  dockerpasswordacr: '' #ACR Admin Password
  dockeracr: ''
  dockerapppath: ''
  dockerfile: ''
  

steps:    
- powershell: |
    # Get Build Date Variable if need be
    $date=$(Get-Date -Format "yyyyMMdd");
    Write-Host "##vso[task.setvariable variable=builddate]$date"

    # Set branchname to lower case because of docker repo standards or it will error out
    $branchname= $env:sourcebranchname.ToLower();
    Write-Host "##vso[task.setvariable variable=sourcebranch]$branchname"

    # Set docker tag from build definition name: $(Date:yyyyMMdd)$(Rev:.r)
    $buildnamesplit = $env:buildname.Split("_")
    $dateandrevid = $buildnamesplit[2]
    Write-Host "##vso[task.setvariable variable=DockerTag]$dateandrevid"
  displayName: 'Powershell Set Environment Variables for Docker Tag and Branch Repo Name'
  env:
    sourcebranchname: '$(Build.SourceBranchName)' # Used to specify Docker Image Repo
    buildname: '$(Build.BuildNumber)' # The name of the completed build which is defined above the upstream YAML file (main yaml file calling templates)

- script: |
      docker build -f ${{ parameters.dockerfile }} -t ${{ parameters.dockeracr }}.azurecr.io/${{ parameters.dockerimagename }}$(sourcebranch):$(DockerTag) ${{ parameters.dockerapppath }}
      docker login -u ${{ parameters.dockeridacr }} -p ${{ parameters.dockerpasswordacr }} ${{ parameters.dockeracr }}.azurecr.io 
      docker push ${{ parameters.dockeracr }}.azurecr.io/${{ parameters.dockerimagename }}$(sourcebranch):$(DockerTag)
  displayName: 'Builds Docker App - Login - Then Pushes to ACR'

This the last job for our demo. Once Quality check passes, we essentially build a docker image and upload it to a container registry. In this case, I’m using an Azure Container Registry.

This is an interesting YAML. I’ve intentionally not used pre-built tasks from Azure DevOps to illustrate YAML capabilities by using external command sets such as Power Shell (which works across platforms) and inline script commands such as docker

First things first, docker when creating images and tags is very case sensitive. Docker has strict naming conventions and one of them is that all tags and images should be lower case. Let’s dissect these steps:

Powershell Step: I’ve added some logic here to get built-in variables from Azure DevOps build definitions. Notice that I’ve binded sourcebranchname and buildname as an environment variable from Azure DevOps built-in

I’ve added some logic here to get built-in variables from Azure DevOps build definitions. Notice that I’ve binded sourcebranchname and buildname as an environment variable from Azure DevOps built-in

'$(Build.SourceBranchName)' # Used to specify Docker Image Repo
'$(Build.BuildNumber)' # The name of the completed build which is defined above the upstream YAML file (main yaml file calling templates)

What’s next is straightforward for you “DevOps practitioners” 🙂

$branchname= $env:sourcebranchname.ToLower();

The above line is the step where I use powershell to set the branchname to all lowercase. I will use it later when calling docker commands to create and publish docker images

$buildnamesplit = $env:buildname.Split("_")
$dateandrevid = $buildnamesplit[2]

The above line is dependent on what you define as your build definition name. I used the last part of the build definition at the docker tag.

name: $(Build.DefinitionName)_$(Build.SourceBranchName)_$(Date:yyyyMMdd)$(Rev:.r)
e.g.: #Webhooks-BuildEvents-YAML_FeatureB_20190417.4

Webhooks-BuildEvents-YAML – BuildName
FeatureB – BranchName
20190417.4 – Date/Rev (Used as the Docker Tag) 

You will see this build definition name defined in our upstream pipeline. Meaning, the main build YAML file that calls all these templates.

Script Step: Pretty straightforward as well. We invoke inline docker commands to: Build, Login and Push a docker image to a registry (ACR in this case). Notice this line though:

docker build -f ${{ parameters.dockerfile }} -t ${{ parameters.dockeracr }}.azurecr.io/${{ parameters.dockerimagename }}$(sourcebranch):$(DockerTag) ${{ parameters.dockerapppath }}

I’m setting the image name with a combination of both passed parameter and sourcebranch. This guarantees that new images will always be created on any source branch you’re working on.

The Complete YAML:

name: $(Build.DefinitionName)_$(Build.SourceBranchName)_$(Date:yyyyMMdd)$(Rev:.r)

trigger:
# branch triggers. Commenting out to trigger builds on all branches
  branches:
    include:
    - master
    - develop
    - feature*
  paths:
    include:
    - AzureDevOpsBuildEvents/*
    - AzureDevOpsBuildEvents.Tests/*
    - azure-pipelines-buildevents.yml

variables: 
  - group: DockerInfo

resources:
  repositories:
  - repository: templates  # identifier (A-Z, a-z, 0-9, and underscore)
    type: git  # see below git - azure devops
    name: SoftwareTransformation/DevOps  # Teamproject/repositoryname (format depends on `type`)
    ref: refs/heads/master # ref name to use, defaults to 'refs/heads/master'

jobs:
- job: AppBuild
  pool:
      name: 'Hosted VS2017' # Valid Values: 'OnPremAgents' - Hosted:'Hosted VS2017',  'Hosted macOS', 'Hosted Ubuntu 1604'
  steps:
  - template: YAML/Builds/DotNetCoreBuildAndPublish.yml@templates  # Template reference
    parameters:
      Name: 'WebHooksBuildEventsWindowsBuild' # 'Ubuntu 16.04' NOTE: Code Coverage doesn't work on Linux Hosted Agents. Bummer. 
      BuildConfiguration: 'Debug'
      ProjectFile: ' ./AzureDevOpsBuildEvents/AzureDevOpsBuildEvents.csproj'  

- job: QualityCheck
  pool:
      name: 'Hosted VS2017' # Valid Values: 'OnPremAgents' - Hosted:'Hosted VS2017',  'Hosted macOS', 'Hosted Ubuntu 1604'
  steps:
  - template: YAML/Builds/DotNetCoreQualitySteps.yml@templates  # Template reference
    parameters:
      Name: 'WebHooksQualityChecks' # 'Ubuntu 16.04' NOTE: Code Coverage doesn't work on Linux Hosted Agents. Bummer. 
      BuildConfiguration: 'Debug'
      TestProjectFile: ' ./AzureDevOpsBuildEvents.Tests/AzureDevOpsBuildEvents.Tests.csproj'
      CoverageThreshold: '10'
  
- job: DockerBuild
  pool:
      vmImage: 'Ubuntu 16.04' # other options: 'macOS-10.13', 'vs2017-win2016'. 'Ubuntu 16.04' 
  dependsOn: QualityCheck
  condition: succeeded('QualityCheck')
  steps:
  - template: YAML/Builds/DockerBuildAndPublish.yml@templates  # Template reference
    parameters:
      Name: "WebHooksBuildEventsLinux"
      dockerimagename: 'webhooksbuildeventslinux'
      dockeridacr: $(DockerAdmin) #ACR Admin User
      dockerpasswordacr: $(DockerACRPassword) #ACR Admin Password
      dockeracr: 'azuredevopssandbox'
      dockerapppath: ' ./AzureDevOpsBuildEvents'
      dockerfile: './AzureDevOpsBuildEvents/DockerFile'

The above YAML runs is the entire build pipeline comprised of all jobs that calls each YAML templates. There are 2 sections that I do want to point out:

Resources: This is the part where I refer to the YAML templates stored in a different Git Repo instance within Azure DevOps

resources:
  repositories:
  - repository: templates  # identifier (A-Z, a-z, 0-9, and underscore)
    type: git  # see below git - azure devops
    name: SoftwareTransformation/DevOps  # Teamproject/repositoryname (format depends on `type`)
    ref: refs/heads/master # ref name to use, defaults to 'refs/heads/master'

Variables:  This is the section where I use Azure DevOps pipeline group variables to encrypt docker login information. For more information on this, see: Variable groups

variables: 
  - group: DockerInfo

The end results. A working pipeline that triggers builds from code that works in your branching strategy of choice. This greatly speeds up the development process without the worry of maintaining manually created build definitions.

Continuous Integration in VSTS using .Net Core (with Code Coverage), NUnit, SonarQube: Part 3: VSTS SonarQube Build Task

What is SonarQube? From SonaQube’s WebsiteSonarQube provides the capability to not only show health of an application but also to highlight issues newly introduced. With a Quality Gate in place, you can fix the leak and therefore improve code quality systematically.”

In short, it’s a continuous integration process targeting developers to set triggers and/or thresholds on maintaining quality code using gates.

Here’s a high-level screenshot of what SonarQube has to offer (Actual screenshot of an application that went through SonarQube’s capabilities:

image

Note that the instance of SonarQube that I’ve used here is their SaaS based offering – SonarCloud. I didn’t want to go through the hassle of hosting my own instance of SonarQube rather use the SaaS based offering as a guideline. In my opinion, SaaS based offerings are better options for medium to enterprise size companies for multiple reasons (Cost, Support, Maintenance, etc…)

To see detailed description of what SonarQube has to offer: https://www.sonarqube.org/features/clean-code/

Personally, I love everything what SonarQube has to offer. Note that SonarQube can also be self-hosted, If you want to host SonarQube within your IT shop, you can step by step directions here: https://www.sonarqube.org/downloads/

Let’s go through setting up SonarQube in VSTS:

Step 1: Prepare analysis on SonarQube

NOTE: Make sure that this task comes before any application build task. This should be the first task. In my example, this task comes after restore Nuget step. This shouldn’t affect how the analysis works. Nuget restore is pretty much restoring Nuget packages for the given .Net solution/project(s).

This is the most crucial step of the process. This what sets all the properties in build time. The fields you need to enter here are both the Project Key and Project Name. These values can be obtained through SonarQube’s administration page or the landing page of your project in SonarQube.

One important field missing here is the Organization. This is needed to publish to SonarQube. As of writing this post, version 4.x of this task will fail unless you specifically add an additional property to set the organization. You set this by expanding “Advanced” on the task and typing:

sonar.organization=<Org Value>

Both Org and Project Keys are specified as well in the project landing page in SonarQube’s site.

image

Step 2: Run Code Analysis

This step should come after a successfully test task for your build. The results from the unit tests are gathered (including code coverage), analyzes the results and preps the proper files for publishing to SonarQube.

image

Step 3: Publish Quality Gate Result

This is the final step. It should come right after the Code Analysis task. No settings are done here since all settings have been properly set in the first step (Prepare analysis on SonarQube).

image

A successful build with SonarQube integration looks like this:

image

Continuous Integration in VSTS using .Net Core (with Code Coverage), NUnit, SonarQube: Part 2: VSTS Build Definition Setup – .Net Core and NUnit

If you haven’t setup your .Net Core project/s for code coverage instrumentation, see my previous post: Part 1: .Net Core Project Setup – Code Coverage.

That said, let’s go through the settings for enabling code coverage in VSTS builds. The basic structure of CI build definition would be:

  1. Build the application
  2. Run Tests (Unit Tests with Code Coverage)
  3. Publish Artifacts

In this post, I’ll skip over using NUnit as a test framework for .Net Core. For using NUnit as a test framework, see my previous post: Using NUNIT Test Framework do validate deployments in VSTS Release Management

image

Note from the image above that we’ve disabled the Dotnet test task because code coverage is currently not supported on dotnet.exe CLI (as of writing of this post)

However, vstest.console.exe does support code coverage. This is the task we’ve enabled to run our unit tests for code coverage instrumentation as well as running the tests from NUnit written code. Vstest.console.exe automatically detects NUnit tests since part of the restore nuget package includes the NUnit test adapters.

The important note here is to ensure that you properly setup the vstest task in the build definition and it’s settings:

  • Ensure that you have code coverage enabled option
  • Ensure that you are pointing to the .runsettings file for further code coverage settings
  • Install the NUnit adapters as part of your test project

Running the build yields the following result.

image

At this point, you can download the code coverage file and open the result in Visual Studio for further inspection.

You may have noticed that in my build, I also have tasks for SonarQube. What is SonarQube and why use it? For this, see part 3 (final) post for this series: Part 3: VSTS SonarQube Build Task

Continuous Integration in VSTS using .Net Core (with Code Coverage), NUnit, SonarQube: Part 1: .Net Core Project Setup – Code Coverage

There are 2 ways to discover and execute unit tests using Microsoft developed test harnesses:

  • Vstest.console.exe = This is the command-line used to execute tests within/embedded in Visual Studio IDE
  • Dotnet.exe = This is the command line interface (CLI) specific to .Net Core Projects

Documentation for Vstest.console.exe is documented here: https://msdn.microsoft.com/en-us/library/jj155796.aspx

For .Net Core Projects: https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-test?tabs=netcore2x

The primary difference between both is that vstest.console.exe can execute tests developed in .Net Framework and .Net Core while dotnet.exe is specifically for .Net Core

An example of executing tests for the same assembly domain (test project) would be:

VSTest.Console.exe:

vstest.console.exe <testassembly>.dll (Pointer to the compiled Assembly)

Dotnet.exe:

dotnet test <testassemblyproject>.csproj (Pointer to the actual .Net Core Test Project)

The issue with dotnet.exe (CLI) is that Code Coverage doesn’t work. In order for code coverage to work on .Net Core projects, you need to:

  1. Edit the .Net Core projects you want to instrument for code coverage
  2. Use vstest.console.exe and supply /EnableCodeCoverage switch

Edit the .Net Core project/s for code coverage instrumentation

When you run unit tests in visual studio and select the option to “Analyze Code Coverage for Selected Tests” (as seen below), by default, code coverage results will not be captured.

image

As of writing of this post, the fix is to modify the project file and enable DebugType to Full on the propertygroup section of the project file.

image

Save the project file and run the unit tests again by selecting the option: to “Analyze Code Coverage for Selected Tests” and you’ll see similar results as shown below.

image

Use vstest.console.exe and supply /EnableCodeCoverage switch

As you saw within Visual Studio, running tests with code coverage can be trigged via a simple click on the context menu. If you want to execute your unit test with code coverage in a command line, you invoke /EnableCodeCoverage switch.

vstest.console.exe <testassembly>.dll /EnableCodeCoverage

The result would be an export of the code coverage results to a .coverage file. You can then open the file within Visual Studio to inspect the results. See screenshot below:

image

Setting up your .Net Core projects appropriately using the preceding steps should give you the proper code coverage numbers. More importantly, this allows you to seamlessly integrate with various build systems. Additionally, here are some tips and practices around code coverage:

Use a test .runsettings

Use a test .runsettings file to exclude assemblies you don’t want to instrument. The .runsettings file can be used on how tests are executed from vstest.console.exe. For more information, see the following: Configure unit tests by using a .runsettings file

Here an example on how you would want to exclude piece of code not to be measured for code coverage:

<DataCollectionRunSettings>
    <DataCollectors>
      <DataCollector friendlyName="Code Coverage" uri="datacollector://Microsoft/CodeCoverage/2.0" assemblyQualifiedName="Microsoft.VisualStudio.Coverage.DynamicCoverageDataCollector, Microsoft.VisualStudio.TraceCollector, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
        <Configuration>
          <CodeCoverage>
            <ModulePaths>
              <Include>  
                <!-- Include all loaded .dll assemblies -->  
              </Include> 
              <Exclude>
                <!-- Exclude all loaded .dll assemblies with the words moq, essentially regex -->
                <ModulePath>.*\\[^\\]*moq[^\\]*\.dll</ModulePath>
                <ModulePath>.*\\[^\\]*Moq[^\\]*\.dll</ModulePath>
              </Exclude>
            </ModulePaths>
            <!-- We recommend you do not change the following values: -->
            <UseVerifiableInstrumentation>True</UseVerifiableInstrumentation>
            <AllowLowIntegrityProcesses>True</AllowLowIntegrityProcesses>
            <CollectFromChildProcesses>True</CollectFromChildProcesses>
            <CollectAspDotNet>False</CollectAspDotNet>s
            <Attributes>
              <Exclude>
                <Attribute>^System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute$</Attribute>
              </Exclude>
            </Attributes>
          </CodeCoverage>
        </Configuration>
      </DataCollector>
    </DataCollectors>
  </DataCollectionRunSettings>

To use the .runsettings file, in Visual Studio, click on Test, Test Settings, Select Test Settings File (see below image)

SNAGHTML2d4683

[ExcludeFromCodeCoverage] attribute

Use [ExcludeFromCodeCoverage] attribute wherever appropriate. When a section of code is decorated with this attribute, that section of the code will be skipped for code coverage. Why? In certain cases, you don’t want code to be measured with code coverage. An example would be entity objects that have default property setters (get / set) that has no functionality. If there is “NO” logic developed on either the get and/or set property why measure it?

This ends the first part of this series, on the next part (VSTS Build Definition Setup – .Net Core and NUnit), we will hook up the test tasks in VSTS to include code coverage reporting.

Working With Stand Alone Entity Framework Core 2.0 in .Net Framework 4.6 (above) with SQL Server

When you work with EF Core, the initial project creation requires that you select a .Net Core project. This is all good if you’re entirely working with a .Net Core App. What about .Net Frameworks 4.6 and above? How about starting with a .Net Core app without EF Core?  There are multiple guides out there, scattered in multiple places to get EF Core installed and working. This post is intended to have you install EF Core at minimum and walk through the setup and unit testing scenarios.

As of this post, we’re utilizing Entity Framework 2.0 within .Net Framework 4.7 projects. Entity Framework Core 2.0 is compatible with versions of .Net Framework 4.6.x and above. Why use EF Core on .Net Framework projects? Simply put, compatibility reasons. Certain services in azure (Azure Functions for example) currently supports .Net Framework projects and not .Net Core. To circumvent the problem, the EF team has done a great job with EF Core so it can work with various .Net versions. While the intention of this post is adopting EF Core 2.0 within .Net 4.7, the goal is to show the features built in EF Core 2.0. In particular, I really love the capability to unit test databases through in memory channel. We’ll talk about this later.

If this is your first time working through Entity Framework, I strongly suggest going through the “Get Started Guide”, see the following article from Microsoft: https://docs.microsoft.com/en-us/ef/core/get-started/

Let’s get started: Create a .Net 4.7 Project

In Visual Studio, create a new project:

EF1

Install the following Nuget Packages for EF Core:

Microsoft.EntityFrameworkCore – Core EF libraries

Microsoft.EntityFrameworkCore.Design – The .NET Core CLI tools for EF Core

Microsoft.EntityFrameworkCore.SqlServer – EF Core Database Provider for SQL. In this case, we’ll be using SQL EF Core Provider

Microsoft.EntityFrameworkCore.Relational– EF Core Libraries that allows EF to be used to access many different databases. Some concepts are common to most databases, and are included in the primary EF Core components. Such concepts include expressing queries in LINQ, transactions, and tacking changes to objects once they are loaded from the database. NOTE: If you install Microsoft.EntityFrameworkCore.SqlServer, this will automatically install the Relational assemblies

Microsoft.EntityFrameworkCore.Tools – EF Core tools to create a model from the database

Microsoft.EntityFrameworkCore.SqlServer.Design– EF Core tools for SQL server

Microsoft.EntityFrameworkCore.InMemory – EF Core In-memory database provider for Entity Framework Core (to be used for testing purposes). This is or will be your best friend! One of the reasons why I switched to EF Core (besides it’s other cool features). You only need to install this package if you’re doing Unit Testing (which you should!)

Edit the Project Files:

Edit the project file and make sure the following entry appears in the initial property group.

<PropertyGroup> 
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>

For test projects, also make sure the following entry is also present:

<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>

Implementing EF Core

I’m not going to go through the basics of EF Core. I’ll skip the entire overview and just dive deep on implementing EF entities and using the toolsets.

The Model and DBContext:
We’ll be using a simple model and context class here.

public class Employee
    {
        [Key]
        public int EmployeeId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string DisplayName => FirstName + " " + LastName;
        public EmployeeType EmployeeType { get; set; }
    }

    public class EmployeeType
    {
        [Key]
        public int EmployeeTypeId { get; set; }
        public string EmployeeTypeRole { get; set; }
    }

public class DbContextEfCore : DbContext
    {
        public DbContextEfCore(DbContextOptions<DbContextEfCore> options) : base(options) { }

        public virtual DbSet<Employee> Employees { get; set; }
        public virtual DbSet<EmployeeType> EmployeeTypes { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            //When creating an instance of the DbContext, you use this check to ensure that if you're not passing any Db Options to always use SQL. 
            if (!optionsBuilder.IsConfigured)
            {
                var connectingstring = ConfigurationManager.ConnectionStrings["SqlConnectionString"].ConnectionString;
                optionsBuilder.UseSqlServer(connectingstring);
            }
        }
    }

 

EF Tools

Database Migrations – This is far one of the best database upgrade tools that you can use for SQL server. Since we have our model and DBContext, let’s create the scripts to eventually create the database on any target server and update the database when necessary
Before you run any Database Migration commands, you’ll need to ensure that you have a class file that implements IDesignTimeDbContextFactory. This class will be recognized and used by entity framework to provide command line tooling such as code generation and database migrations. Before proceeding, make sure your application or web.config file has the connectingstring values set for your SQL server (see example below)

  <connectionStrings>
    <add name="SqlConnectionString" connectionString="Server=XXXX;Database=XXX;User ID=XXX;Password=XXX;Trusted_Connection=False;Encrypt=True;Connection Timeout=30;" providerName="System.Data.SqlClient"/>
  </connectionStrings>

NOTE: Unfortunately, you cannot use InMemory Database when working with Database Migration Tools. In Memory Database doesn’t use a relational provider.

Here’s an example class file that you can use in your project:

public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<DbContextEfCore>
    {
        public DbContextEfCore CreateDbContext(string[] args)
        {
            var builder = new DbContextOptionsBuilder<DbContextEfCore>();
            //Database Migrations must use a relational provider. Microsoft.EntityFrameworkCore.InMemory is not a Relational provider and therefore cannot be use with Migrations.
            //builder.UseInMemoryDatabase("EmployeeDB");
            //You can point to use SQLExpress on your local machine
            var connectionstring = ConfigurationManager.ConnectionStrings["SqlConnectionString"].ConnectionString;
            builder.UseSqlServer(connectionstring);
            return new DbContextEfCore(builder.Options);
        }
    }

 

When running command line options, make sure you select the .Net Project where you are working on Entity Framework. At the same time, ensure that the default start-up project in solution explorer is set on the EF project

In Visual Studio. Go to Tools > Nuget Package Manager > Package Manager Console

In the PMC (Package Manager Console) type: Add-Migration InitialCreate

After running, this command, notice that it will create the proper class files for you to create the initial database schema.

EF2

Let’s create our database on the target server. For this, run: update-database. When done, you should see the following:

EF3

Your database has been created as well:

EF4

As we all know, requirements do change more often than before (specially in agile environments), a request was made to add the city and zip code to the employee data. This is easy as:

  • Modifying the entity (model)
  • Running “Add-Migration <ChangeSet>”
  • Running “Update-Database”

The Entity Change:

public class Employee
    {
        [Key]
        public int EmployeeId { get; set; }

        public string FirstName { get; set; }

        public string LastName { get; set; }

        public string DisplayName => FirstName + " " + LastName;

        public string City { get; set; }

        public int ZipCode { get; set; }

        public EmployeeType EmployeeType { get; set; }
    }

In the PMC (Package Manager Console) type: Add-Migration AddCityAndZipCodeToEmployee

New file has been created:

EF5

Schema has been added as well:

EF6

In the PMC (Package Manager Console) type: Update-Database

EF7

Changes have been applied to the database directly.

EF8

I’ll leave database migrations here. You can get more information on all the nice and nifty features of database migrations here: https://msdn.microsoft.com/en-us/library/jj554735(v=vs.113).aspx

For all command line options for database migrations: https://docs.microsoft.com/en-us/ef/core/miscellaneous/cli/powershell

Finally, Database Migrations uses the __MigrationsHistory table to store what migrations have been applied to the database.

Testing EF Core

This is my favorite topic! I can’t stress enough how easy it is to Unit Test databases using EF Core. InMemory Database as part of EF Core sets up the runtime and everything else you need to Unit Test databases. Before EF Core, it really took sometime to setup mock objects, fakes, dependencies, etc… With EF Core InMemory Database, I was able to focus more on the design of the database rather spend time focusing on the test harness. The short story is this, InMemory database is another provider in EF that stores data In Memory during runtime. Meaning, you get all the same benefits, features and functions with EF to SQL (or other provider) except with the beauty of not connecting to an actual provider endpoint (SQL in this case).

Start of by adding a new .Net Framework 4.7 Test Project

EF9

Add a reference to the previously created project with EF Core Enabled.

Add the following nuget packages:

  • Microsoft.EntityFrameworkCore
  • Microsoft.EntityFrameworkCore.InMemory
  • Microsoft.EntityFrameworkCore.SqlServer

Edit the project file and make sure the following entry appears in the initial property group.

<PropertyGroup>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
</PropertyGroup>

Test Class:

[TestClass]
    public class DbContextTests
    {
        protected DbContextOptions<DbContextEfCore> Dboptionscontext;

        [TestInitialize]
        public void TestInitialize()
        {
            //This is where you use InMemory Provider for working with Unit Tests
            //Unlike before, we're you'll need to work with Mocks such as MOQ or RhinoMocks, you can use InMemory Provider
            Dboptionscontext = new DbContextOptionsBuilder<DbContextEfCore>().UseInMemoryDatabase("EmployeeDatabaseInMemoery").Options;

            //Lets switch to use Sql Server
            //var connectionstring = ConfigurationManager.ConnectionStrings["SqlConnectionString"].ConnectionString;
            //Dboptionscontext = new DbContextOptionsBuilder<DbContextEfCore>().UseSqlServer(connectionstring).Options;
        }

        [TestMethod]
        public void ValidateInsertNewEmployeeRecord()
        {
            //Employee Entity Setup
            var employee = new Employee
            {
                FirstName = "Don",
                LastName = "Tan",
                City = "Seattle",
                ZipCode = 98023,
                EmployeeType = new EmployeeType
                {
                    EmployeeTypeRole = "HR"

                }
            };

            using (var dbcontext = new DbContextEfCore(Dboptionscontext))
            {
                dbcontext.Employees.Add(employee);
                dbcontext.SaveChanges();
                Assert.IsTrue(dbcontext.Employees.Any());
            }
        }
    }

Test Initialize method is where I set the DBContext option to use InMemory vs SQL providers. When you run the tests switching either InMemory or SQL DBContext options, both tests run successfully.

Switching to Sql EF Core provider, stores the data to the DB directly:

EF10

Using InMemory provider for EF Core lets you focus unit testing your database more efficiently. This is definitely useful when working with multiple tables and each table has multiple relationships (keys and other constraints). More importantly, this lets you focus designing the appropriate DB strategies such as repository or unit of work patterns.

Using NUNIT Test Framework do validate deployments in VSTS Release Management

As of writing of this post, I prefer NUnit when using .Net for developing application deployment tests. Why? Simply put, better support and test execution. More importantly, the ability to simply reference test runners without installing the actual test execution files (Like VSTest or MSTest). This guide will walk you through on developing simple tests via NUnit, build the artifacts then executing those tests on the server where the application is being deployed to.

NOTE: NUnit and MSTest are completely separate testing frameworks. You can mix/reference both NUnit and MSTest Test frameworks in the same VS Test Project and Tests (Test Methods) without duplicating the same tests. This however is a separate post.

Backing a bit, we use VSTS as our primary adoption platform for Continuous Integration and Deployment. In short, CI through build definitions and CD through release management. We have standards within our processes and one of those standards is to ensure that any application being deployed has the appropriate application deployment tests. Nope, I’m not talking about smoke tests where an application is being verified “after” its been deployed through a UI tests or simply some high level scenarios. I refer to “application deployments tests” as tests developed to be run specifically on the server where the application is being deployed to. This mostly maps to any application being deployed and hosted to individual servers (IIS hosted machines if you’re a Windows shop)

Let’s begin.

1) Develop NUnit Tests to validate application deployment.

Before proceeding, you need to ensure that you at least have one application deployment tests. The number of tests vary on the application that you want to verify. In my scenario, I’m deploying a Web API so one of the tests to validate is whether a web method works after deployment.

Here, we’re going to start off with an existing Visual Studio Test Project. However, If this is your first time (as a devops person), feel free to go through this guide, “Create a unit test project

Reference NUnit assemblies in your test project.

Expand your Test Project, right click on “References” and click on “Manage NuGet Packages”. The nuget package explorer window opens and type in “Nunit” under the search box within the “Browse” tab.

Nunit1

NOTE: If you want to see your NUnit Tests within Visual Studio, you’ll need to install the NUnit Test Adapter within Visual Studio, “Tools”, “Extensions and Updates

Decorate your code with the NUnit Test Attributes

Nunit2

The goal for running tests is to ensure that the app has been deployed properly. In these situations, tests would then normally target or construct the URL based on the IP Address or the HostName of the sever then the application URL. This would be something that you’ll need to plan on how to build your application URLs correctly. There are many ways to do this and I’ll creating a separate blog post for this.

Once you’ve validated your tests targeting the right server, we then package the tests as artifacts to be executed on the server. For this, see the next step.

2) Package your tests files as build artifacts to be consumed in VSTS Release Management

In normal CI processes, your tests files should be coupled together with your application deployment files. In an event that your tests files are not packages together with your application deployments files, follow these steps:

Start off by creating a build definition in VSTS. For this, follow this guide. “Build and deploy your app”.

Build your Test Solution with the Test Project

Nunit3

Publish your Test Artifacts/Files

Nunit4

NOTE: Very Important! Make sure your Test Files includes the NUnit Console Runner assemblies and executable. There are many ways to reference the Nuget Test Runner files but for the purpose of this demo, ensure that your Test artifacts include all test runner files. Here’s a snipped of such files.

Nunit5

3) Modify your release tasks to include running application deployment tests via NUnit

In your existing release definition:

Add the build artifacts (test artifacts) as part of your Release Definition

Nunit7

Add a Command Line Task to Run NUnit Tests

Nunit6

And the result…

Nunit8

Referencing MSTest And MSTestv2 Unit Testing Framework Through Namespace Aliasing

Let me start-off by explaining what MSTest and MSTestV2 are.

MSTest (Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll) – This is the unit testing framework that comes pre-installed when you install Visual Studio IDE (Available through the .Net Framework – GAC)

MStestV2 (Microsoft.VisualStudio.TestPlatform.TestFramework.dll) – This is now the open source version of MSTest. With any open source libraries, there are lots of good contributions but also features do change more frequent. More often, removed (or enhanced in this case). You install this version of MSTest through Nuget.

With that brief description on MSTest and MSTestV2, now comes the question: Why would I reference both MSTest and MSTestV2 in the same test project? Well, there are two reasons; Backwards compatibility and issues exposed in MSTestV2 that is still being worked on.

In terms of backwards compatibility, I work with many developers around utilizing data driven features in MSTest. The good back then is that we can data drive tests using many data source providers (e.g. Excel, SQL, etc…). The bad part is that the open source framework (MSTestV2) only supports both XML and CSV as the data source providers (Though, it supports DataRow as a data source which is good).

Ideally, I would ask the developers to migrate directly to MSTestV2 but in this case, I’d like for them to regress any issues they find in MSTest and see what else could break in MSTestV2.

The issue: Referencing both dlls causes collisions and/or conflicts simply because most of the attributes (or All- [TestClass],[TestMethod],etc…) uses the exact same namespace:

Microsoft.VisualStudio.TestTools.UnitTesting

The Solution! Welcome back namespace aliasing. The last time I used namespace aliasing, oh, I can’t remember exactly but probably late 2006 (C# 2.0)

With namespace aliases, you can reference multiple assemblies even if those assemblies have the exact same namespace

Step 1: Provide an alias name at the assembly level.

Go to the properties of each assembly and provide an alias.

MSTest1

Step 2: In code, refer to the assembly alias using the C# reserved keywordextern

extern alias FrameworkV1;
extern alias FrameworkV2;

using System;
using TestFrameworkV1 = FrameworkV1.Microsoft.VisualStudio.TestTools.UnitTesting;
using TestFrameworkV2 = FrameworkV2.Microsoft.VisualStudio.TestTools.UnitTesting;

Step 3: Refer to the appropriate assembly classes and/or attributes through the namespace alias (variable you created through the “using” statement)

MSTest2

And the result

MSTest3