Wednesday, July 6, 2022

.NET 7 Preview 5: Using [AsParameters] Attribute for Parameter Binding in Minimal APIs

In this post let's go through this new attribute [AsParameters] that got introduced in .NET 7 Preview 5. This attribute is especially for Minimal APIs in ASP.NET Core.

Let's consider the following endpoint using Minimal APIs.

app.MapGet("ToDos"async (ToDoDbContext dbContextint offsetint limit, ILogger<Program> logger) =>
{
    logger.LogInformation("Getting ToDos with Offset: '{Offset}', Limit: '{Limit}'",
        offset,
        limit);
 
    List<ToDo> todos = await dbContext.ToDos
        .OrderBy(x => x.Id)
        .Skip(offset)
        .Take(limit)
        .ToListAsync();
 
    return TypedResults.Ok(todos);
});

With the new [AsParameters] attribute, we can move all the parameters into a POCO type.

app.MapGet("ToDos/WithRequest"async ([AsParameters] GetRequest request) =>
{
    request.Logger.LogInformation("Getting ToDos with Offset: '{Offset}', Limit: '{Limit}'",
        request.Offset,
        request.Limit);
 
    List<ToDo> todos = await request.DbContext.ToDos
        .OrderBy(x => x.Id)
        .Skip(request.Offset)
        .Take(request.Limit)
        .ToListAsync();
 
    return TypedResults.Ok(todos);
});
 
record struct GetRequest(ToDoDbContext DbContext    int Offset    int Limit, 
    ILogger<GetRequest> Logger);

Here I have used a record struct, but classes are also supported. The recommendation is to use record struct to avoid additional memory allocation.

And on top of that, we can apply other binding attributes (FromHeaderFromQuery, FromServices, etc.), something like below.

record struct GetRequest(ToDoDbContext DbContext,
    int Offset,
    int Limit,
    [FromHeader(Name = "X-OrganizationId")] string? OrganizationId,
    ILogger<GetRequest> Logger);

Here the OrganizationId will get picked from HTTP Request Header (if present). 

The following rules are applied during the parameter binding.

Structs

  • A declared public parameterless constructor will always be used if present.
  • A public parameterized constructor will be use if a single constructor is present and all arguments have a matching (case-insensitive) public property.
    • If a constructor parameter does not match with a property, InvalidOperationException will be thrown if binding is attempted.
  • Since struct always has a default constructor, the default constructor will be used if it is the only one present or more than one parameterized constructor is present.
  • When binding using a parameterless constructor all public settable properties will be bound.
Classes
  • A public parameterless constructor will be used if present.
  • A public parameterized constructor will be used if a single constructor is present and all arguments have a matching (case-insensitive) public property.
    • If a constructor parameter does not match with a property, InvalidOperationException will be thrown if binding is attempted.
  • Throw InvalidOperationException when more than one parameter is declared and the parameterless constructor is not present.
  • Throw InvalidOperationException if a suitable constructor cannot be found.

Hope this helps.

Happy Coding.

Regards,
Jaliya

Tuesday, July 5, 2022

Received Microsoft MVP Award in Developer Technologies

I am humbled and honored once again to receive the precious Microsoft Most Valuable Professional (MVP) Award for the ninth consecutive year.

