Thursday, May 21, 2020

Microsoft Build 2020: What's new with C#

Today is a special day for me. I got the opportunity to join Microsoft Build as a speaker. The session was about What's new with C#. It was mostly on the latest features that got shipped yesterday with Studio Version 16.7.0 Preview 1.0 and an early glance on what's hoping to be released on coming November as part of C# 9 final release.

The best part is, legendary Scott Hunter joined the session and it was like a dream come true.
Microsoft Build 2020: What's new with C#
Want to know more about C# 9, check these out:

Happy Coding.

Regards,
Jaliya

Wednesday, May 20, 2020

C# 9: Pattern Matching Improvements

At the Microsoft Build today, an early glimpse of C# 9.0 was announced. While C# 9.0 is officially to be announced in November, 2020 during dotnetconf, some of the features have already found their way out with Visual Studio Version 16.7.0 Preview 1.0. Some of those features are improvements to Pattern Matching which was/is a trending topic in C# 7.2 and 8.

With C# 9, we have access to type patterns, relational patterns and logical patterns. Let's have a look at what these are.

Consider I have a base class Customer, and two derived classes WalkInCustomer and OnlineCustomer. And I have the following method to get the discount percentage based on the Customer written using C# 9.0.
public double GetDiscountPercentage(Customer customer)
{
    return customer switch
    {
        WalkInCustomer => 0.2,
        OnlineCustomer onlineCustomer => onlineCustomer.Age switch
        {
            >= 60 and < 70 => 0.4,
            >= 70 => 0.6,
            _ => 0.3,
        },
        not null => throw new ArgumentException(nameof(customer)),
        null => throw new ArgumentNullException(nameof(customer))
    };
}
Here in the outer switch statement, I am using type pattern and logical (and, or, not) patterns. If the customer is not of type WalkInCustomer and OnlineCustomer, then I am throwing an exception. If the Customer is WalkInCustomer, giving a straight off discount of 20%. Then if the customer in an OnlineCustomer, based on the customers' age I am giving different discounts. Here to match the age, relational pattern along with logical pattern is used.

Isn't this nice!

On a last note, make sure you are using Visual Studio Version 16.7.0 Preview 1.0 and LangVersion is set to preview in your csproj file to try out early C# 9.0. And you can also check out https://sharplab.io/ and play around with other features that are still finding their way out!

Happy Coding.

Regards,
Jaliya

Saturday, May 9, 2020

ASP.NET Core Integration Tests: Different Database for Each Test

It's a common requirement when writing Integration Tests, the test data to be isolated, so we are not interfering with other tests. And also, for some tests, you can use an InMemory database, but in some cases, you might require an actual SQL Server database. In this post, let's see how we can use a different database for each Integration Test in ASP.NET Core. 

Please note the data access technology is EF Core.

ASP.NET Core provides a nice set of building blocks through Microsoft.AspNetCore.Mvc.Testing to efficiently write Integrated Tests for all kinds of ASP.NET Core Web Applications. There is a very detailed documentation page at Integration tests in ASP.NET Core which describes all the important aspects, so I am not going to write about those.

So I have a custom TestWebApplicationFactory deriving from WebApplicationFactory<TEntryPoint>. And there when Configuring the WebHost, I am expecting a builder setting for TestDatabaseType. And based on that, I am configuring the database context.
public class TestWebApplicationFactory : WebApplicationFactory<Startup>
{
    private static readonly InMemoryDatabaseRoot _inMemoryDatabaseRoot = new InMemoryDatabaseRoot();

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        string _inMemoryConnectionString = "<InMemoryConnectionString>";
        string _sqliteConnectionString = "<SqliteConnectionString>";
        string _sqlConnectionString = "<SqlServerConnectionString>";

        builder.ConfigureServices(services =>
        {
            if (!Enum.TryParse(builder.GetSetting(TestConstants.TestDatabaseTypeKey), out TestDatabaseType testDatabaseType))
            {
                testDatabaseType = TestDatabaseType.InMemory;
            }

            ServiceDescriptor descriptor = services
                .SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<ApplicationCoreDbContext>));

            if (descriptor != null)
            {
                services.Remove(descriptor);
            }

