Supercharge Your Applications with Azure Functions v4, C# and .NET 8: A Deep Dive

M.F.M Fazrin
5 min readJun 3, 2024

--

Azure Functions, Microsoft’s serverless compute platform, empowers developers to build and deploy event-driven applications without worrying about infrastructure management. With the release of Azure Functions v4, alongside the powerful features of C# 12 and .NET 8, the possibilities for crafting efficient and scalable solutions are truly boundless.

Azure Functions

Azure Functions v4: The Next Generation of Serverless

Azure Functions v4 introduces several key improvements, including enhanced performance, improved cold start times, and support for .NET 8’s latest features. This allows for:

  • Increased Developer Productivity: Focus on writing code instead of managing servers.
  • Cost Optimization: Only pay for the compute resources consumed during function execution.
  • Improved Scalability: Automatically scales up and down based on demand, ensuring your application can handle any load.

C# 12 and .NET 8: A Perfect Match for Azure Functions

C# 12 brings valuable features that streamline code and enhance its readability, making it ideal for developing concise and efficient Azure Functions. .NET 8, with its performance improvements and cross-platform capabilities, provides a robust and versatile foundation for your serverless applications.

Azure Functions for Microservices and Standalone Tasks

Azure Functions shine in both microservices architectures and as standalone functions for handling specific tasks. Let’s explore:

Microservices:

  • Independent, Scalable Components: Each function can represent a distinct microservice, allowing for independent development, deployment, and scaling.
  • Event-Driven Communication: Trigger functions based on events from other microservices, fostering loose coupling and promoting resilience.

Standalone Functions:

  • Scheduled Tasks: Automate tasks like data processing, backups, or sending notifications at specific intervals.
  • Webhooks: Process data from external systems or APIs when certain events occur.

Sample Code: Image Resizing Microservice

Imagine a scenario where your application needs to resize uploaded images. Here’s how you can achieve this using an Azure Function:

using Azure.Storage.Blobs;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;

namespace ImageResizerFunction
{
public class ResizeImage
{
[Function("ResizeImage")]
public async Task<HttpResponseData> Run([HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequestData req)
{
var formdata = await req.ReadAsFormDataAsync();
var imageFile = formdata.Files["image"];
var width = int.Parse(formdata["width"]);
var height = int.Parse(formdata["height"]);
var blobServiceClient = new BlobServiceClient("YOUR_STORAGE_CONNECTION_STRING");
var containerClient = blobServiceClient.GetBlobContainerClient("resized-images");
using var image = Image.Load(imageFile.OpenReadStream());
image.Mutate(x => x.Resize(width, height));
var blobName = $"{Guid.NewGuid()}-{imageFile.Name}";
var blobClient = containerClient.GetBlobClient(blobName);

using var memoryStream = new MemoryStream();
image.SaveAsJpeg(memoryStream);
memoryStream.Position = 0;
await blobClient.UploadAsync(memoryStream);
var response = req.CreateResponse(HttpStatusCode.OK);
response.Headers.Add("Content-Type", "text/plain; charset=utf-8");
response.WriteString($"Image resized and uploaded to: {blobClient.Uri}");
return response;
}
}
}

Lets try to observe an another real word scenario where Azure Functions can be implemented efficiently

Automating Email with Azure Functions: A Practical Scenario

Scenario: Imagine you have a blog site, and you want to automatically send out a welcome email to every new subscriber. We can use Azure Functions to achieve this in a serverless and efficient way.

Steps:

  1. User Subscribes: A user subscribes to your blog, and their email address is stored in a database (e.g., Azure Cosmos DB).
  2. Database Trigger: A change in the database (new subscriber added) triggers an Azure Function.
  3. Azure Function Execution: The Azure Function fetches the new subscriber’s email, composes a welcome email, and sends it using a service like SendGrid.

Sample Code:

using Azure.Cosmos;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using SendGrid;
using SendGrid.Helpers.Mail;
using System.Net;

namespace EmailAutomation
{
public class WelcomeEmailFunction
{
private readonly CosmosClient _cosmosClient;
private readonly string _sendGridApiKey;
private readonly string _senderEmail;
public WelcomeEmailFunction(CosmosClient cosmosClient)
{
_cosmosClient = cosmosClient;
_sendGridApiKey = Environment.GetEnvironmentVariable("SENDGRID_API_KEY"); // Store API key securely
_senderEmail = Environment.GetEnvironmentVariable("SENDER_EMAIL");
}
[Function("SendWelcomeEmail")]
public async Task Run([CosmosDBTrigger(
databaseName: "BlogDatabase",
collectionName: "Subscribers",
ConnectionStringSetting = "CosmosDBConnection",
LeaseCollectionName = "leases",
CreateLeaseCollectionIfNotExists = true)] IReadOnlyList<Document> input,
FunctionContext context)
{
var logger = context.GetLogger("SendWelcomeEmail");
if (input != null && input.Count > 0)
{
foreach (var document in input)
{
// Assuming your document has a structure like { "email": "user@example.com" }
string subscriberEmail = document.GetPropertyValue<string>("email");
if (!string.IsNullOrEmpty(subscriberEmail))
{
await SendWelcomeEmail(subscriberEmail, logger);
}
}
}
}
private async Task SendWelcomeEmail(string toEmail, ILogger logger)
{
var client = new SendGridClient(_sendGridApiKey);
var from = new EmailAddress(_senderEmail, "Your Blog Name");
var to = new EmailAddress(toEmail);
var subject = "Welcome to Our Blog!";
var plainTextContent = "Thank you for subscribing to our blog! We're excited to have you.";
var htmlContent = "<strong>Thank you for subscribing to our blog!</strong> We're excited to have you.";
var msg = MailHelper.CreateSingleEmail(from, to, subject, plainTextContent, htmlContent);

var response = await client.SendEmailAsync(msg);
if (response.StatusCode == HttpStatusCode.Accepted)
{
logger.LogInformation($"Welcome email sent successfully to {toEmail}");
}
else
{
logger.LogError($"Error sending welcome email to {toEmail}: {response.StatusCode}");
}
}
}
}

Detailed Explanation:

Dependencies: The code uses the Azure Cosmos DB and SendGrid NuGet packages. Make sure to install these.

Constructor:

The CosmosClient is injected into the constructor, allowing for communication with the Cosmos DB database.

The SendGrid API key and sender email address are retrieved from environment variables for security.

SendWelcomeEmail Function:

CosmosDBTrigger: This attribute indicates that the function should be triggered when there are changes (new documents) in the specified Cosmos DB collection.

Input: This parameter receives a list of documents representing the changes in the database. In this case, it’s the new subscriber document(s).

FunctionContext: Provides access to logging and other function-related information.

Email Extraction: The code iterates through the new documents, extracts the subscriber’s email address, and calls the SendWelcomeEmail method.

SendWelcomeEmail Method:

SendGrid Client: Creates a SendGrid client using the API key.

Email Composition: Defines the sender, recipient, subject, and content of the email.

Send Email: Sends the email using client.SendEmailAsync().

Logging: Logs the success or failure of the email sending operation.

Setup and Configuration:

  1. Create an Azure Function App: In the Azure Portal, create a Function App and select .NET 8 as the runtime stack.
  2. Install NuGet Packages: Add the Microsoft.Azure.Functions.Worker.Extensions.CosmosDB and SendGrid packages to your project.
  3. Configure Cosmos DB: Create a Cosmos DB database and collection to store subscriber email addresses.
  4. Configure SendGrid: Create a SendGrid account and obtain your API key. Store this key securely in the function app’s configuration as an environment variable named “SENDGRID_API_KEY”.
  5. Set Environment Variables: Set the “SENDER_EMAIL” environment variable with the email address you want to use as the sender.
  6. Deploy the Function: Publish your function code to the Azure Function App.

Now, whenever a new subscriber is added to your Cosmos DB collection, the Azure Function will be triggered, and a welcome email will be automatically sent to the subscriber. This serverless approach ensures scalability, cost-efficiency, and reliability for your email automation workflow.

Benefits of using Azure Functions

  • Simplified Architecture: Break down complex order processing into manageable, independent units.
  • Increased Reliability: Individual functions can fail without affecting the entire system.
  • Cost-Effectiveness: Pay only for the execution time of each function, optimizing costs for variable order volumes.

Conclusion

Azure Functions v4, empowered by C# 12 and .NET 8, provides a potent platform for building modern, scalable applications. Whether you’re building microservices or automating tasks, Azure Functions simplifies development, reduces costs, and improves the overall resilience of your applications. By embracing the serverless paradigm, you can focus on delivering value and innovation, leaving the complexities of infrastructure management to Azure.

--

--

M.F.M Fazrin

Senior Software Development Specialist @ Primary Health Care Corporation (Qatar)