This year, the announcement of new MVPs took a bit longer (usually it's on 07/01), but finally got the email.

As always looking forward to another great year on top of Microsoft Development Stack.
Microsoft Most Valuable Professional (MVP)
Thank you Microsoft for your appreciation and Thank you everyone for your continuous support.

Happy Coding.

Regards,
Jaliya

Tuesday, June 28, 2022

Session: Improve Developer Productivity and Code Quality with GitHub at the First Ever Microsoft Reactor Pop-Up, New Zealand

First-Ever Microsoft Reactor Pop-Up, New Zealand started today and it's a three-day event. 

Microsoft Reactor Pop-Up, New Zealand
Microsoft Reactor Pop-Up, New Zealand
Day 1 was targetting on Application Innovation with Azure, Day 2 on Innovating with Data and AI and the Day 3 is all about the new Azure NZ North region.

I got the opportunity to deliver a session on the first day and the session was about Improving Developer Productivity and Code Quality with GitHub. There were also some amazing sessions delivered by Microsoft FTEs and MVPs.

There in the session, I went through demoing GitHub Copilot using JavaScript, Python and C# examples. Then went through GitHub Advanced Security Features which are Code Scanning, Secret Scanning, Dependency Review, and Security Overview.

It was fun!

If you missed today, you can still join for Day 2 and Day 3 to learn from some amazing list of speakers.

Happy Coding.

Regards,
Jaliya

Thursday, June 16, 2022

.NET 7 Preview 5: Improved Polymorphic Serialization/Deserialization Support in System.Text.Json

.NET 7 Preview 5 is out and we now have omproved Polymorphic Serialization/Deserialization Support in System.Text.Json.

Consider the following classes.
public class Person
{
    public string Name { getset; }
}
 
public class Student : Person
{
    public int StudentId { getset; }
}
 
public class Employee : Person
{
    public int EmployeeId { getset; }
}
Now if I do something like the below, you can see it only serialized the properties that are in the Parent class. (wrote a post about this a couple of months back: System.Text.Json.JsonSerializer: Serialize Properties of Derived Classes).
JsonSerializerOptions options = new() { WriteIndented = true };
 
Person person = new Student
{
    Name = "John Doe",
    StudentId = 1
};

string jsonString = JsonSerializer.Serialize<Person>(person, options);
Console.WriteLine(jsonString);
 
//{
//  "Name": "John Doe"
//}
We can change this behavior now using JsonDerivedType attribute annotations.
[JsonDerivedType(typeof(Student))]
[JsonDerivedType(typeof(Employee))]
public class Person
{
    public string Name { getset; }
}
This configuration enables polymorphic serialization for Person, specifically when the runtime type is one of the derived classes.

Now if we do something like the below, we can see correct runtime types are getting serialized.
JsonSerializerOptions options = new() { WriteIndented = true };
 
Person person = new Student
{
    Name = "John Doe",
    StudentId = 1
};
string jsonString = JsonSerializer.Serialize<Person>(person, options);
Console.WriteLine(jsonString);
 
//{
//  "StudentId": 1,
//  "Name": "John Doe"
//}
 
person = new Employee
{
    Name = "Jane Doe",
    EmployeeId = 1
};
 
jsonString = JsonSerializer.Serialize<Person>(person, options);
Console.WriteLine(jsonString);
 
//{
//  "EmployeeId": 1,
//  "Name": "Jane Doe"
//}
If I try to serialize a derived type that's not annotated in the base class, I am going to see an exception like the one below.
System.NotSupportedException: Runtime type 'Teacher' is not supported by polymorphic type 'Person'
Now when deserializing, it would be as follows.
string jsonString = """
{
  "StudentId": 1,
  "Name": "John Doe"
}
""";
 
Person person = JsonSerializer.Deserialize<Person>(jsonString);
Console.WriteLine(person is Student); // false, polymorphic deserialization doesn't work
Here note that this does not enable polymorphic deserialization. 

In order to enable polymorphic deserialization, we need to specify a type discriminator.
[JsonDerivedType(typeof(Student), typeDiscriminator: "student")]
public class Person
{
    public string Name { getset; }
}
Now when we serialize, the JSON will include the type discriminator.
Person person = new Student
{
    Name = "John Doe",
    StudentId = 1
};
string jsonString = JsonSerializer.Serialize<Person>(person, options);
Console.WriteLine(jsonString);
 
//{
//  "$type": "student",
//  "StudentId": 1,
//  "Name": "John Doe"
//}
Now when deserializing, we can see polymorphic deserialization works.
string jsonString = """
{
  "$type": "student",
  "StudentId": 1,
  "Name": "John Doe"
}
""";
 
Person person = JsonSerializer.Deserialize<Person>(jsonString);
Console.WriteLine(person is Student); // true, polymorphic deserialization works
Hope this helps.

Happy Coding.

Regards,
Jaliya

Wednesday, June 15, 2022

Azure Functions (In Process): Output Binding to a Dynamic Queue/Topic

In this post let's see how we can set up an Output Binding in an In Process Azure Function to send messages to a dynamic target Azure Service Bus Queue or Topic.

We usually use ICollector or IAsyncCollector to write values to an output binding. And often times, the target queue/topic is set up through attributes. Something like below,

[FunctionName(FunctionNames.SomeFunction)]
public async Task Run(
    [ServiceBusTrigger("%ServiceBus_ListeningQueue%", Connection = "ServiceBus_ConnectionString")] 
        ServiceBusReceivedMessage serviceBusReceivedMessage,
    [ServiceBus("%ServiceBus_TargetQueue%", Connection = "ServiceBus_ConnectionString")] 
        IAsyncCollector<MyOutputMessage> asyncCollector)
{
    // TODO: Some code

    await asyncCollector.AddAsync(new MyOutputMessage { });
}

The above function is listening to an Azure Service Bus Queue (ServiceBus_ListeningQueue) and outputting a message to another Queue (ServiceBus_TargetQueue). Here the target Queue is static and cannot be changed. 

What if we want to route the message to a target Queue or Topic based on a property of the incoming message (most of the time based on ServiceBusReceivedMessage.ReplyTo Property).

In these cases, we can use Azure Functions imperative binding instead of declarative binding (which is done above).

[FunctionName(FunctionNames.SomeFunction)]
public async Task Run(
    [ServiceBusTrigger("%ServiceBus_ListeningQueue%", Connection = "ServiceBus_ConnectionString")]
        ServiceBusReceivedMessage serviceBusReceivedMessage,
    IBinder binder)
{
    // TODO: Some code
 
    IAsyncCollector<MyOutputMessage> asyncCollector =
        await binder.BindAsync<IAsyncCollector<MyOutputMessage>>(new ServiceBusAttribute(serviceBusReceivedMessage.ReplyTo)
        {
            // Note: This should be the App Setting Name that contains the Service Bus connection string
            Connection = "ServiceBus_ConnectionString",
        });
 
    await asyncCollector.AddAsync(new MyOutputMessage { });
}

And that's it. You can change the EntityType property of ServiceBusAttribute to ServiceBusEntityType.Topic if you want to output into a Topic (default is Queue).

Hope this helps.

Happy Coding.

Regards,
Jaliya

Wednesday, June 8, 2022

Update Azure APIM API from an OpenAPI Definition using az apim Command Group

This is a quick post on how you can update Azure APIM API from an OpenAPI definition using az apim command group. 

Note: As of the day I am writing this post, apim Command group is experimental and under development.

The command we need to use is az apim api import.

$resourceGroup = "<Resource Group>"
$serviceName = "<APIM Name>"
$apiId = "<API Name>"
$path = "/<API URL suffix>"
$specificationUrl = "https://.../swagger.json"
 
az apim api import `
    --resource-group $resourceGroup `
    --service-name $serviceName `
    --api-id $apiId `
    --path $path `
    --specification-url $specificationUrl `
    --specification-format OpenApi

This will update the existing API while preserving existing policies that you may have on your API.

There is also az apim api update command and that is to be used if you want to update the properties of the API.

This approach can be used if you are facing 504 Gateway Timeout errors when updating APIs that contain a large schema using Azure Resource Manager (ARM) APIs and you are on a Developer Pricing Tier APIM: Azure API Management with Developer Pricing Tier: 504 Gateway Timeout.

Hope this helps.

Happy Coding.

Regards.
Jaliya

Monday, June 6, 2022

Playing Around with GitHub Copilot

I have been spending some quality time with GitHub Copilot and so far it has made me fall out of my chair a couple of times. It truly is amazing. Thought of writing this post to share some experience, so whoever hasn't tried it so far, can get amazed like me.

GitHub Copilot is a Pair Programmer, which does way more than auto-completion and it's completely powered with AI (Artificial Intelligence). I have been using Visual Studio for a long time, and probably since the day I started, we had IntelliSense which does the basic auto-completion. Then a couple of years back, Microsoft announced IntelliCode which is an AI-Assisted tool to suggest the next piece of code based on where we are, and it was great.

Now GitHub Copilot places things on a whole different level. It basically can write code based on the comments in Natural Language, can you believe that? So whether it's a comment, docstring, function name, or the code itself, GitHub Copilot uses those to synthesize code. Currently, it's still in its Technical Preview stage, and has first-class support for the following programming languages.

  • Python
  • JavaScript
  • TypeScript
  • Ruby
  • Java
  • Go

Same time it understands dozens of other languages and can help you find your way around almost anything. I have tried with C#, T-SQL and it's working super nicely.

As of today, GitHub Copilot  is available as an extension for 

This is a simple express app, I have created using Visual Studio Code, I only wrote the comments, and the code was completely written by GitHub Copilot.
// Create an Express application
var express = require('express');
 
// Expose an endpoint
var app = express();
app.get('/'function (reqres) {
    res.send('Hello World!');
});
 
// Expose another endpoint
app.get('/api'function (reqres) {
    // Send a response of current time
    res.send(new Date());
});
 
// Run express application
app.listen(3000, function () {
    console.log('Listening on port ' + 3000);
});
I have also created a simple ASP.NET Core Minimal API with CRUD operations, again I only wrote the comments on natural language, and GitHub Copilot wrote all the new code for creating a class, the DbContext, and exposing CRUD endpoints and that's with the functionality.

So how does this work?

First, let's start with a bit of history. It all started with an AI Research Startup called OpenAI founded by Elon Musk and Sam Altman. In July 2019, Microsoft partnered up with OpenAI. In June 2020, OpenAI announced GPT-3, a language model trained on trillions of words from the Internet. In June 2021, OpenAI Codex was introduced, a model based on GPT-3 to translate natural language to code (and vice-versa).

GitHub Copilot is powered with OpenAI Codex models. 
How GitHub Copilot Works
OpenAI Codex was trained on publicly available source code (including code in public repositories on GitHub) and natural language, so it understands both programming and human languages. The GitHub Copilot editor extension sends our comments and code to the GitHub Copilot service, which then uses OpenAI Codex to synthesize and suggest individual lines and/or whole functions.

The whole GitHub Copilot experience is beyond words, you have to try it for yourself. Sign up with the Technical Program now, you will have to be on the waitlist for some time, but I would definitely say it's worth the wait, because once you have access, it's going to blow your minds off.

Hope this helps.

Happy Coding.

Regards,
Jaliya

Tuesday, May 31, 2022

Visual Studio 2022 17.3.0 Preview 1.1: Introducing Port Tunneling for ASP.NET Core Projects

Last week, it's Microsoft Build week, and hope you have enjoyed it.  

There were some nice announcements and one of my favorites out of them all is this nice feature that got released with Visual Studio 2022 17.3.0 Preview 1.1. That's introducing the private preview of port tunneling in Visual Studio for ASP.NET Core projects. 

With this, I can run my Web Application locally and the URL it's running is public and can be accessed from outside of my local environment. With most of us working from home, I am finding this very helpful. I can do things like, 

  • Share the public URL with a colleague to test out the application. 
  • If it's a Frontend Web Application,  access the URL from my mobile, and see how it's behaving.
  • I don't have to deploy the application to test a Webhook with a third party etc
In order to use this feature, there are a couple of things you need to do first.

The first thing is obviously you need to download and install the latest preview of Visual Studio 2022. And that's Visual Studio 2022 17.3.0 Preview 1.1.

Next, you need to sign up for the private preview program of port tunneling in Visual Studio. Otherwise, you are going to get an error like below when you are going to try it.
Missing sign-up for Port Tunneling program
You can do it by filling out the form here: https://aka.ms/tunnels-signup. Something to note here is, it's going to take some time for access to be granted into the private program and at this time individual users will not be considered, only organizations with tenant IDs.

After signing up with the preview program, log in to Visual Studio with the email address you have used. Then under Tools -> Options -> Environment -> Preview Features, check Enable port tunneling for Web Applications.
Enable port tunneling for Web Applications
Now you are almost set.

Finally, create a new ASP.NET Core Web Application, and once the project is created, update the launchSettings.json as below.

launchSettings.json
{
  "$schema""https://json.schemastore.org/launchsettings.json",
  "iisSettings": {
    "windowsAuthentication"false,
    "anonymousAuthentication"true,
    "iisExpress": {
      "applicationUrl""http://localhost:4367",
      "sslPort": 44305
    }
  },
  "profiles": {
    "WebApplication1": {
      "commandName""Project",
      "dotnetRunMessages"true,
      "launchBrowser"true,
      "launchUrl""swagger",
      "applicationUrl""https://localhost:7015;http://localhost:5015",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT""Development"
      },
      "createTunnel"true
    },
    "IIS Express": {
      "commandName""IISExpress",
      "launchBrowser"true,
      "launchUrl""swagger",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT""Development"
      }
    }
  }
}
The only change here I have done here is I have set a new property "createTunnel"true under the profile I am using to run the Application.