            services.AddDbContext<ApplicationCoreDbContext>(options =>
            {
                switch (testDatabaseType)
                {
                    case TestDatabaseType.InMemory:
                        options.UseInMemoryDatabase($"{_inMemoryConnectionString}", _inMemoryDatabaseRoot);
                        break;
                    case TestDatabaseType.Sqlite:
                        options.UseSqlite(_sqliteConnectionString);
                        break;
                    case TestDatabaseType.SqlServer:
                        options.UseSqlServer(_sqlConnectionString);
                        break;
                }
            }); // other setup
        });
    }
}
and from the tests, when I am creating the HttpClient, I am just setting up a builder setting to specify which database to use.
public class UsersControllerTests : IClassFixture<TestWebApplicationFactory>
{
    private readonly TestWebApplicationFactory _factory;

    public UsersControllerTests(TestWebApplicationFactory factory)
    {
        _factory = factory;
    }

    [Fact]
    public async Task TestOne()
    {
        HttpClient client = _factory.WithWebHostBuilder(builder =>
            {
                builder.UseSetting(TestConstants.TestDatabaseTypeKey, TestDatabaseType.InMemory.ToString()); // other setup
            })
            .CreateClient();
    }

    [Fact]
    public async Task TestTwo()
    {
        HttpClient client = _factory.WithWebHostBuilder(builder =>
            {
                builder.UseSetting(TestConstants.TestDatabaseTypeKey, TestDatabaseType.SqlServer.ToString()); // other setup
            })
            .CreateClient();
    }
}
It's pretty easy, right!

Happy Coding.

Regards,
Jaliya

Saturday, May 2, 2020

Hybrid Connection for an Azure App Service

In this post, let's see how we can set up a Hybrid Connection for an Azure App Service. In simple Hybrid Connection for an Azure App Service allows your App Service to access a service that is not exposed to the internet. 

Better to go by an example, let's say you have an API running in your local machine and you need to consume this API from an Azure-hosted App Service.

I have created a very simple ASP.NET Core Web API application, something like below.
using Microsoft.AspNetCore.Mvc;
 
namespace SampleApi.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class HelloController : ControllerBase
    {
        [HttpGet]
        public ActionResult<stringGet()
        {
            return Ok("Hello from localhost.");
        }
    }
}
And this is running locally at https://localhost:44350 and I can invoke like this. https://localhost:44350/hello. I have another ASP.NET Core Web API application that will consume this API.
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
 
namespace AppServiceWithHybridConnection.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class ValuesController : ControllerBase
    {
        [HttpGet]
        public async Task<ActionResult<string>> Get()
        {
            using var httpClientHandler = new HttpClientHandler
            {
                ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
            };
            using var httpClient = new HttpClient(httpClientHandler);
            HttpResponseMessage httpResponseMessage = await httpClient.GetAsync("https://localhost:44350/hello");
            string message = await httpResponseMessage.Content.ReadAsStringAsync();
 
            return Ok($"Message: {message}");
        }
    }
}
So if I run both these applications locally, of course, it will work.
Running locally
Now, I have created an Azure App Service with below configurations.
Create Azure Windows App Service
Note: I have created it as Azure Windows App Service. And this is required as Hybrid Connections are only available in Windows.

Now, I have deployed the consumer API to this service, exact same code, and now if I go to https://app-hybridconnection-demo.azurewebsites.net/values, of course, it's not going to work. There is no publicly available endpoint https://localhost:44350/hello which this API is calling.
Cannot connect to localhost
Now comes the interesting part. Now let's set up a Hybrid Connection, so the Azure-hosted app service has access to our locally running API.

First, from inside our Windows App Service, I am going to Networking.
Networking
Now I am clicking on Configure your hybrid connection endpoints.
Configure your hybrid connection endpoints
From the next screen, there are 2 options.
  1. Add hybrid connection
  2. Download connection manager
Hybrid Connections
I have clicked on Add hybrid connection and from the next page, I am clicking on Create new hybrid connection.
Create new hybrid connection
Here I am configuring the connection. The important thing here is providing the Endpoint Host and Endpoint Port. I have given the local API information.
Create new hybrid connection
Now I am clicking on Create. Once is created, it will be displayed.
Hybrid Connection Created
When you click on the hybrid connection, you can see the properties and copy the GATEWAY CONNECTION STRING.

