Tuesday, 21 August 2018

Creating a validation attribute for multiple enum values in C#

This article will present a validation attribute for multiple enum value in C#. In C#, generics is not supported in attributes. The following class therefore specifyes the type of enum and provides a list of invalid enum values as an example of such an attribute.


using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace ValidateEnums
{

    public sealed class InvalidEnumsAttribute : ValidationAttribute
    {

        private List<object> _invalidValues = new List<object>();



        public InvalidEnumsAttribute(Type enumType, params object[] enumValues)
        {
            foreach (var enumValue in enumValues)
            {
                var _invalidValueParsed = Enum.Parse(enumType, enumValue.ToString());
                _invalidValues.Add(_invalidValueParsed);
            }
        }

        public override bool IsValid(object value)
        {
            foreach (var invalidValue in _invalidValues)
            {
                if (Enum.Equals(invalidValue, value))
                    return false;
            }
            return true;
        }

    }

}

Let us make use of this attribute in a sample class.

 public class Snack
    {
        [InvalidEnums(typeof(IceCream), IceCream.None, IceCream.All )]
        public IceCream IceCream { get; set; }

    }

We can then test out this attribute easily in NUnit tests for example:

[TestFixture]
    public class TestEnumValidationThrowsExpected
    { 

        [Test]
        [ExpectedException(typeof(ValidationException))]
        [TestCase(IceCream.All)]
        [TestCase(IceCream.None)]
        public void InvalidEnumsAttributeTest_ThrowsExpected(IceCream iceCream)
        {
            var snack = new Snack { IceCream = iceCream };
            Validator.ValidateObject(snack, new ValidationContext(snack, null, null), true);
        }

        [Test]
        public void InvalidEnumsAttributeTest_Passes_Accepted()
        {
            var snack = new Snack { IceCream = IceCream.Vanilla };
            Validator.ValidateObject(snack, new ValidationContext(snack, null, null), true);
            Assert.IsTrue(true, "Test passed for valid ice cream!"); 
        }


Sunday, 19 August 2018

ConfigurationManager for .Net Core

.Net Core is changing a lot of the underlying technology for .Net developers migrating to this development environment. System.Configuration.ConfigurationManager class is gone and web.config and app.config files, which are XML-based are primrily replaced with .json files, at least in Asp.NET Core 2 for example. Let's look at how we can implement a class to let you at least be able to read AppSettings in your applicationSettings.json file which can be later refined. This implementation is my first version.

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using System.IO;
using System.Linq;

namespace WebApplication1
{

    public static class ConfigurationManager
    {

        private static IConfiguration _configuration;

        private static string _basePath;

        private static string[] _configFileNames;

        public static void SetBasePath(IHostingEnvironment hostingEnvironment)
        {
            _basePath = hostingEnvironment.ContentRootPath;
            _configuration = null;

            //fix base path
            _configuration = GetConfigurationObject();
        }

        public static void SetApplicationConfigFiles(params string[] configFileNames)
        {
            _configFileNames = configFileNames;
        }

        public static IConfiguration AppSettings
        {
            get
            {
                if (_configuration != null)
                    return _configuration;

                _configuration = GetConfigurationObject();
                return _configuration;
            }
        }

        private static IConfiguration GetConfigurationObject()
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(_basePath ?? Directory.GetCurrentDirectory());
            if (_configFileNames != null && _configFileNames.Any())
            {
                foreach (var configFile in _configFileNames)
                {
                    builder.AddJsonFile(configFile, true, true);
                }
            }
            else
                builder.AddJsonFile("appsettings.json", false, true);
            return builder.Build(); 
              
        }
    }
}

We can then easily get app settings from our config file:

  string endPointUri = ConfigurationManager.AppSettings["EmployeeWSEndpointUri"];

Sample appsettings.json file:
  {
  "Logging": {
    "IncludeScopes": false,
    "Debug": {
      "LogLevel": {
        "Default": "Warning"
      }
    },
    "Console": {
      "LogLevel": {
        "Default": "Warning"
      }
    }
  },
  "EmployeeWSEndpointUri": "https://someserver.somedomain.no/someproduct/somewcfservice.svc"

}

 
If you have nested config settings, you can refer to these using the syntax SomeAppSetting:SomeSubAppSetting, like "Logging:Debug:LogLevel:Default".

Creating a VM in Azure with Powershell core in Linux

This article will describe how a virtual machine in Azure from a command line in Linux. Powershell core will be used and I have tested this procedure using the Linux distribution MX-17 Horizon. First, we will update the package list for APT (Advanced Packaging Tool), get the latest versions of cURL and apt-transport-https and add the GPG key for the Microsoft repository for Powershell core. Then APT is once more updated and the package powershell is installed. The following script works on Debian-based distributions:
 sudo apt-get install 
 sudo apt-get install curl apt-transport-https
 curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
 
 sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-debian-jessie-prod jessie main" > /etc/apt/sources.list.d/microsoft.list'

 sudo apt-get update
 sudo apt-get install -y powershell 
 
On my system however, I need to make use of Snap packages. This is because MX-Horizon fails to install powershell due to restrictions on libssl1.0 and libicu52. Anyways this route let me start up Powershell on MX-17 Horizon:
 sudo apt-get install snapd
 sudo snap install powershell-preview --classic
 sudo snap run powershell-preview 
 






Logging into Azure RM account

Running powershell-preview allows you to both run Powershell commands such as Get-ChildItem ("ls") and Unix tools such as ls (!) from the Powershell command line. We will first install the Powershell module AzureRM.NetCore inside the Powershell session, which is running.
 Install-Module AzureRM.Netcore
 Get-Command -Module AzureRM.Compute.Netcore
 