And that's it. Now you can launch the application.

Happy Coding.

Regards,
Jaliya

Thursday, May 12, 2022

.NET 7 Preview 4: Introducing Self-describing Support for Minimal APIs in ASP.NET Core

.NET 7 Preview 4 is released and it includes some nice features related to ASP.NET Core Minimal APIs. One of them is the support for self-describing API endpoints. 

In this post, let's have a look at how it works.

Consider the below Minimal API endpoints prior to .NET 7 Preview 4.

app.MapGet("/employees"async (EmployeeContext dbContext) =>
{
    return Results.Ok(await dbContext.Employees.ToListAsync());
});

Now if we have a look at the Swagger document, I can see something like this.

GET: /employees
It only says the endpoint returns 200, but nothing about the response type.

Let's have a look at another example. Consider the below endpoint.

app.MapGet("/employees/{id}"async (int id, EmployeeContext dbContext) =>
{
    Employee employee = await dbContext.Employees.FindAsync(id);
    if (employee is null)
    {
        return Results.NotFound();
    }
 
    return Results.Ok(employee);
});

And this would appear in the Swagger document as follows.

GET: /employees/{id}
Again nothing about the Response Type, and obviously no sign about the endpoint returning 404.
 
If we are to enrich these missing details, we will have to update the code as follows.

