Wednesday, September 20, 2023

.NET 8.0: Additional JsonNode Functionality

.NET 8 offers some exciting updates to System.Text.Json.

One of my favorites of them is, now we have some handy additional functionalities on JsonNode. The JsonNode has following new methods:

namespace System.Text.Json.Nodes;

public partial class JsonNode
{
    // Creates a deep clone of the current node and all its descendants.
    public JsonNode DeepClone();

    // Returns true if the two nodes are equivalent JSON representations.
    public static bool DeepEquals(JsonNode? node1, JsonNode? node2);

    // Determines the JsonValueKind of the current node.
    public JsonValueKind GetValueKind(JsonSerializerOptions options = null);

    // If node is the value of a property in the parent object, returns its name.
    public string GetPropertyName();

    // If node is an element of a parent JsonArray, returns its index.
    public int GetElementIndex();

    // Replaces this instance with a new value, updating the parent node accordingly.
    public void ReplaceWith<T>(T value);
}

public partial class JsonArray
{
    // Returns an IEnumerable<T> view of the current array.
    public IEnumerable<T> GetValues<T>();
}

For example, GetValueKind can be a huge win. 

Consider the following example with .NET 8.

using System.Text.Json.Nodes;

JsonNode jsonNode = JsonNode.Parse("""
    {
        "name": "John Doe",
        "age": 42,
        "isMarried": true,
        "addresses":
        [
            { "street": "One Microsoft Way", "city": "Redmond" },
            { "street": "1st Street", "city": "New York" }
        ]
    }
    """
)!;

Console.WriteLine(jsonNode.GetValueKind());
//Object

Console.WriteLine(jsonNode["name"]!.GetValueKind());
//String

Console.WriteLine(jsonNode["age"]!.GetValueKind());
//Number

Console.WriteLine(jsonNode["isMarried"]!.GetValueKind());
//True

Console.WriteLine(jsonNode["addresses"]!.GetValueKind());
//Array

Imagine how much code we needed to write prior to .NET 8 to achieve the same.

Isn't this handy?

Read more:
   What’s new in System.Text.Json in .NET 8

Happy Coding.

Regards,
Jaliya

Tuesday, September 19, 2023

Visual Studio 2022: HTTP Files and Variables

In this post, let's get to know some different ways to manage variables within HTTP Files.

Let's consider the following simple API.
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

WebApplication app = builder.Build();

app.MapGet("/users/{userId}", (int userId) =>
{
    return userId;
});

app.Run();

1. Use Inline Variables


You can define the variables in the .http file itself as follows
@HostAddress = http://localhost:5179
@userId = 1

GET {{HostAddress}}/users/{{userId}}
Accept: application/json
In-line variable

2. Use an Environment File


You can define an environment file with the name httpenv.json. The file can be in the same folder as in the .http file or a folder above it. Visual Studio will look for that file in the folder where the HTTP file exists. If it’s not found, Visual Studio will look through the parent directories to find it. When a file is found named httpenv.json, Visual Studio will stop searching for the file. The nearest file to the HTTP file found will then be used.

Here, I am just going to add httpenv.json next to the .http file.
{
  "development": {
    "userId": 2
  },
  "test": {
    "userId": 3
  }
}
Now I am closing and opening up the .http file (in order for the environment file to be picked), removing @userId = 1 defined in the .http file. Now because of the httpenv.json, I can see the environments in a dropdown as follows.
Environments
Now based on the environment we are selecting, I will get different values.
Environment: development
Environment: test

3. Use a user-specific environment file


You can also add a user-specific environment file named httpenv.json.user, next to the httpenv.json file.
{
  "development": {
    "userId": 4
  }
}
And now when you execute the request, you can see the following output.
User-specific environment file
The order of precedence for the variables is below. Once a match is found that value will be used, and other sources ignored.
  1. Variable declared in the .http file
  2. Variable declared in the httpenv.json.user file
  3. Variable declared in the httpenv.json file
Hope this helps.

Happy Coding.

Regards,
Jaliya

Wednesday, September 13, 2023

.NET In-Process Azure Durable Functions: Preserve Stack Order When Passing Between Orchestrators, Activities etc

In this post let's see how we can preserve Stack<T> order when it's getting passed between Orchestrators/Activities in a .NET In-Process Azure Durable Functions.

As you already know, when using Orchestrators/Activities in an Azure Durable Function,  different types of data are being serialized and persisted. Durable Functions for .NET In-Process internally uses Json.NET to serialize orchestration and entity data to JSON.

And when you pass in a Stack<T> to an Orchestrator or an Activity for an example, the order of items will not be preserved when it's serialized. It's actually an issue with Json.NET.

