Monday, June 30, 2025

Azure Container Apps: az functionapp create VS az containerapp create --kind functionapp

At Build 2025, Microsoft announced Announcing Native Azure Functions Support in Azure Container Apps.

We already can do this: That is create a Function App in Container Apps Environment using az functionapp create.

az functionapp create `
    --name ${APP_NAME} `
    --resource-group ${RESOURCE_GROUP} `
    --environment ${ENVIRONMENT} `
    --image ${IMAGE} `
    --registry-server ${REGISTRY_SERVER} `
    --registry-username ${REGISTRY_USERNAME} `
    --registry-password ${REGISTRY_PASSWORD} `
    --max-replicas 10 `
    --min-replicas 1 `
    --storage-account ${STORAGE_ACCOUNT}

With the new announcement, we can now do this.

az containerapp create `
    --name ${APP_NAME} `
    --resource-group ${RESOURCE_GROUP} `
    --environment ${ENVIRONMENT} `
    --kind functionapp ` # Create a Function App in Azure Container Apps
    --image ${IMAGE} `
    --registry-server ${REGISTRY_SERVER} `
    --registry-username ${REGISTRY_USERNAME} `
    --registry-password ${REGISTRY_PASSWORD} `
    --ingress external `
    --target-port 80

Notice the --kind functionapp.

I have created these two container apps for testing:

Function Apps in Container Apps Environment
So what's the difference.

When I look at fa-hello-world, it's the regular function app experience, I can see the functions etc.

When I look at ca-hello-world it's the regular container app experience. I can't see the functions which is kind of reduced functionality. However, this approach gives access to ACA specific features such as multi-revision management & traffic split, authentication/authorization, health probes, side cars etc (more information here) which were previously limited when using az functionapp create.

The future Direction from Microsoft is to continue evolving and supporting the az containerapp create --kind functionapp approach. And hopefully we will be able to see the functions as well.

Hope this helps.

Happy Coding.

Regards,
Jaliya

Wednesday, June 25, 2025

ASP.NET Core in .NET 10 Preview 4: JSON Patch with System.Text.Json

With .NET 10 Preview 4 onwards, you can now use JsonPatch with System.Text.Json in ASP.NET Web API. 

Currently if you need to use JsonPatch you need to rely on Newtonsoft.Json as described in this article: JsonPatch in ASP.NET Core web API

Note: this isn't a complete drop-in replacement for the existing Newtonsoft.Json based implementation. In particular, the new implementation doesn't support dynamic types (like ExpandoObject).

Now let's see how this works.

First you need to install a new package, Microsoft.AspNetCore.JsonPatch.SystemTextJson. Note: it's still prerelease.

dotnet add package Microsoft.AspNetCore.JsonPatch.SystemTextJson --prerelease

Next, we can use the JsonPatchDocument<TModel>, that is introduced in Microsoft.AspNetCore.JsonPatch.SystemTextJson.

using Microsoft.AspNetCore.JsonPatch.SystemTextJson;
using Microsoft.AspNetCore.Mvc;
namespace WebApplication1.Controllers;
[ApiController]
[Route("[controller]")]
public class EmployeesController : ControllerBase
{
    [HttpPatch]
    [Route("{employeeId}")]
    public Employee PatchEmployee([FromRoute] int employeeId
        JsonPatchDocument<Employee> patchDocument)
    {
        Employee employee = new()
        {
            Id = employeeId,
            FirstName = "John",
            LastName = "Doe",
            Address = new Employee.AddressDto
            {
                Street = "123 Main St",
                City = "Redmond",
                State = "WA",
                ZipCode = "12345"
            }
        };
        patchDocument.ApplyTo(employee);
        return employee;
    }
}

You don't have to do any changes to the Program.cs.

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

WebApplication app = builder.Build();

app.MapControllers();

app.Run();

And invoke the endpoint like follows.

@WebApplication1_HostAddress = https://localhost:7070
PATCH {{WebApplication1_HostAddress}}/employees/1
Content-Type: application/json
[
    {
        "op": "replace",
        "path": "/LastName",
        "value": "Bloggs"
    },
    {
        "op": "replace",
        "path": "/Address/ZipCode",
        "value": "90100"
    }
]

PATCH
Hope this helps.

Happy Coding.

Regards,
Jaliya

Sunday, June 22, 2025

Super Simple .NET Run Program.cs

From .NET 10 Preview 4 onwards, we can run individual .NET Code files without any ceremony just like you would do in Python etc.