app
    .MapGet("/employees"async (EmployeeContext dbContext) =>
    {
        return Results.Ok(await dbContext.Employees.ToListAsync());
    })
    .Produces<List<Employee>>();
 
app
    .MapGet("/employees/{id}"async (int id, EmployeeContext dbContext) =>
    {
        Employee employee = await dbContext.Employees.FindAsync(id);
        if (employee is null)
        {
            return Results.NotFound();
        }
 
        return Results.Ok(employee);
    })
    .Produces<Employee>()
    .Produces(StatusCodes.Status404NotFound);

And now we can see the Swagger document is updated.

GET: /employees
GET: /employees/{id}
But what if we can let the APIs describe themselves without adding additional annotations.

With .NET 7 Preview 4, I can change the above endpoints as follows.

app.MapGet("/employees"async (EmployeeContext dbContext) =>
{
    return TypedResults.Ok(await dbContext.Employees.ToListAsync());
});

This will describe the endpoint the same way it did with annotations. 

The only change I did here is use the new TypedResults factory class instead of Results factory class when generating the result. The new TypedResults factory class will create Typed results (as the name suggests of course) instead of IResult as it did with Results factory class. And all these Typed results implement a new interface IEndpointMetadataProvider.

public interface IEndpointMetadataProvider
{
    static abstract void PopulateMetadata(EndpointMetadataContext context);
}