All good up to this point. Now let's Download connection manager and install. It's a very straightforward installation and don't think no screenshots/explanation is necessary. Once installed, find and open Hybrid Connection Manager UI.
Hybrid Connection Manager UI
Here, you can either click on Add a new Hybrid Connection or Enter Manually. Add a new option will take you through the Azure Sign-In process which can be a pain, I prefer to use the manual process.
Add Hybrid Connection From Connection String
From here, I am pasting the connection string I have copied and clicked on Add.
Hybrid Connection Is Connected
You can see it's now connected.

Now I am going back to my Azure-hosted API endpoint and just refreshing the browser.
App Service Consumes From Locallhost
It's working and our Azure-hosted API is consuming the locally running API. Isn't this great?

Learn more,
   Azure App Service Hybrid Connections

Happy Coding.

Regards,
Jaliya

Thursday, April 30, 2020

Visual C# Technical Guru - March 2020

Another month as a judge in Microsoft TechNet Guru Awards under Visual C# category. The TechNet Guru Awards celebrate the technical articles on Microsoft TechNet.

Post in Official Blog,
https://techcommunity.microsoft.com/t5/wiki-ninjas-blog/technet-wiki-guru-winners-march-2020/ba-p/1349990
Visual C# Technical Guru - March 2020
Happy Coding.

Regards,
Jaliya

Sunday, April 5, 2020

Basic Azure DevOps Pipeline to deploy an ASP.NET Core Containerized Application and Applying EF Core Database Migrations

In this post, I am going to share a sample azure-pipelines.yml to deploy an ASP.NET Core Containerized Application and Applying Entity Framework Core Database Migrations.

Here the pipeline is based on the following requirement which I believe is common.
  1. When doing the release, we need to apply database migrations in the target database. The migration script will be created on the fly during the execution of the pipeline and will be executed on the target database.
  2. We might need to execute some additional SQL scripts in the target database after the database is migrated, maybe to CREATE/ALTER stored procedures, etc. The pipeline assumes those scripts are residing in scripts/postdeploy folder in the root of the project.
I have left comments in the below azure-pipelines.yml itself, so I don't think any other explanation is necessary. You should be able to changes this as per your needs.

azure-pipelines.yml
trigger:
- master

resources:
repo: self

variables:
  # Container registry service connection established during pipeline creation  
  dockerRegistryServiceConnection: '<dockerRegistryServiceConnection>'

  # Something like xxxxxx.azurecr.io
  containerRegistry: '<containerRegistry>'

  # Docker image name
  imageRepository: '<imageRepository>'

  # The relative localtion of the Dockerfile
  dockerfilePath: '$(Build.SourcesDirectory)/Dockerfile'

  # This docker image will be tagged using this
  tag: '$(Build.BuildId)'

  # Agent to be used
  vmImageName: 'ubuntu-latest'

# We have 2 stages, 
#   1. Build
#   2. Release
stages:

# This stage will and build the docker image and push it to ACR
stage: Build
  displayName: Build and push stage
  jobs:
    - job: Build
      displayName: Build
      pool:
        vmImage: $(vmImageName)
      steps:

      - task: Docker@2
        displayName: Build and push an image to container registry
        inputs:
          command: buildAndPush
          repository: $(imageRepository)
          dockerfile: $(dockerfilePath)
          containerRegistry: $(dockerRegistryServiceConnection)
          tags: |
            $(tag)

