Tuesday, March 24, 2026

Azure Content Understanding Client Library for .NET

In this post, let's have a look at the new Azure Content Understanding Client Library for .NET.

Until now, when working with Azure Content Understanding, we had to use the Azure Content Understanding REST API directly. That meant building requests ourselves, handling responses manually, and taking care of the long-running operation flow on our own.

Now there is a proper NET client library, and that makes the experience much nicer. It follows the familiar Azure SDK for .NET patterns such as Response<T> and Operation<T>, so if you have worked with other Azure SDKs before, this will feel very natural.

As of today, this is the latest package and APIs will likely evolve over time. But even at this stage, the SDK already gives a much cleaner developer experience compared to calling the REST endpoints directly.

For the rest of this post, let's go through a simple example.

First step is installing the Azure.AI.ContentUnderstanding NuGet package.

dotnet add package Azure.AI.ContentUnderstanding
Then I have the following simple example.
using Azure;
using Azure.AI.ContentUnderstanding;
using Azure.Identity;
using System.Net.Mime;

string endpoint = "<Endpoint>";
string analyzerId = "<AnalyzerId>";
string filePath = @"<sample-file>.pdf";

BinaryData fileData = BinaryData.FromBytes(await File.ReadAllBytesAsync(filePath));
string contentType = Path.GetExtension(filePath).ToLowerInvariant() switch
{
    ".pdf" => MediaTypeNames.Application.Pdf,
    _ => throw new NotSupportedException($"File type {Path.GetExtension(filePath)} is not supported.")
};

ContentUnderstandingClient contentUnderstandingClient = new(new Uri(endpoint), new DefaultAzureCredential());

Operation<AnalysisResult> operation = await contentUnderstandingClient.AnalyzeBinaryAsync(
    WaitUntil.Started,
    analyzerId,
    fileData,
    contentType: contentType);

Console.WriteLine($"Operation Id: {operation.Id}, Started.");

while (!operation.HasCompleted)
{
    Console.WriteLine($"Operation Id: {operation.Id}, Running.");
    await Task.Delay(TimeSpan.FromSeconds(3));
    await operation.UpdateStatusAsync();
}

Console.WriteLine($"Operation Id: {operation.Id}, Completed.");

AnalysisResult = operation.Value;
foreach (AnalysisContent? item in analysisResult.Contents)
{ foreach (KeyValuePair<string, ContentField> field in item.Fields) { Console.WriteLine($"Field: {field.Key}: Value: {field.Value.Value}"); Console.WriteLine(); } } Console.WriteLine("Done");

Hope this helps.

Read more:
   Azure Content Understanding client library for .NET

Happy Coding.

Regards,
Jaliya

Friday, March 20, 2026

EF Core 11.0: Create and Apply Migrations in a Single Command

When working with migrations in Entity Framework Core, what we usually do is first create the migration and then apply it to the database.

It has always been a two-step process, and that can be a bit annoying when you are continuously developing.

dotnet ef

dotnet ef migrations add Initial
dotnet ef database update
Package Manager Console/PowerShell
Add-Migration Initial
Update-Database

But with EF Core 11.0, we can create and apply the migration in a single step.

dotnet ef  

dotnet ef database update Initial --add
Note the new --add argument.

Package Manager Console/PowerShell
Update-Database -Migration Initial -Add
This will create a migration named Initial and apply it to the database in one go. The migration files will still be created and saved, so you can push it along with your code

Since EF Core 11.0 is still in preview, if you are using the global EF tool, make sure it is updated to the latest version.
dotnet ef --version
dotnet tool update --global dotnet-ef
Hope this helps.

Read more:

Happy Coding.

Regards,
Jaliya

Tuesday, March 17, 2026

EF Core 11.0: Complex Types and JSON Columns on Entity Types with TPT/TPC Inheritance

In this post, let's have a look at a nice improvement in EF Core 11 around complex types, JSON columns, and inheritance mapping.

If you tried to use a complex type as a JSON column on an entity hierarchy that uses TPT (Table-per-Type) or TPC (Table-per-Concrete-Type) in EF Core 10, you would have noticed that it was not working as expected. With EF Core 11, that limitation is now gone.

Let's see how this works.

Consider the following Db Context.

public abstract class Person
{
    public int Id { get; set; }

    public required string Name { get; init; }

    public required Address Address { get; set; }
}

public class Student : Person
{
    public required string School { get; set; }
}