For example, I can just create a .cs file (HelloWorld.cs) with some C# code.
Console.WriteLine("Hello World!");
And then do the following.
dotnet run HelloWorld.cs
dotnet run HelloWorld.cs
If we need to use external packages within our code, we can do something like this. Here we don't have a .csproj file, so we can do the package reference inline. For demo purposes, I am using  Humanizer package.
// Reference the Humanizer package
#:package Humanizer@2.*

// Using directive
using Humanizer;

// Using the package
Console.WriteLine("Hello World!".Humanize(LetterCasing.AllCaps));
dotnet run HelloWorld.cs
That's neat!

Happy Coding.

Regards,
Jaliya

Thursday, June 19, 2025

Running Python Code within .NET Projects

Back home now after what was meant to be a relaxing holiday - unfortunately, it took quite a turn with some unexpected hospital stays both overseas and after coming home.

Started catching up and there has been some mind blowing announcements in the world of Software Development with AI.

For ML and AI, I think we all agree that Python is the go to programming language. Might change in the future, but at least currently that's the main one.

I recently had a requirement where we want to use a functionality that is written on Python to be consumed by a .NET application. The way I thought is to expose a Python API (using Flask, FastAPI etc), but then there is going to be a lot of data (floats) travelling through HTTP. Since I didn't have any other option, started on that.

And then on Microsoft Build 2025, there was this cool session on Python Meets .NET with Anthony Shaw and Scott Hanselman. It's a new project called CSnakes, where we could use Python using .NET and most importantly that's not by emulating or simulating Python. It's embedding Python into .NET process. .NET and Python code shares the same threads and same memory etc.
CSnakes
Let's have a look at a basic working example.

Here I have a basic Python function in a file called hello_world.py.
def say_hello(name: str) -> str:
    return f"Hello {name}!"
I have now created a Console Application that targets .NET 9 and installed CSnakes.Runtime package.
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net9.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="CSnakes.Runtime" Version="1.0.34" />
  </ItemGroup>

</Project>
I am putting the hello_world.py inside the project and updating .csproj file to copy the .py to Output directory. And this is a very important step, this enables CSnakes to run the source generator over Python files.
<ItemGroup>
  <AdditionalFiles Include="hello_world.py">
    <CopyToOutputDirectory>Always</CopyToOutputDirectory>
  </AdditionalFiles>
</ItemGroup>
And now updating the Program.cs as follows.
using CSnakes.Runtime;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

IHostBuilder builder = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        // Path to your Python modules
        var home = Path.Join(Environment.CurrentDirectory, ".")
        services
            .WithPython()
            .WithHome(home)
            .FromRedistributable()// Download Python 3.12 and store it locally
    });

IHost app = builder.Build();

IPythonEnvironment pythonEnvironment =  app.Services.GetRequiredService<IPythonEnvironment>();

// IMPORTANT: Source generated by CSnakes
IHelloWorld helloWorld = pythonEnvironment.HelloWorld();
string result = helloWorld.SayHello("John Doe");
Console.WriteLine(result)// Hello John Doe!
Output
If you are wondering how IHelloWorld comes into the picture, it was generated at compile time. 
Generated Source
While it's the recommended approach, you can still use CSnakes without source generator.

Now let's see how an example of how to use a Python file that uses packages. We can make use of Python pip install and  requirements.txt .

First I am adding a requirements.txt file and enable copying it to the output.
<ItemGroup>
<AdditionalFiles Include="requirements.txt">
   <CopyToOutputDirectory>Always</CopyToOutputDirectory>
  </AdditionalFiles>
</ItemGroup>
Here for the demo purposes I am adding a simple package stringcase.
stringcase
Now I am modifying the hello_world.py file as follows.
import stringcase

def say_hello(name: str) -> str:
    return f"Hello {stringcase.titlecase(name)}!"
Now I am updating Program.cs the as follows.
using CSnakes.Runtime;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

IHostBuilder builder = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        // Path to your Python modules
        var home = Path.Join(Environment.CurrentDirectory, ".");
        services
            .WithPython()
            .WithHome(home)
            .FromRedistributable() // Download Python 3.12 and store it locally
            .WithVirtualEnvironment(Path.Join(home".venv"))
            .WithPipInstaller()// Optional, Installs packages listed in requirements.txt on startup
    });

IHost app = builder.Build();

IPythonEnvironment pythonEnvironment =
    app.Services.GetRequiredService<IPythonEnvironment>();

// Source generated by CSnakes
IHelloWorld helloWorld = pythonEnvironment.HelloWorld();
string result = helloWorld.SayHello("JohnDoe");
Console.WriteLine(result)// Hello John Doe!
Output
This is pretty cool!

Watch the video:

Happy Coding.

Regards,
Jaliya