# This stage will,
#   1. Create a database migration script named update-database.sql inside scripts folder
#   2. Execute the migration script first
#   3. Then execute all the other SQL scripts inside scripts/postdeploy folder
#   4. Finally spin up the container at the target web app
stage: Release  
  displayName: Release stage
  jobs:
    - job: Release
      displayName: Release
      pool:
        vmImage: $(vmImageName)
      steps:

      # This task is required to run dotnet-ef which is getting installed in the next task
      - task: UseDotNet@2
        inputs:
          version: '3.1.200'

      # Install dotnet-ef
      - task: DotNetCoreCLI@2
        displayName: Install dotnet-ef
        inputs:
          command: 'custom'
          custom: 'tool'
          arguments: 'install --global dotnet-ef --ignore-failed-sources'

      # Generate update-database.sql using dotnet-ef
      - task: PowerShell@2
        displayName: Generate Database Migration Script
        inputs:
          targetType: 'inline'
          script: |
            # Splitted into multiple lines for brevity. Below command needs to be a single line
            dotnet ef migrations script 
              -i 
              -o "$(Build.SourcesDirectory)/scripts/update-database.sql" 
              --project "$(Build.SourcesDirectory)/<ProjectThatContainsTheDbContext.csproj>" 
              --startup-project "$(Build.SourcesDirectory)/<TheStartupProject.csproj>"

    # Install SqlServer module to be able to run Invoke-SqlCmd in the next task
      - task: PowerShell@2
        displayName: PowerShell Install-Module SqlServer
        inputs:
          targetType: 'inline'
          script: 'Install-Module -Name SqlServer -AllowPrerelease -Force -Verbose -Scope CurrentUser'

    # Execute the update-database.sql in the target database
      - task: PowerShell@2
        displayName: PowerShell Invoke-Sqlcmd Database Migration Script
        inputs:
          targetType: 'inline'
          script: |
            # Splitted into multiple lines for brevity. Below command needs to be a single line
            Invoke-Sqlcmd 
              -ServerInstance "<ServerInstance>" 
              -Database "<Database>" 
              -Username "<Username>" 
              -Password "<Password>" 
              -Inputfile "$(Build.SourcesDirectory)/scripts/update-database.sql" 
              -Verbose 
              -ConnectionTimeout 120

      # Execute other SQL scripts in the target database
      - task: PowerShell@2
        displayName: PowerShell Invoke-Sqlcmd Other SQL Scripts
        inputs:
          targetType: 'inline'
          script: |
            $files = Get-ChildItem $(Build.SourcesDirectory)/scripts/postdeploy
            foreach ($f in $files) 
            {
              # Splitted into multiple lines for brevity. Below command needs to be a single line
              Invoke-Sqlcmd 
                -ServerInstance "<ServerInstance>" 
                -Database "<Database>" 
                -Username "<Username>" 
                -Password "<Password>" 
                -Inputfile "$f" 
                -Verbose 
                -ConnectionTimeout 120
            }

      # All good, let's spin up a new container at the target web app
      - task: AzureWebAppContainer@1
        displayName: Spin up the container
        inputs:
          azureSubscription: '<azureSubscription>'
          appName: '<appName>'
          containers: $(containerRegistry)/$(imageRepository):$(tag)

          # Something like dotnet TheStartupProject.dll
          containerCommand: '<containerCommand>'
Hope this helps.

Happy Coding.

Regards,
Jaliya

Friday, April 3, 2020

Invoke Invoke-SqlCmd inside a Linux Agent in Azure DevOps Pipelines

I was trying to execute Invoke-Sqlcmd command inside a Linux Agent in Azure DevOps Pipelines and getting this error.

"The term 'Invoke-Sqlcmd' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again."
The term 'Invoke-Sqlcmd' is not recognized...
Then according to this post: Invoke-Sqlcmd is Now Available Supporting Cross-Platform, installed SqlServer module from another PowerShell@2 task.
- task: PowerShell@2
  inputs:
    targetType: 'inline'
    script: 'Install-Module -Name SqlServer -AllowPrerelease'
That task passed, but still, the PS Invoke-SqlCmd task was throwing the same error. But noticed this warning inside PS Install-Module task.

"WARNING: User declined to install module (SqlServer)."
WARNING: User declined to install module (SqlServer)
Then I modified the PS Install-Module task to pass in an additional parameter -Scope CurrentUser.
- task: PowerShell@2
  inputs:
    targetType: 'inline'
    script: 'Install-Module -Name SqlServer -AllowPrerelease -Force -Verbose -Scope CurrentUser'
This time it got installed successfully.
Install-Module -Name SqlServer
And finally I was able to run Invoke-SqlCmd inside a Linux Agent in Azure DevOps Pipeline.
Invoke-SqlCmd is running
Hope this helps!

Happy Coding.

Regards,
Jaliya

Thursday, April 2, 2020

