Optional variables in Azure Function triggered by BLOB - c#

When configuring a blob trigger for an Azure function, it is possible to use pattern matching on the blob name to map portions of the name, to variables in the function. For example:
[FunctionName("BlobTriggered")]
public static void BlobTriggered(
[BlobTrigger("myContainer/{name}.{extension}")] Stream myBlob,
string name,
string extension,
TraceWriter log)
{
...
}
But what if I wanted one of the variables to be optional? For the given example, what if I wanted the extension to be optional? Thus, I would need to make the ., and the extension itself, to be optional. Is that possible to achieve?

You can remove the pattern and handle the filename and extension yourself like this.
public static void Run([BlobTrigger("test/{name}", Connection = "teststorage_STORAGE")]Stream myBlob, string name, ILogger log)
{
log.LogInformation($"Name: {Path.GetFileName(name)}");
log.LogInformation($"Extension: {Path.GetExtension(name)}");
}
Log entries below where the first upload is with extension and the last without.

Related

Get Queue name or Topic+Subscriber name in azure function (Azure Service Bus triggered functions)

Is it possible to get the queue name in a service bus triggered Azure Function?
I know it is possible if the QueueName or TopicName+SubscriberName are hard coded. In that case I can make a const string and use it inside the function. However my service bus trigger gets the name from the settings using "%ServiceBusSettings:QueueName%", "%ServiceBusSettings:TopicName%" and "%ServiceBusSettings:SubscriberName%".
How to get the Queue or Topic+Subscriber name in this configurable case?
[FunctionName("topic-and-subscriber-function")]
public async Task Run(
[ServiceBusTrigger("%ServiceBusSettings:TopicName%", "%ServiceBusSettings:SubscriptionName%", Connection = "ServiceBusSettings:ServiceBusConnection")] Message message,
ILogger log, MessageReceiver messageReceiver)
{
// Get TopicName and SubscriberName
}
[FunctionName("queue-function")]
public async Task Run(
[ServiceBusTrigger("%ServiceBusSettings:QueueName%", Connection = "ServiceBusSettings:ServiceBusConnection")] Message message,
ILogger log, MessageReceiver messageReceiver)
{
// Get QueueName
}
What you need to get is the name of the queue, which is a property of QueueClient, but Microsoft.Azure.ServiceBus.QueueClient itself cannot be serialized. The Microsoft.Azure.ServiceBus.Message can be serialized but does not have a queuename attribute. (And Microsoft.Azure.ServiceBus.Message has no parent class. This means that it is generally impossible to obtain queuename through this class.)
You can pass in queuename as part of the message, such as input json format message:
{
"queuename":"testname",
...
}
then you can get the queuename in code.(Also You can get this value in bindings by this way. All key values passed in in json format can be obtained in the binding.)

Cannot bind parameter, cause the parameter type is not supported by the binding (HttpTrigger of Azure Functions)

I have to migrate a part of a monolith to be able to run the migrated part independently, but i'm new in azure functions.
Multiple HttpTriggers contain an a unsupported parameter type. (IMultiplierService)
public static async Task<IActionResult> GetMultiplier(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "multipliers/{id:guid}")] HttpRequest req,
string id,
IMultiplierService multiplierService){ ... }
I read online and understand that the string id is a reference to the {id:guid} in the route, but i could not find online what the purpose is of such an interface given as a parameter.
(IMultiplierService is a CRUD like interface. Contains method like 'GetById' or 'GetAll'.)
Can anyone explain how to support such a custom class as parameter input for the HttpTrigger Azure Function.
If you have questions or need more information. Go ahead.
The proper way to insert the crud like interface into the azure functions is to use dependency injection. You dont need to create static functions anymore. All you need to do is register the interface and its implementation in the startup class so that the azure functions runtime inject an instance of correct implementation of your interface. Consider following example of a azure function which uses a ISqlHelper interface. I write my non static function class as follows
public class NotifyMember
{
private readonly ISqlHelper _sqlHelper;
public NotifyMember(ISqlHelper sqlHelper)
{
_sqlHelper = sqlHelper ?? throw new ArgumentNullException(nameof(sqlHelper));
}
[FunctionName(nameof(NotifyMember))]
public async Task Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "multipliers/{id:guid}")] HttpRequest req,
string id, ILogger logger)
{//Perform function work here}
}
And I register my instance of class which implements ISqlHelper in my startup class as
[assembly: FunctionsStartup(typeof(MyFunction.Startup))]
namepace MyFunction{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddTransient<ISqlHelper, SqlHelper>();
}
}
}
For more information on how to do it refer Dependency Injection in Azure Functions

