Wednesday, December 15, 2021

C# 10.0: Nice Little Features

In this post, let's see a couple of nice features that are available with C# 10.0, which are kind of tends to go unnoticed, but are pretty nice.

Constant Interpolated Strings

With C# 10.0, Interpolated strings can be declared as const. But in order to do that, all the interpolated strings need to be const.

const string Language = "C#";
const string Version = "10.0";
const string FullProductName = $"Language: {Language} Version: {Version}";

For the next features, let's consider the below types.

public enum PackageSize
{
    Small,
    Medium,
    Large
}
 
public record Package(PackageSize PackageSizedecimal Widthdecimal Heightdecimal Length);
 
public record Shipment(string Fromstring ToPackage Package);

Now say, we have a method to Calculate the Package Cost based on the PackageSize. And we are doing a null check on the parameter being passed in.

static decimal CalculatePackageCost(Shipment shipment)
{
    if (shipment == null)
    {
        throw new ArgumentNullException(nameof(shipment));
    }
     
    // TODO: Determine Package Cost
};    

ArgumentNullException.ThrowIfNull

With C# 10.0, Argument Null Check can be simplified as follows.

static decimal CalculatePackageCost(Shipment shipment)
{
    ArgumentNullException.ThrowIfNull(shipment, nameof(shipment));
             
    // TODO: Determine Package Cost
}   

That's neat!

Now say, we determine the Package Cost by doing something like below. This code is written prior to C# 10.0.

static decimal CalculatePackageCost(Shipment shipment)
{
    // other code
        
    return shipment switch
    {
        { Package: { PackageSize: PackageSize.Small } } => 100.00m,
        { Package: { PackageSize: PackageSize.Medium } } => 150.00m,
        { Package: { PackageSize: PackageSize.Large } } => 200.00m,
        _ => throw new NotImplementedException()
    };
};

Nested Property Patterns

With C# 10.0, you can simplify the code by using Nested Property Pattern.

static decimal CalculatePackageCost(Shipment shipment)
{
    // other code
    
    return shipment switch
    {
        { Package.PackageSize: PackageSize.Small } => 100.00m,
        { Package.PackageSize: PackageSize.Medium } => 150.00m,
        { Package.PackageSize: PackageSize.Large } => 200.00m,
        _ => throw new NotImplementedException()
    };
}

Hope this helps.

Happy Coding.

Regards,
Jaliya

Wednesday, December 8, 2021

Creating Dapr Enabled Azure Container Apps

Azure Container Apps has native support for Dapr. That means we don't have to deploy Dapr runtime along with our images and have very straightforward integration.

In this post, let's see how we can create Dapr enabled services in Azure Container Apps and do service invocations. If you are new to Azure Container Apps, make sure to read this post: Public Preview: Azure Container Apps, which will help you get up to speed.

I have created 3 Azure Container Apps for the following components.

  • A Web App
  • Customers API
  • Orders API

Web App is public-facing (ingress mode is external) and the two APIs are internal (ingress mode is internal). Web App is consuming Customers API and Orders API.

I can create Dapr enabled Container Apps by running the following script (replacing variables and for APIs ingress mode is internal).

az containerapp create `
   --name $WEB_APP_NAME `
   --resource-group $RESOURCE_GROUP `
   --environment $CONTAINERAPPS_ENVIRONMENT `
   --image mcr.microsoft.com/azuredocs/containerapps-helloworld:latest `
   --target-port 80 `
   --ingress 'external' `
   --min-replicas 1 `
   --max-replicas 1 `
   --enable-dapr `
   --dapr-app-port 80 `
   --dapr-app-id $WEB_APP_SERVICE_ID `
   --dapr-components ./dapr/components.yaml

And then, in the Web App, appsettings.json, I have the Service App IDs defined. These are what you use for --dapr-app-id argument.

{
  ...
  "Services": {
    "Customers": {
"ServiceId""customers-api"
}, "Orders": { "ServiceId""orders-api"
} } }

And finally I can easily consume APIs/Services from WebApp, something like below for an example.

using ContainerAppsDemo.Web.Blazor.Data;
using Dapr.Client;
    
namespace ContainerAppsDemo.Web.Blazor.Services;

public class CustomerService : ICustomerService
{
    private readonly DaprClient _daprClient;
    private readonly string _serviceId;
    
    public CustomerService(DaprClient daprClient, IConfiguration configuration)
    {
        _daprClient = daprClient;
        _serviceId = configuration.GetValue<string>("Services:Customers:ServiceId");     }     public async Task<IEnumerable<Customer>> GetCustomers(CancellationToken cancellationToken = default)     {         return await _daprClient.InvokeMethodAsync<List<Customer>>(HttpMethod.Get,
            _serviceId,
            "customers",
            cancellationToken);     } }
Dapr Enabled Container Apps

Isn't that easy?

You can find the source code for this post here,
   jaliyaudagedara/azure-container-apps-demo

Hope this helps.

Happy Coding.

Regards,
Jaliya

Thursday, December 2, 2021

.NET 6: DateOnly and TimeOnly Types

In this post, let's have a look at two new types that got added to .NET Base Class Library (core set) in .NET 6 and that is DateOnly and TimeOnly. These were added to System namespace just like other DateTime types.

Let's consider the following DateTime.

DateTime datetime = new(2021, 12, 15);
Console.WriteLine(datetime);                 // 15/12/2021 12:00:00 am

Since it's a DateTime, it has the Date component and a Time component which makes perfect sense. Now let's say you want to extract the Date component and the Time component separately. That's where the new DateOnly and TimeOnly types comes into the picture.

DateOnly dateonly = DateOnly.FromDateTime(datetime);
Console.WriteLine(dateonly);                 // 15/12/2021
 
TimeOnly timeonly = TimeOnly.FromDateTime(datetime);
Console.WriteLine(timeonly);                 // 12:00 am

You can construct DateOnly and TimeOnly as you construct DateTime. And you have all the different constructor overloads to create DateOnly and TimeOnly the way you want. For example,

DateOnly dateonly = new(2021, 12, 15);
Console.WriteLine(dateonly);                 // 15/12/2021
 
TimeOnly timeonly = new(13, 30);
Console.WriteLine(timeonly);                 // 1:30 pm

DateOnly and TimeOnly types have their own Properties and Methods that you can use. For example,

// Some of DateOnly Properties
Console.WriteLine(dateonly.Day);             // 15
Console.WriteLine(dateonly.DayOfWeek);       // Wednesday Console.WriteLine(dateonly.Month);           // 12 Console.WriteLine(dateonly.Year);            // 2021 // Some of TimeOnly Properties Console.WriteLine(timeonly.Hour);            // 13 Console.WriteLine(timeonly.Minute);          // 30 Console.WriteLine(timeonly.Second);          // 0 // Some of DateOnly Methods Console.WriteLine(dateonly.AddDays(30));     // 14/01/2022 Console.WriteLine(dateonly.AddYears(1));     // 15/12/2022 // Some of TimeOnly Methods Console.WriteLine(timeonly.AddHours(4));     // 5:30 pm Console.WriteLine(timeonly.AddMinutes(270)); // 6:00 pm

These two new types are going to be super useful, specially DateOnly. There are a lot of scenarios we don't care about the Time component, so it's going to be very handy.

Hope this helps.

Happy Coding.

Regards,
Jaliya