The last command is executed to check that the Powershell module is available. Type [Y] to allow the module installation in the first step. Next off, logging into the Azure Resource Manager. Type the following command in Powershell core:
 Login-AzureRMAccount
 
Running this command will prompt a code and tell you to open up a browser window and log on to: https://microsoft.com/devicelogin Then you log in to your Azure account and if you are successfully logged in, your Powershell core session is authenticated and you can access your Azure account and its resources!

Creating the Virtual machine in Azure

Creating the virtual machine is done in several steps. The script below is to be saved into a Powershell script file, forexample AzureVmCreate.ps1. There are many steps involved into establishing an installation of a VM in Azure. We will in this sample set up all the necessities to get an Ubuntu Server LTS. If you already have got for example a resource group in Azure, the script below can use this resource group instead of creating a new one. The list of steps are as follows to create a Linux VM in Azure:
  • Create a resource group
  • Create a subnet config
  • Create a virtual network, set it up with the subnet config
  • Create a public IP
  • Create a security rule config to allow port 22 (SSH)
  • Create a network security group nsg and add in the security rule config
  • Create a network interface card nic and associate it with the public ip and the nsg
  • Create a VM config and set the operating system, OS image and nic
  • Create a VM config's SHH public key config
  • Create a VM - Virtual Machine in Azure !

param([string]$resourceGroupName,
       [string]$resourceGroupLocation,
       [string]$vmComputerName,
       [string]$vmUser, 
       [string]$vmUserPassword,
       [string]$virtualNetworkName)
#Write-Host $resourceGroupName
#Write-Host $resourceGroupLocation
#Write-Host $vmComputerName
#Write-Host $vmUser
#Write-Host $vmUserPassword

# Definer user name and blank password
$securePassword = ConvertTo-SecureString ' ' -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential ("azureuser", $securePassword)

New-AzureRmResourceGroup -Name $resourceGroupName -Location $resourceGroupLocation 

$subnetConfig = New-AzureRmVirtualNetworkSubnetConfig -Name default -AddressPrefix 10.0.0.0/24  

$virtualNetwork = New-AzureRMVirtualNetwork -ResourceGroupName $resourceGroupName -Name `
$virtualNetworkName -AddressPrefix 10.0.0.0/16 -Location $resourceGroupLocation `
-Subnet $subnetConfig

Write-Host "Subnet id: " $virtualNetwork.Subnets[0].Id

# Create a public IP address and specify a DNS name
$pip = New-AzureRmPublicIpAddress -ResourceGroupName $resourceGroupName -Location $resourceGroupLocation `
  -Name "mypublicdns$(Get-Random)" -AllocationMethod Static -IdleTimeoutInMinutes 4

# Create an inbound network security group rule for port 22
$nsgRuleSSH = New-AzureRmNetworkSecurityRuleConfig -Name myNetworkSecurityGroupRuleSSH  -Protocol Tcp `
  -Direction Inbound -Priority 1000 -SourceAddressPrefix * -SourcePortRange * -DestinationAddressPrefix * `
  -DestinationPortRange 22 -Access Allow

# Create a network security group
$nsg = New-AzureRmNetworkSecurityGroup -ResourceGroupName $resourceGroupName -Location $resourceGroupLocation `
  -Name myNetworkSecurityGroup -SecurityRules $nsgRuleSSH

# Create a virtual network card and associate with public IP address and NSG
$nic = New-AzureRmNetworkInterface -Name myNic -ResourceGroupName $resourceGroupName -Location $resourceGroupLocation `
  -SubnetId $virtualNetwork.Subnets[0].Id -PublicIpAddressId $pip.Id -NetworkSecurityGroupId $nsg.Id


# Create a virtual machine configuration
$vmConfig = New-AzureRmVMConfig -VMName $vmComputerName -VMSize Standard_D1 | `
Set-AzureRmVMOperatingSystem -Linux -ComputerName $vmComputerName -Credential $cred -DisablePasswordAuthentication | `
Set-AzureRmVMSourceImage -PublisherName Canonical -Offer UbuntuServer -Skus 14.04.2-LTS -Version latest | `
Add-AzureRmVMNetworkInterface -Id $nic.Id


# Configure SSH Keys
$sshPublicKey = Get-Content "$HOME/.ssh/id_rsa.pub"
Add-AzureRmVMSshPublicKey -VM $vmconfig -KeyData $sshPublicKey -Path "/home/azureuser/.ssh/authorized_keys"

# Create a virtual machine
New-AzureRmVM -ResourceGroupName $resourceGroupName -Location $resourceGroupLocation -VM $vmConfig

 
We can then instantiate a new VM in Azure running the script above, which will create a standard D1 blade server in Azure with approx. 3,5 GB RAM and 30 GB disk space with Ubuntu Server LTS latest from the publisher Canonical by calling this script like for example: ./AzureVmCreate.ps1 -resourceGroupName "SomeResourceGroup" -resourceGroupLocation "northcentralus" "SomeVmComputer" -vmUser "adminUser" -password "s0m3CoolPassw0rkzzD" -virtualNetworkName "SomeVirtualNetwork" After the VM is created, which for me took about 5 minutes time before the VM was up and running in Azure, you can access it by visiting the Azure portal at https://portal.azure.com You can then take notice of the public IP adress that the script in this article created and connect with : ssh azureuser@ip_address_to_linux_VM_you_just_created_in_Azure! The following images shows me logging into the Ubuntu Server LTS Virtual Machine that was created with the Powershell core script in this article!
A complete list of available Linux images can be seen in the Azure marketplace or in the Microsoft Docs: Linux VMs in Azure overview After installation, you can run the following cmdlet to clean up the resource group and all its resources, including the VM you created for testing.
  Remove-AzureRmResourceGroup -Name myResourceGroup