We can reproduce the issue simply by creating a simple .NET In-Process Azure Durable Function, something like below.

public static class Function1
{
    [FunctionName("HttpStart")]
    public static async Task<HttpResponseMessage> HttpStart(
        [HttpTrigger(AuthorizationLevel.Anonymous, "get""post")] HttpRequestMessage req,
        [DurableClient] IDurableOrchestrationClient starter,
        ILogger log)
    {
        string instanceId = await starter.StartNewAsync("Function1");

        log.LogInformation("Started orchestration with ID = '{instanceId}'.", instanceId);

        return await starter.WaitForCompletionOrCreateCheckStatusResponseAsync(req, instanceId, TimeSpan.FromSeconds(30));
    }

    [FunctionName("Function1")]
    public static async Task<Stack<string>> RunOrchestrator1([OrchestrationTrigger] IDurableOrchestrationContext context)
    {
        Stack<stringtransitions = new();

        transitions.Push("T0");
        transitions.Push("T1");
        transitions.Push("T2");

        return await context.CallSubOrchestratorAsync<Stack<string>>("Function2", transitions);
    }

    [FunctionName("Function2")]
    public static async Task<Stack<string>> RunOrchestrator2([OrchestrationTrigger] IDurableOrchestrationContext context)
    {
        Stack<stringtransitions = context.GetInput<Stack<string>>();

        transitions.Push("T3");
        transitions.Push("T4");
        transitions.Push("T5");

        return await Task.FromResult(transitions);
    }
}

The expected output should be as follows.

[
    "T5",
    "T4",
    "T3",
    "T2",
    "T1",
    "T0"
]
But it comes as follows.
Incorrect Result
The order can be so messed up when you have complex logic like when having recursive function calls.

This can be addressed by applying a custom serialization to the Durable Function.

First, we need to create a custom JsonConverter to serialize/deserialize Stack<T> correctly preserving the order.

/// <summary>
/// Converter for any Stack<T> that prevents Json.NET from reversing its order when deserializing.
/// https://github.com/JamesNK/Newtonsoft.Json/issues/971
/// https://stackoverflow.com/a/39481981/4865541
/// </summary>
public class StackConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return StackParameterType(objectType) != null;
    }

    public override object ReadJson(JsonReader reader, Type objectTypeobject existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
        {
            return null;
        }

        try
        {
            Type parameterType = StackParameterType(objectType);
            MethodInfo method = GetType().GetMethod(nameof(ReadJsonGeneric), BindingFlags.NonPublic | BindingFlags.Static);

            MethodInfo genericMethod = method.MakeGenericMethod(new[] { parameterType });
            return genericMethod.Invoke(thisnew object[] { reader, objectType, existingValue, serializer });
        }
        catch (TargetInvocationException ex)
        {
            // Wrap the TargetInvocationException in a JsonSerializerException
            throw new JsonSerializationException("Failed to deserialize " + objectType, ex);
        }
    }

    public override bool CanWrite { get { return false; } }

    public override void WriteJson(JsonWriter writerobject value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    private static Type StackParameterType(Type objectType)
    {
        while (objectType != null)
        {
            if (objectType.IsGenericType)
            {
                Type genericType = objectType.GetGenericTypeDefinition();
                if (genericType == typeof(Stack<>))
                {
                    return objectType.GetGenericArguments()[0];
                }
            }
            objectType = objectType.BaseType;
        }

        return null;
    }

    private static object ReadJsonGeneric<T>(JsonReader reader, Type objectTypeobject existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
        {
            return null;
        }

        List<T> list = serializer.Deserialize<List<T>>(reader);
        Stack<T> stack = existingValue as Stack<T> ?? (Stack<T>)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
        for (int i = list.Count - 1; i >= 0; i--)
        {
            stack.Push(list[i]);
        }

        return stack;
    }
}
Then we can override the default Json.NET serialization settings of the Durable Function using custom implementations of the IMessageSerializerSettingsFactory as follows.
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Azure.WebJobs.Extensions.DurableTask;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Reflection;

[assembly: FunctionsStartup(typeof(DurableFunctions.StackSerialization.Startup))]
namespace DurableFunctions.StackSerialization;