dotnet ef is Failing in Azure DevOps Pipeline When the Agent is ubuntu-latest

I was trying to run dotnet ef command within Azure DevOps Pipeline and it's failing with the error: Could not execute because the specified command or file was not found. This is even after installing the dotnet ef tool.
trigger:
- master
 
resources:
- repo: self
 
variables:
  vmImageName: 'ubuntu-latest'
 
stages:
 
- stage: Build
  displayName: Build and push stage
  jobs:  
  - job: Build
    displayName: Build
    pool:
      vmImage: $(vmImageName)
    steps:
    - task: DotNetCoreCLI@2
      displayName: Install dotnet-ef
      inputs:
        command: 'custom'
        custom: 'tool'
        arguments: 'install --global dotnet-ef'
    - task: DotNetCoreCLI@2
      displayName: Check dotnet-ef version
      inputs:
        command: 'custom'
        custom: 'ef'
        arguments: '--version'
So here I have 2 tasks, in the first task is I am installing dotnet-ef and in the second task, I am just checking the version.

This is failing with the error: Could not execute because the specified command or file was not found.

The reason turned out to be, we need to use the UseDotnet@2 task first, to install the dotnet sdk and then install the dotnet ef. This will make dotnet-ef installed into same path as of dotnet sdk and will make it able to execute.
Use .NET Core
trigger:
- master
 
resources:
- repo: self
 
variables:
  vmImageName: 'ubuntu-latest'
 
stages:
 
- stage: Build
  displayName: Build
  jobs:  
  - job: Build
    displayName: Build
    pool:
      vmImage: $(vmImageName)
    steps:
    - task: UseDotNet@2
      inputs:
        version: '3.1.200'
    - task: DotNetCoreCLI@2
      displayName: Install dotnet-ef
      inputs:
        command: 'custom'
        custom: 'tool'
        arguments: 'install --global dotnet-ef'
    - task: DotNetCoreCLI@2
      displayName: Check dotnet-ef version
      inputs:
        command: 'custom'
        custom: 'ef'
        arguments: '--version'
So this works. Hope this helps!

Happy Coding.

Regards,
Jaliya

Wednesday, April 1, 2020

IdentityServer4: Adding Additional Claims to the Token

In this post let's see how we can additional claims to the token. This can be either creating tokens or when handling requests to the userinfo or introspection endpoints. In addition, let's have a look at how we can add in additional claims to a client ASP.NET Core application.

IS4, provides an interface IProfileService which we can implement to add in additional claims. It's basically going to be something like this.
public class ProfileService : IProfileService
{
    public ProfileService()
    {
    }
 
    public async Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        // Get all the claims you need to add into the token by context.Subject
        // Set it to context.IssuedClaims
        // This is expected to be populated
    }
 
    public async Task IsActiveAsync(IsActiveContext context)
    {
        // Check context.Subject is allowed to retrieve additional claims
        // Based on that set context.IsActive
        // This is expected to be set
    }
}
So here one of the important things is, context contains a property named Caller, as the name suggests that determines what triggered this call. Unfortunately, it's a type of string, but there is a set of constants in ID4, that you can use.
public static class ProfileDataCallers
{
    public const string UserInfoEndpoint = "UserInfoEndpoint";
    public const string ClaimsProviderIdentityToken = "ClaimsProviderIdentityToken";
    public const string ClaimsProviderAccessToken = "ClaimsProviderAccessToken";
}
So by checking the context.Caller, you can decide which claims you want to include based on the Caller.

Plugging in ProfileService to IdentityServer is quite simple, we can just do as follows when we are adding IdentityServer.
AddProfileService
It's pretty neat!

Sometimes, you might face a scenario, you want to add in additional claims, not when the token issued from ID4, but from the client ASP.NET Core application. Basically, this is how we protect an ASP.NET Core Web API application with an ID4.
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        // set Authority and additional options
    });
Instead of using AddJwtBearer, we can use AddIdentityServerAuthentication authentication handler which is available in IdentityServer4.AccessTokenValidation NuGet package.
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddIdentityServerAuthentication(options =>
    {
        // set Authority and additional options

        options.JwtBearerEvents.OnTokenValidated = OnTokenValidated;
    });