Azure Functions QueueTrigger with variable name, QueueTriggerAttribute restrictions

Assume the following typical Queue Trigger function:
public void Run([QueueTrigger("queue1")]object data, ILogger log)
{
// Do something with data
}
My problem is that "queue1" has to be a constant field, so it has to be defined at compile time.
Also, I'd want to have a base class for Queue Triggers, that could work like this:
public abstract class QueueBase<TModel>
{
public void Run([QueueTrigger("queueName")]TModel data, ILogger log)
{
// Do something with data, log something etc.
OnRunExecuted(data);
// Do something with data, log something etc.
}
public abstract void OnRunExecuted(TModel data);
}
with this, I could write own classes which inherit from QueueBase but can even live inside a library which doesn't have Microsoft.Azure.WebJobs dependency:
public class MyQueueHandler : QueueBase<MyModel>
{
public void OnRunExecuted(MyModel data) => ...;
}
But it's impossible to pass in a Queue name... is it?
See binding expressions:
In short, you can pass in a variable queue name as "%queue-name-variable%"
[FunctionName("QueueTrigger")]
public static void Run(
[QueueTrigger("%queue-name-variable%")]string myQueueItem,
ILogger log)
{
log.LogInformation($"C# Queue trigger function processed: {myQueueItem}");
}
Where input-queue-name is defined in your configuration like
{"queue-name-variable": "queue-name-in-current-env"}
As i remember attribute QueueTrigger accept only const string, so you can try make some tricks using environment variables like in post how to pass dynamic queue name

Azure Functions: C# Get blob URI

I have an Azure Function which is triggered whenever an image is uploaded to a container in a Azure storage account. I read the stream and do some actions.
I would also like to get the uri of the blob that is triggering the Function but how would I do this? Do I have to work with additional inputs / outputs?
public static void Run(Stream myBlob, TraceWriter log)
{
//get byte array of the stream
byte[] image = ReadStream(myBlob);
// ...
}
If you really want the full URI, as opposed to just the blob relative path (which string blobTrigger would give you), you'll need to do something like this:
public static void Run(CloudBlockBlob myBlob, TraceWriter log)
{
// ...
}
At that point, you can use the CloudBlockBlob object model both to get the URI (e.g. StorageUri or some other related props) and to get the stream (e.g. BeginDownloadToStream). Note that when you do this, you can no longer directly receive the Stream as an input param.
Based on the webjobs' documentation for blobs, you can simply add a string blobTrigger parameter:
public static void Run(Stream myBlob, string blobTrigger, TraceWriter log)
In your binding, you can define a variable name for blob path:
"path": "foo/{name}.bar",
Then add name as another function parameter:
public static void Run(Stream myBlob, string name, TraceWriter log)

Azure Functions - To write events from eventhub to Blob Storage

I am trying to move the events from event hub to the Blob storage. I have created trigger on the eventhub to trigger whenever a message comes to event hub. Also, i have configured outputs as Blob Storage. Right now, what i am doing within the function is:
public static void Run(string myEventHubMessage, out string outputBlob, TraceWriter log)
{
outputBlob = myEventHubMessage;
}
This will create a new blob in the container mentioned in the Ouputs configuration. But, I want is to create a blob with a specified name based on the data present in the eventhub message and need to set the content type and other metadata while saving the data to Azure Blob. Can someone help how to do this?
Regards,
John
You might no longer need to do that.
Event hub now supports piping to blob storage out of the box.
There are several possibilities to configure your output binding.
If you need to set the Blob path based on event hub message properties, you can declare your strongly typed message
public class MyEvent
{
public string SomeName { get; set; }
// more properties
}
then bind it declaratively to blob path, e.g.
{
"type": "blob",
"name": "outputBlob",
"path": "mycontainer/{SomeName}.json",
"connection": "...",
"direction": "out"
},
and modify the function accordingly
public static void Run(MyEvent myEventHubMessage, out MyEvent outputBlob)
{
outputBlob = myEventHubMessage;
}
If you need more advanced calculation to determine the output path, you can remove declarative output binding from function.json and use imperative binding:
public static async Task Run(string myEventHubMessage, Binder binder)
{
var path = ...;
using (var writer = binder.Bind<TextWriter>(new BlobAttribute(path)))
{
writer.Write(myEventHubMessage);
}
}
If you need to set more properties of Blob, bind to ICollector<CloudBlockBlob>
var collector = binder.Bind<ICollector<CloudBlockBlob>>(new BlobAttribute(path)));
collector.Add(new CloudBlockBlob { ... });
You should play with all those options to identify which scenario works for you.

Categories