public class Employee : Person
{
    public required string Employer { get; set; }
}

[ComplexType]
public class Address
{
    public required string AddressLine1 { get; set; }

    public required string City { get; set; }

    public required string State { get; set; }
}

public class MyDbContext : DbContext
{
    public DbSet<Student> Students { get; set; }

    public DbSet<Employee> Employees { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        // Note: Database compatibility level 170 (Microsoft SQL Server 2025)
        optionsBuilder
            .UseSqlServer(@"<Connection_String>");
    }

    override protected void OnModelCreating(ModelBuilder modelBuilder)
    {
        // TODO: Configure
    }
}

TPT (Table-per-Type)

EF Core 10

Let's first see TPT with EF Core 10.

override protected void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>()
        .UseTptMappingStrategy();
}

This would generate 3 tables: Person, Students and Employees.

EF 10 TPT: Person, Students and Employees
Which is good, working as expected.
 
Now if we try using Complex Types:

override protected void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>()
        .UseTptMappingStrategy()
        .ComplexProperty(a => a.Address, b => b.ToJson());
}

This would be an error.

EF Core 11

With EF Core 11.0, above would create: again 3 tables: Person, Students and Employees.

EF 11 TPT: Person, Students and Employees: Identical to EF Core 10.0
EF 11 TPT: Person, Students and Employees with JSON Columns
Note: here the JSON column should be created only in the Parent table.

TPC (Table-per-Concrete-Type)

EF Core 10

Now let's now see TPC with EF Core 10.

override protected void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>()
        .UseTpcMappingStrategy();
}

This would generate only 2 tables per concrete types: Students and Employees.

EF 10 TPC: Students and Employees
Note: Here the Address column is missing.

Now if we try using Complex Types:

override protected void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>()
        .UseTpcMappingStrategy()
        .ComplexProperty(a => a.Address, b => b.ToJson());
}

This again would be an error.

EF Core 11

With EF Core 11.0, above would create: again 2 tables: Students and Employees.

EF 11 TPC: Students and Employees
EF 11 TPC: Students and Employees with JSON Columns
Here on TPC, everything seems to be working as expected.

Hope this helps.

Read more:

Happy Coding.

Regards,
Jaliya

Tuesday, March 3, 2026

C# 15: Collection Expression Arguments

In this post, let's have a look at a new C# 15 feature: Collection Expression Arguments which is now available with .NET 11 Preview 1.0.

With C# 12, we got Collection Expressions which gave us a nice unified syntax to initialize collections using [...]. But one thing we couldn't do was pass arguments to the underlying collection's constructor. For example, if you wanted to set a capacity for a List<T> or pass in a comparer for a HashSet<T>, you had to fall back to the traditional way.

Now with C# 15, we can use with(...) as the first element in a collection expression to pass arguments to the collection's constructor.

Let's have a look at some examples.

Note: C# 15 is supported on .NET 11. You will need the latest Visual Studio 2026 or the .NET 11 SDK. And make sure to set the LangVersion to preview.

Consider the below code.

string[] items = ["one", "two", "three"];

// Pass capacity
List<string> names = [with(capacity: items.Length * 2), .. items];

names.AddRange(["four", "five", "six", "seven"]);

foreach (string name in names)
{
    Console.WriteLine(name);
}
Here we are creating a List<string> using a collection expression and passing in a capacity hint to the constructor using with(capacity: items.Length * 2). The capacity here would be 6.

The output would be,
one
two
three
four
five
six
seven
Now you might notice, we are adding 7 items in total which exceeds the capacity of 6 we specified. And that's perfectly fine. Capacity is not a max size limit, it's a performance hint. It pre-allocates the internal array to that size upfront, so the list doesn't need to repeatedly resize as you add items. If you exceed it, the list simply grows automatically.

Now, let's have a look at another interesting example.

This is useful when you have a rough idea of how many items you'll have. Without it,
// Pass comparer
HashSet<string> set = [with(StringComparer.OrdinalIgnoreCase), "Hello", "HELLO", "hello"];

foreach (string name in set)
{ 
    Console.WriteLine(name); 
}
Here the output would be,
Hello
The set contains only one element because all three strings are equal when compared with OrdinalIgnoreCase. Previously, if you wanted to do this with collection expressions, you couldn't, you had to use the constructor directly. Now it's all nice and clean in a single expression.

Love it.

Read more:

Happy Coding.

Regards,
Jaliya