Search code examples
c#azureazure-functionsazure-blob-storageazure-functions-isolated

Instantiate AzureBlogStorage Binder C# isolated-process Function


Based on this Microsoft example, this so answer and this blog post I can get declarative bindings to compile. At runtime, I get a System.NullReferenceException: Object reference not set to an instance of an object from the binder.

I've tried moving the Binder to constructor. I've tried both Binder and IBinder and dug through a ton of docs. Can someone point me in the right direction?

Function

public class BeaconIngestion
    {
        private readonly ILogger<BeaconIngestion> _logger;

        public BeaconIngestion(ILogger<BeaconIngestion> logger )
        {
            _logger = logger;

        }

        [Function(nameof(BeaconIngestion))]
        [StorageAccount("DATA_STORAGE_ACCOUNT_CONNECTION_STRING")]
        public async Task Run([EventHubTrigger("messages/events", Connection = "IOTHUB_CONNECTION_STRING") ] EventData[] events,
                Binder binder)
        {
            foreach (EventData _event in events)  //leading underscore because 'event is a keyword.
            {
                string message =  System.Text.Encoding.UTF8.GetString(_event.Body.ToArray());
                _logger.LogInformation("Event Body: {body}", message);
                _logger.LogInformation("Event Content-Type: {contentType}", _event.ContentType);
                string path = $"landing/{DateTime.UtcNow.ToString("yyyy/MM/dd/HH_mm_ss")}data.json";
                _logger.LogInformation("Sending data to {path} using binder {binder}", path, binder);
                using (var writer = await binder.BindAsync<TextWriter>(new BlobAttribute(path, FileAccess.Write)))
                {
                    writer.Write("Hello World");
                }
            
            }
        }
    }

host.json

    "logging": {
        "applicationInsights": {
            "samplingSettings": {
                "isEnabled": true,
                "excludedTypes": "Request"
            },
            "enableLiveMetricsFilters": true
        }
    },
    "extensions": {
        "blobs": {
            "maxDegreeOfParallelism": 4,
            "poisonBlobThreshold": 2 
        }
    }
}

local.settins.json

{
    "IsEncrypted": false,
    "Values": {
        "AzureWebJobsStorage": "UseDevelopmentStorage=true",
        "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
        "IOTHUB_CONNECTION_STRING": "<connectionstring>",
        "DATA_STORAGE_ACCOUNT_CONNECTION_STRING" : "<connectionstring>"
    }
}

Program.cs

using Microsoft.Extensions.Hosting;

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .Build();

host.Run();

Solution

  • AFAIK, IBinder and Binder can't be used in isolated function as they are the attributes of Microsoft.Azure.WebJobs and also confirmed by product team in this github issue.

    enter image description here

    Alternatively, I have used the below code to write the data into Blob storage using BlobContainerClient attribute.

    Code:

    using System;
    using Azure.Messaging.EventHubs;
    using Azure.Storage.Blobs;
    using Microsoft.Azure.Functions.Worker;
    using Microsoft.Extensions.Logging;
    
    namespace FunctionApp33
    {
        public class Function1
        {
            private readonly ILogger<Function1> _logger;
    
            public Function1(ILogger<Function1> logger)
            {
                _logger = logger;
            }
    
            [Function(nameof(Function1))]
            public async Task Run([EventHubTrigger("samples-workitems", Connection = "EventHub_ConnectionString")] EventData[] events,
                BlobContainerClient blobContainerClient)
            {
                foreach (EventData @event in events)
                {
                    _logger.LogInformation("Event Body: {body}", @event.Body);
                    _logger.LogInformation("Event Content-Type: {contentType}", @event.ContentType);
                    var blobServiceClient = new BlobServiceClient("Storage account connection string");
    
                    //Create a unique name for the container
                    string containerName = "demo";
    
                    // Create the container and return a container client object
                    blobContainerClient = await blobServiceClient.CreateBlobContainerAsync(containerName);
    
                    // Create a local file in the ./data/ directory for uploading and downloading
                    string localPath = "data";
                    Directory.CreateDirectory(localPath);
                    string fileName = "test.txt";
                    string localFilePath = Path.Combine(localPath, fileName);
    
                    // Write text to the file
                    await File.WriteAllTextAsync(localFilePath, "Hello, World!");
    
                    // Get a reference to a blob
                    BlobClient blobClient = blobContainerClient.GetBlobClient(fileName);
    
                    Console.WriteLine("Uploading to Blob storage as blob:\n\t {0}\n", blobClient.Uri);
    
                    // Upload data from the local file
                    await blobClient.UploadAsync(localFilePath, true);
                }
            }
        }
    }
    

    Output:

    enter image description here

    enter image description here

    References: Quickstart: Azure Blob Storage library - .NET | Microsoft Learn.