Sarıkaya Dev Logo

Automated Document Processing Pipeline with .NET 8, Azure AI Vision, and Azure Functions

Mahmut Sarıkaya 5 min read 5 Views 0
Automated Document Processing Pipeline with .NET 8, Azure AI Vision, and Azure Functions

Why manual document handling slows digital transformation

Enterprises still spend an average of 30 % of employee time on repetitive data entry, according to a 2023 Gartner report. The root cause is often a fragmented workflow that relies on human operators to scan, read, and validate paperwork. When invoices, contracts, or insurance forms arrive in PDF or image format, the latency introduced by manual OCR can add days to a processing cycle, increasing operational costs and error rates.

Core components of a modern pipeline

Three Azure services form the backbone of an end‑to‑end solution: Azure AI Vision for optical character recognition (OCR), Azure Functions as a serverless orchestrator, and .NET 8 as the development platform. Azure AI Vision provides a pre‑trained “prebuilt‑read” model that extracts text with 98 % accuracy on printed English documents. Azure Functions scales instantly from zero to thousands of concurrent executions, ensuring that a sudden influx of 10 000 invoices does not overwhelm the system. .NET 8 brings performance improvements (up to 20 % faster start‑up) and native support for minimal APIs, making the codebase lean and testable.

Preparing Azure AI Vision

First, create a Computer Vision resource in the Azure portal. Choose the “Standard” tier, which costs roughly $1.50 per 1 000 pages processed in 2024. After provisioning, note the endpoint URL and the key; they will be injected into the Function app as environment variables VISION_ENDPOINT and VISION_KEY. Enable the “Read” API in the resource settings – no additional training is required for English‑language documents.

Setting up the Azure Function project

Open a terminal on a Windows 11 or Ubuntu 22.04 machine that meets the .NET 8 SDK requirement (version 8.0.100 or later). Then execute the following commands to scaffold a new isolated Functions project:

dotnet new func -n DocProcessor --worker-runtime dotnet-isolated --target-framework net8.0
cd DocProcessor
dotnet add package Azure.AI.FormRecognizer

The Azure.AI.FormRecognizer package contains the DocumentAnalysisClient used to call the Vision OCR endpoint. Add a local.settings.json file with the two keys mentioned earlier; the Azure Functions runtime reads them automatically when running locally.

Implementing the OCR trigger

The function below receives an HTTP POST containing a binary document stream. It creates a DocumentAnalysisClient, invokes the prebuilt-read model, and returns the extracted text as JSON. The code uses minimal API syntax introduced in .NET 8, eliminating the need for a separate Startup class.

using System.Threading.Tasks; <span class=\"comment\">// Required namespaces</span>
using Azure; <span class=\"comment\">// Azure SDK core</span>
using Azure.AI.FormRecognizer.DocumentAnalysis; <span class=\"comment\">// Vision client</span>
using Microsoft.Azure.Functions.Worker; <span class=\"comment\">// Function attributes</span>
using Microsoft.Azure.Functions.Worker.Http; <span class=\"comment\">// HTTP types</span>
using Microsoft.Extensions.Logging;

public class OcrFunction
{
    private readonly ILogger _logger;
    private readonly DocumentAnalysisClient _client;

    public OcrFunction(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger();
        var endpoint = new Uri(Environment.GetEnvironmentVariable("VISION_ENDPOINT"));
        var credential = new AzureKeyCredential(Environment.GetEnvironmentVariable("VISION_KEY"));
        _client = new DocumentAnalysisClient(endpoint, credential);
    }

    [Function("ProcessDocument")]
    public async Task Run([HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req)
    {
        _logger.LogInformation("Document received for OCR processing.");
        using var stream = req.Body;
        var operation = await _client.AnalyzeDocumentAsync("prebuilt-read", stream);
        var result = operation.Value;
        var lines = result.Content;
        var response = req.CreateResponse(System.Net.HttpStatusCode.OK);
        await response.WriteStringAsync(lines);
        return response;
    }
}

Notice the use of AnalyzeDocumentAsync with the model name "prebuilt-read". The method returns a AnalyzeResult object that already concatenates the detected text in reading order, which is sufficient for most invoice‑processing scenarios.

Connecting the pipeline to storage

In production, documents usually land in Azure Blob Storage. Instead of an HTTP trigger, you can replace it with a Blob trigger:

[Function("BlobOcr")]
public async Task Run([BlobTrigger("invoices/{name}", Connection = "AzureWebJobsStorage")] Stream blob, string name)
{
    var operation = await _client.AnalyzeDocumentAsync("prebuilt-read", blob);
    var result = operation.Value;
    // Store result in a Cosmos DB collection or send to a Service Bus queue
}

This tiny change enables the function to react automatically whenever a new file appears in the invoices container, turning the pipeline into a fully event‑driven architecture.

Performance tuning and cost control

Azure Functions on the Premium plan provide up to 2 GB of memory and a minimum of 1 vCPU per instance. For OCR‑heavy workloads, configure FUNCTIONS_WORKER_PROCESS_COUNT to 2 so that each instance can process two documents concurrently. Monitoring the DocumentModelCount metric in Azure Monitor helps you stay within the 1 M page free tier that Microsoft offers each month.

Logging, retries, and dead‑letter handling

Wrap the OCR call in a try/catch block and use the built‑in retry policy of Azure Functions (default 3 attempts). If the Vision service returns a 429 (throttling) response, log the request ID and push the blob name to a dead‑letter queue for later reprocessing. This pattern guarantees at‑least‑once delivery without losing data during peak spikes.

Deploying with Azure DevOps

A typical CI/CD pipeline includes the following steps:

  • Restore NuGet packages (dotnet restore).
  • Run unit tests with dotnet test.
  • Publish the function app (dotnet publish -c Release -o ./publish).
  • Deploy using the az functionapp deployment source config-zip command.

Because the function is container‑agnostic, you can also push it to Azure Container Apps for a fully managed Kubernetes‑free experience.

Conclusion

By pairing .NET 8’s streamlined programming model with Azure AI Vision’s high‑accuracy OCR and Azure Functions’ serverless elasticity, you can cut document‑to‑data latency from days to seconds. The architecture scales automatically, keeps costs predictable, and isolates each processing step for easier maintenance. Start with a small proof‑of‑concept—process ten invoices a day—and let Azure handle the growth as your volume climbs to thousands.

Author: Mahmut Sarıkaya — sarikayadev.com

Sources

  • Microsoft Azure Documentation – Form Recognizer (Vision OCR) API
  • .NET 8 Release Notes – Performance Improvements
  • Azure Functions Best Practices – Scaling and Retry Policies
Tags: #dotnet 8 #azure ai vision #document processing #azure functions #ocr
Share:
M

Written by

Mahmut Sarıkaya

Software Developer

Comments

No comments yet. Be the first to share your thoughts!

Leave a Comment

1 + 1 =