public class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        builder.Services.AddSingleton<IMessageSerializerSettingsFactory, CustomMessageSerializerSettingsFactory>(); // ...
    }

    /// <summary>
    /// A factory that provides the serialization for all inputs and outputs for activities and
    /// orchestrations, as well as entity state.
    /// </summary>
    internal class CustomMessageSerializerSettingsFactory : IMessageSerializerSettingsFactory
    {
        public JsonSerializerSettings CreateJsonSerializerSettings()
        {
            return new JsonSerializerSettings
            {
                TypeNameHandling = TypeNameHandling.None,
                DateParseHandling = DateParseHandling.None,
                Converters = new List<JsonConverter>
                {
                    new StackConverter()
                }
            };
        }
    }
}
And now if we invoke the function, we can see the correct output.
Correct Result
Sample code:


Hope this helps.

Happy Coding.

Regards,
Jaliya

Wednesday, August 23, 2023

EF Core 8.0 Preview: Raw SQL Queries for Unmapped Types

This is one of my favorite features in EF Core 8.0 (or EF 8). Actually, it's likely this is my all-time favorite in EF Core and I have been waiting so long for this one. 

With EF Core 8.0, now you can write SQL queries that return unmapped types. With EF Core 7.0, we are able to query ONLY scalar (non-entity) types.

Let's have a look at this feature with an example code.

Consider the following Entities and the DbContext.

public class MyDbContext : DbContext
{
    public DbSet<Customer> Customers { getset; }

    public DbSet<Order> Orders { getset; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder
            .UseSqlServer(@"<ConnectionString>");;
    }
}

public class Customer(string name)
{
    public int Id { getinit; }

    public string Name { getinit; } = name;
}

public class Order
{
    public int Id { getinit; }

    public Customer Customer { getset; }

    public decimal Amount { getset; }
}

Now let's get some data seeded.

using var context = new MyDbContext();
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();

Customer customer = new("John Doe");
await context.Customers.AddAsync(customer);

List<Order> orders = new()
{
    new() { Customer = customer, Amount = 200 },
    new() { Customer = customer, Amount = 300 }
};
await context.Orders.AddRangeAsync(orders);

await context.SaveChangesAsync();

With EF Core 7.0, we were able to do something like below.

IQueryable<intorderIds = context.Database
    .SqlQuery<int>($"""
        SELECT
            Id
        FROM Orders
    """
);

foreach (int orderId in orderIds)
{
    Console.WriteLine(orderId);
}

decimal sumOfAmounts = context.Database
    .SqlQuery<decimal>($"""
        SELECT 
            SUM(Amount) AS Value
        FROM Orders
    """
)
    .Single();

Console.WriteLine(sumOfAmounts);

Note: Here in RelationalDatabaseFacadeExtensions.SqlQuery<TResult> Method<TResult> needs to be a scalar type. 

Now let's move to EF Core 8.0. With EF Core 8.0, we no longer have the restriction where <TResult> needs to be a scalar type. 

I am declaring a Custom DTO, something like the one below.

public record OrderDto(int Idstring CustomerNamedecimal Amount);

And now I can write a query to map data to the above type.

IQueryable<OrderDto> orderDetails = context.Database
    .SqlQuery<OrderDto>($"""
        SELECT
            o.Id,
            c.Name AS CustomerName,
            o.Amount
        FROM Orders o
        INNER JOIN Customers c ON o.CustomerId = c.Id
    """
);

foreach (OrderDto orderDto in orderDetails)
{
    Console.WriteLine(orderDto);
}

Output
Isn't it just great?


Hope this helps.

Happy Coding.

Regards,
Jaliya

Wednesday, August 16, 2023

ASP.NET Core 8.0 Preview: All New Identity Endpoints

In this post let's have a look at brand new Identity endpoints which is available with ASP.NET 8 Preview 7.

To get started, let's create a new ASP.NET Core Web API project targeting .NET 8. Now install the latest previews of the following packages.

<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.0-preview.7.23375.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.0-preview.7.23375.4" />

And update the Program.cs as follows.

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using System.Security.Claims;

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

// Add authentication
builder.Services
    .AddAuthentication()
    .AddBearerToken(IdentityConstants.BearerScheme);

// Add Authorization
builder.Services.AddAuthorizationBuilder();

// Add DbContext
builder.Services
    .AddDbContext<AppDbContext>(options => options.UseInMemoryDatabase("AppDb"));

// Map Identity System
// Specify the DbContext for the identity store
// Opt-in to use the new endpoints
builder.Services
    .AddIdentityCore<MyUser>()
    .AddEntityFrameworkStores<AppDbContext>()
    .AddApiEndpoints();

builder.Services
    .AddEndpointsApiExplorer()
    .AddSwaggerGen();

WebApplication app = builder.Build();

// Map Identity API Endpoints to Middleware
app.MapIdentityApi<MyUser>();

app
    .MapGet("/", (ClaimsPrincipal user) => $"Hello {user.Identity!.Name}")
    .RequireAuthorization();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.Run();