There we can handle the OnTokenValidated event. This will be invoked after the security token has passed validation and a ClaimsIdentity has been generated. So here also we can additional claims. But there is a downside with this approach, each time the API is being called with the token, this will get invoked.
private async Task OnTokenValidated(TokenValidatedContext context)
{
    // var identity = context.Principal.Identity as ClaimsIdentity;
    // identity.AddClaim(new Claim("X","Y");
}
So hope this bit of information helps.

Happy Coding.

Regards,
Jaliya

Tuesday, March 31, 2020

Visual C# Technical Guru - February 2020

Another month as a judge in Microsoft TechNet Guru Awards under Visual C# category. The TechNet Guru Awards celebrate the technical articles on Microsoft TechNet. 

Visual C# Technical Guru - February 2020
Happy Coding. 

Regards,
Jaliya

Sunday, March 22, 2020

Maintain Entity Framework Core DbContext in a Separate Project

Usually, when I have an application that has an API and a Database managed with EF, I really don't like to have the Application DbContext and all the Migrations inside the API project. It's actually very easy to split it out, this is a quick post on how we can maintain Entity Framework Core DbContext in a separate project. 

I have created a Solution WebApplication1 which has 2 projects, one is ASP.NET Core Web API and the other one just a .NET Core Class Library where I am maintaining Database specific things.

Solution
For WebApplication1.Data project, I have installed Microsoft.EntityFrameworkCore.SqlServer package and my WebApplication1.Data.csproj looks like this.
<Project Sdk="Microsoft.NET.Sdk">
 
  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>
 
  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.2" />
  </ItemGroup>
 
</Project>
And I have a very simple ApplicationDbContext.
using Microsoft.EntityFrameworkCore;
 
namespace WebApplication1.Data
{
    public class Employee
    {
        public int Id { getset; }
        public string FirstName { getset; }
        public string LastName { getset; }
    }
 
    public class ApplicationDbContext : DbContext
    {
        public ApplicationDbContext(DbContextOptions options) : base(options)
        {
        }
 
        public DbSet<Employee> Employees { getset; }
    }
}
Then inside my WebApplication1.Api project, I have referenced WebApplication1.Data project and installed Microsoft.EntityFrameworkCore.Tools package there. This is very much required. This is my WebApplication1.Api.csproj looks like.
<Project Sdk="Microsoft.NET.Sdk.Web">
 
  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>
 
  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.2" />
  </ItemGroup>
 
  <ItemGroup>
    <ProjectReference Include="..\WebApplication1.Data\WebApplication1.Data.csproj" />
  </ItemGroup>
 
</Project>
I have the Connection String inside appsettings.json and setup the ApplicationDbContext inside Startup.ConfigureServices method.
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
 
    services.AddDbContext<ApplicationDbContext>(options =>
    {
        options.UseSqlServer(Configuration.GetConnectionString("ApplicationDbContext"));
    });
}
So basically that's it. Using the Package Manager Console in Visual Studio, I can run Add-Migration command targetting WebApplication.Data project.
Package Manager Console
Or alternatively using dotnet ef global tool (you need to install the tool first). The parameters are self-explanatory.
 dotnet ef migrations add Initial --project .\WebApplication1.Data\WebApplication1.Data.csproj --startup-project .\WebApplication1.Api\WebApplication1.Api.csproj                                                
And it will add all the Migrations inside WebApplication.Data project which is what I wanted.
Migrations
To apply the migrations,  again using the Package Manager Console in Visual Studio, I can run Update-Database command targetting WebApplication.Data. Or using the CLI,
dotnet ef database update --project .\WebApplication1.Data\WebApplication1.Data.csproj --startup-project .\WebApplication1.Api\WebApplication1.Api.csproj
That was easy.

Happy Coding.

Regards,
Jaliya

Wednesday, March 18, 2020

dotnet ef Command Doesn't Work inside a Docker Container

I was facing this issue where I needed to run dotnet ef command inside a docker container and it's not recognizing the command.