The framework will call PopulateMetadata() when the endpoint is built and that adds the necessary endpoint metadata to describe the HTTP response type.

Now when we have multiple return types, we need to explicitly specify the return types as follows.

app.MapGet("/employees/{id}"async Task<Results<Ok<Employee>, NotFound>> (int id, EmployeeContext dbContext) =>
{
    Employee employee = await dbContext.Employees.FindAsync(id);
    if (employee is null)
    {
        return TypedResults.NotFound();
    }
 
    return TypedResults.Ok(employee);
});

And this also will describe the endpoint the same way it did with annotations. 

You can find the complete sample code here.
   https://github.com/jaliyaudagedara/minimal-api

More Read
   ASP.NET Core updates in .NET 7 Preview 4

Hope this helps.

Happy Coding.

Regards,
Jaliya

Wednesday, May 11, 2022

CoreWCF Is Released

CoreWCF, the .NET Core version of Windows Communication Foundation is finally released. The 1.0 release of CoreWCF is compatible with .NET Standard 2.0 so that it will work with,
  • .NET Framework 4.6.2 (and above)
  • .NET Core 3.1
  • .NET 5+
In this post, let's have a look at a sample implementation of WCF on top of .NET 6.

I have an ASP.NET Core Web API (with Minimal API support) created and installed the following packages.
<ItemGroup>
  <PackageReference Include="CoreWCF.Http" Version="1.0.1" />
  <PackageReference Include="CoreWCF.Primitives" Version="1.0.1" />