class MyUser : IdentityUser { }

class AppDbContext : IdentityDbContext<MyUser>
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
}

I have added inline comments, so it is easy to follow.

And now when I run the application, I can see all the Identity endpoints.

Swagger UI - Identity API
You can use the following .http file to test the main endpoints.

@url=https://localhost:7015
@user=user1
@password=P@ssw0rd!
@email=user1@test.com

# Register User
POST {{url}}/register
Content-Type: application/json

{
  "username": "{{user}}",
  "password": "{{password}}",
  "email": "{{email}}"
}

# Login
POST {{url}}/login
Content-Type: application/json

{
  "username": "{{user}}",
  "password": "{{password}}"
}

# Call secured endpoint
@token=<your token here>
GET {{url}}
Authorization: Bearer {{token}}

David Fowler (@davidfowl) has created a nice example that you can fork and try out.
   davidfowl/IdentityEndpointsSample

Hope this helps.

Happy Coding.

Regards,
Jaliya

Tuesday, August 15, 2023

ASP.NET Core 8.0 Preview: Improved Debugging Experience on HttpContext

I have just noticed this with ASP.NET 8.0 Preview, and it's fantastic.

I am going to debug and inspect HttpContext.

Following is when the project is targeting .NET 7.
HttpContext
HttpContext.Request
HttpContext.User
Now let's have a look at the same when the project is targeting .NET 8.
HttpContext
HttpContext.Request
HttpContext.User
Look how simplified it is. Now it's really easy to find important information without even expanding items.

One more reason to go for .NET 8.

Happy Coding.

Regards,
Jaliya

Monday, August 7, 2023

ASP.NET Core 8.0 Preview: All New AddBearerToken Extensions

With ASP.NET Core 8.0 Preview, we now have some new AddBearerToken extension methods on Microsoft.AspNetCore.Authentication.AuthenticationBuilder. It seems to have been introduced as part of ASP.NET Core 8.0 Preview 4: Microsoft.Extensions.DependencyInjection.BearerTokenExtensions)

 Note: this is for Bearer Tokens and not JSON Web Tokens (JWT).

Let's go by an example. Make sure your project's Target Framework is .NET 8.0.
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <InvariantGlobalization>true</InvariantGlobalization>
  </PropertyGroup>

</Project>
I can now add bearer token authentication as follows.
using Microsoft.AspNetCore.Authentication.BearerToken;
using System.Security.Claims;

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddAuthentication()
    .AddBearerToken();
builder.Services.AddAuthorization();

WebApplication app = builder.Build();

app.UseHttpsRedirection();

app
    .MapPost("/login", (Dictionary<stringstringkeyValuePairs) =>
    {
        IEnumerable<Claim> claims = keyValuePairs.Select(x => new Claim(x.Key, x.Value));
        ClaimsPrincipal claimsPrinciple = 
            new(new ClaimsIdentity(claims, BearerTokenDefaults.AuthenticationScheme));

        return Results.SignIn(claimsPrinciple);
    });

app
    .MapGet("/", (ClaimsPrincipal claimsPrincipal) =>
    {
        return Results.Ok(claimsPrincipal.Claims.Select(x => new { x.Type, x.Value }));
    })
    .RequireAuthorization();

app.Run();
Note the AddBearerToken() on AuthenticationBuilder.

I can try it out using the following .http file.
@hostname=localhost
@port=7285
@host={{hostname}}:{{port}}

POST https://{{host}}/login
Content-Type: application/json

{
    "name": "John",
    "role": "administrator"
}

# Replace the token below with the access_token returned from the previous request
GET https://{{host}}
Authorization: Bearer CfDJ8HM03LdIurJAspnEgIvCgGvRg4CERoTTGFrsX-2P_XJaCwdzo8bH6DoKZgL51KM_W8Qr1iB8U3XKatYAVMLubrqXJmkPWOATrGudmGEcGINZwl04m1Eue6U-fyYTevKGZG-dSyKPmtaYBbHqSiknhLe07VlUTFgLHDGw1Yd6m5A4N4KFZkj9fB7Ciyfn3YoBanEyXQTqGxOntz_hnVazocL6xONaIThfGmHx3kgLMjG72Vfte8cp0cV89u1cXzkTTapPz2k9yXuCBjgO-Oks49OVRCTETQcdd5vIyV1xJTkKWyGaQOxkImtwDoUqPE1rwA
.http
Isn't it handy? 

Hope this helps.

Happy Coding.

Regards,
Jaliya

Monday, July 24, 2023