Basically, my Dockerfile looked something like.
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
RUN dotnet tool install -g dotnet-ef --version 3.1.1 
RUN export PATH="$PATH:/root/.dotnet/tools"
RUN dotnet ef
I was installing dotnet-ef command, and I was setting the path. But still, I was getting this error,
Could not execute because the specified command or file was not found.
Possible reasons for this include:
  * You misspelled a built-in dotnet command.
  * You intended to execute a .NET Core program, but dotnet-ef does not exist.
  * You intended to run a global tool, but a dotnet-prefixed executable with this name could not be found on the PATH.
I have been trying different things for a couple of hours, finally, thanks to Petr Onderka with his brilliant answer managed to solve the issue. The reason is for it's to not work is the environment variables set by export don't survive across directives. What we need to do is to use the ENV directive instead.

And this worked.
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
RUN dotnet tool install -g dotnet-ef --version 3.1.1 
ENV PATH $PATH:/root/.dotnet/tools
RUN dotnet ef --version
Thanks Petr Onderka again.

I hope this saves someone's time.

Happy Coding.

Regards,
Jaliya

Wednesday, March 4, 2020

Visual C# Technical Guru - January 2020

Another month as a judge in Microsoft TechNet Guru Awards under Visual C# category. The TechNet Guru Awards celebrate the technical articles on Microsoft TechNet. 

Visual C# Technical Guru - January 2020
Happy Coding. 

Regards,
Jaliya

Saturday, February 29, 2020

ASP.NET Core Application Reading Configuration from Azure App Configuration

In this post let's see how we can setup configuration settings for ASP.NET Core Application to read from Azure App Configuration.

Azure App Configuration is a managed service by Azure that provides a way to centrally manage application settings and feature flags. Nowadays a single application consists of multiple components running in different places, so the idea behind Azure App Configuration is having a single place to manage all your configuration settings.

It's always better to go by demonstration. Let's start by creating an Azure App Configuration service in Azure.
App Configuration
I am clicking on create.
App Configuration Setup
We just need to enter the basic information and I am completing the creation with the above settings.

Once that is created, we can create the settings by navigating into Settings -> Configuration Explorer.
Configuration explorer
Configuration settings here are basically a Key-Value pair. Before adding any values, let's create an ASP.NET Core Application and configure it to read settings from here. For the demo purpose, I will just create an API application named AzureAppConfigurationDemo and I am adding the NuGet package Microsoft.Azure.AppConfiguration.AspNetCore which will enable adding Microsoft Azure App Configuration as a configuration source in our application.

Now I am updating the Program.cs to configure the Azure App Configuration we created above to be used in our application.
public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }
 
    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder
                    .ConfigureAppConfiguration((hostingContextconfig) =>
                    {
                        var settings = config.Build();
                        config.AddAzureAppConfiguration(settings["ConnectionStrings:AppConfig"]);
                    })
                    .UseStartup<Startup>();
            });
}
Here, under the ConfigureAppConfiguration, we can add the AzureApp Configuration with a target connection string. You can find the connection string under Settings -> Access Keys inside the Azure App Configuration.
Access Keys
Now I am adding the secret named ConnectionStrings:AppConfig to Secret Manager. We can do this by either running dotnet user-secrets set command or if you are using Visual Studio by right-clicking on the project and Manage User Secrets.
dotnet user-secrets set ConnectionStrings:AppConfig "<ConnectionString>"
The secret Manager is used only to test things locally. When the app is deployed to Azure App Service, for example, we can use the Connection Strings application setting in App Service.

In my application, I have below dummy controller, which will return the value of a key named "AzureAppConfigurationDemo:TestKey" inside the configuration settings
[ApiController]
[Route("[controller]")]
public class SettingsController : ControllerBase
{
    private IConfiguration Configuration { get; }
 
    public SettingsController(IConfiguration configuration)
    {
        Configuration = configuration;
    }
 
    public IActionResult GetValue()
    {
        string value = Configuration["AzureAppConfigurationDemo:TestKey"];
        return Ok(value);
    }
}
Now let's create the key: "AzureAppConfigurationDemo:TestKey" by going back to Settings -> Configuration Explorer in our Azure App Configuration.
Add Key
I am leaving Label and Content Type empty for now and I am clicking on Apply.

Now we are all set. Let's run the application.
Result
And here it is. Hope this helps.

Happy Coding.

Regards,
Jaliya