</ItemGroup>
Then I created the following services. I am exposing two services as I want to show the support for different Bindings.

GreetService.cs
[ServiceContract]
public interface IGreetService
{
    [OperationContract]
    string Greet(string message);
}
 
public class GreetService : IGreetService
{
    public string Greet(string message)
    {
        return $"You said: {message}";
    }
}
AnotherGreetService.cs
[ServiceContract]
public interface IAnotherGreetService
{
    [OperationContract]
    string AnotherGreet(string message);
}
 
public class AnotherGreetService : IAnotherGreetService
{
    public string AnotherGreet(string message)
    {
        return $"You said another: {message}";
    }
}
Now I am modifying the Startup.cs as follows.

Startup.cs
using CoreWCF;
using CoreWCF.Configuration;
using CoreWCF.Description;
using CoreWcfDemo.Server.Services;
 
var builder = WebApplication.CreateBuilder(args);
 
// Add WSDL support
builder.Services.AddServiceModelServices().AddServiceModelMetadata();
builder.Services.AddSingleton<IServiceBehavior, UseRequestHeadersForMetadataAddressBehavior>();
 
WebApplication? app = builder.Build();
app.UseServiceModel(builder =>
{
    // This service only supports BasicHttpBinding
    builder
        .AddService<GreetService>()
        .AddServiceEndpoint<GreetService, IGreetService>(new BasicHttpBinding(), 
            "/GreetService/BasicHttp");
 
    // This service supports BasicHttpBinding and WSHttpBinding
    builder
        .AddService<AnotherGreetService>()
        .AddServiceEndpoint<AnotherGreetService, IAnotherGreetService>(new BasicHttpBinding(), 
            "/AnotherGreetService/BasicHttp")
        .AddServiceEndpoint<AnotherGreetService, IAnotherGreetService>(new WSHttpBinding(SecurityMode.Transport), 
            "/AnotherGreetService/WSHttps");
});
 
var serviceMetadataBehavior = app.Services.GetRequiredService<ServiceMetadataBehavior>();
 
serviceMetadataBehavior.HttpGetEnabled = true;
serviceMetadataBehavior.HttpsGetEnabled = true;
 
serviceMetadataBehavior.HttpGetUrl = new Uri("http://localhost:5051/metadata");
serviceMetadataBehavior.HttpsGetUrl = new Uri("https://localhost:7051/metadata");
 
app.Run();
Now, I am adding a Console Application and adding Service References to the project.
Add Service Reference
Add Service Reference: WCF Web Service
Discover Services
Once the Service is discovered, I have selected Next and opted to use the defaults. And once the Service Reference is created, I have the following code to call the different WCF Service methods using different bindings.
using ServiceReference1;
 
// BasicHttpsBinding
var greetServiceClient = new GreetServiceClient(
    GreetServiceClient.EndpointConfiguration.BasicHttpBinding_IGreetService,
    "http://localhost:5051/GreetService/BasicHttp"
);
var result = await greetServiceClient.GreetAsync("Hello");
Console.WriteLine(result);
 
// WSHttpBinding
var anotherGreetServiceClient = new AnotherGreetServiceClient(
    AnotherGreetServiceClient.EndpointConfiguration.WSHttpBinding_IAnotherGreetService,
    "https://localhost:7051/AnotherGreetService/WSHttps"
);
result = await anotherGreetServiceClient.AnotherGreetAsync("Hello");
Console.WriteLine(result);
 
Console.ReadLine();
And now when I run the Console App while the Server App is running, I can see everything is working as expected.

You can find the complete code sample here:
   https://github.com/jaliyaudagedara/corewcf-demo

Hope this helps.

Happy Coding.

Regards,
Jaliya