Installing SQL Server Express LocalDB in a GitHub Workflow

In this post, let's see how you can install SQL Server Express LocalDB in GitHub Workflows. This is useful when you want to run integration tests in a Workflow. 

Technically you can use this approach anywhere as long as the Agent is Windows, as this is just a set of PowerShell commands.

name: Build and deploy

on:
  push:
    branches:
      - main
  workflow_dispatch:

jobs:
  build:
    runs-on: windows-latest

    steps:
      - uses: actions/checkout@v2

      - ...

      - name: Install MSSQLLocalDB
        run: |
            Import-Module BitsTransfer
            Start-BitsTransfer `
               -Source https://download.microsoft.com/download/3/8/d/38de7036-2433-4207-8eae-06e247e17b25/SqlLocalDB.msi `
               -Destination SqlLocalDB.msi
            Start-Process `
               -FilePath "SqlLocalDB.msi" `
               -ArgumentList "/qn", "/norestart", "/l*v SqlLocalDBInstall.log", "IACCEPTSQLLOCALDBLICENSETERMS=YES"; `
               -Wait
            sqlcmd -l 60 -S "(LocalDb)\MSSQLLocalDB" -Q "SELECT @@VERSION;"
First, we are importing the BITS (Background Intelligent Transfer Management) module, and downloading the  SqlLocalDB.msi. Then we are doing a silent install and the last command is to test the connectivity to the instance.

The specified link for SqlLocalDB.msi is for SQL Server 2022. If you want to use SQL Server 2019, you can this link: https://download.microsoft.com/download/7/c/1/7c14e92e-bdcb-4f89-b7cf-93543e7112d1/SqlLocalDB.msi

Hope this helps.

Happy Coding.

Regards,
Jaliya

Monday, July 17, 2023

C# 12.0 Preview: Access Instance Members within nameof

In this post, let's go through a new feature that's coming with C# 12.0. With this feature, you can access instance members within nameof.

To try this feature out, make sure you have the latest version of .NET SDK (>= 8.0.0-preview.6) installed and the <LangVersion> is set to preview.
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    ...
    <LangVersion>preview</LangVersion>
  </PropertyGroup>

</Project>
Let's consider the following example.
using System.ComponentModel;

Console.WriteLine(nameof(NameOfHelper.StaticInteger.MinValue));

internal class NameOfHelper
{
    public string MyString { get; } = "SomeValue";

    public static int StaticInteger;
    public string NameOfLength { get; } = nameof(MyString.Length);

    [Description($"Returns {nameof(MyString.Length)} of given String")]
    public int StringLength(string s) => s.Length;
}
With the above code, if we use a C# <LangVersion> prior to 12.0, we will get compile errors when using these nameof expressions. Because we are using nameof to access instance members.

But with C# 12.0, it would build just build fine.
nameof()
Check out more C# 12 features:

Happy Coding.

Regards,
Jaliya

Friday, July 14, 2023

Azure PowerShell: Check Directory Exists in Azure File Share

In this post, let's see how to check whether a Directory exists in Azure File Share using Azure PowerShell.

I am assuming you have connected to Azure with an authenticated account and have chosen the subscription in which your storage account resides by running the following commands.
Connect-AzAccount
Set-AzContext -Subscription "<SubscriptionId>"
Now first let's get a reference to the File Share.
$ResourceGroup = "<ResourceGroup>"
$StorageAccountName = "<StorageAccountName>"
$StorageFileShareName = "<StorageFileShareName>"

# Get Storage Account Key
$storageAccountKey = (Get-AzStorageAccountKey `
    -ResourceGroupName $ResourceGroup `
    -AccountName $StorageAccountName).Value[0]

# Set AzStorageContext
$storageContext = New-AzStorageContext `
    -StorageAccountName $StorageAccountName `
    -StorageAccountKey $storageAccountKey

# Get the FileShare
$storageFileShare = Get-AzStorageShare `
    -Name $StorageFileShareName `
    -Context $storageContext
Here Get-AzStorageShare cmdlet returns an AzureStorageFileShare. On that, we have this nice ShareClient property. And we can use that for basically all types of operations. In this case, we need to check whether a Directory exists, so we can do something like below.
if ($storageFileShare.ShareClient.GetRootDirectoryClient().GetSubdirectoryClient("parent-directory").Exists().Value)
{
    # Directory Exists
} else {
    # Directory does not exist
}
Here I am getting to the root of the File Share, getting to the directory of my choice, and checking whether it exists. You can even pass nested paths for the directoryName, something like "parent-directory/child-directory".

This is really neat.

Hope this helps.

Happy Coding.

Regards,
Jaliya