How to set blob properties in an Azure Function? - c#

This example shows how to set blob properties such as ContentType using C#. How can this be done in the following Azure Function? The method signature does not use a CloudBlob object, but rather a Stream object to read the blob.
[FunctionName("MyFunction")]
public static async Task Run([BlobTrigger("container-name/folder-name/{name}", Connection = "ConnectionString")]Stream myBlob, string name, ILogger log, Binder binder)
{
// How to change the ContentType property?
}

Please use the code below(I'm using visual studio 2017, and create function v2):
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage.Blob;
namespace FunctionApp3
{
public static class Function1
{
[FunctionName("Function1")]
public static void Run([BlobTrigger("container-name/folder-name/{name}", Connection = "AzureWebJobsStorage")]ICloudBlob myBlob, string name, ILogger log)
{
log.LogInformation("...change blob property...");
//specify the property here
myBlob.Properties.ContentType = "text/html";
//commit the property
myBlob.SetPropertiesAsync();
}
}
}

Related

SoapCore WCF C# program using XmlSerializer doesn't create the WSDL with async methods with CancellationToken

I have an issue in async WCF service using SoapCore in .Net 6 using a cancellation token and XmlSerializer serializer.
The detailed WCF application is as follows:
WCF service in C# .Net Core 6 using SoapCore NuGet package using SoapSerializer.XmlSerializer serializer
I created an async method that has the [OperationContract] attribute with a CancellationToken parameter
I try to get the WSDL using the URL https://localhost:7026/Services.svc?WSDL and it fails because of the CancellationToken with the exception ArgumentException: .NET type CancellationToken cannot be resolved into XML schema type (CancellationToken has namespace starting with System (System.Threading.CancellationToken), is a structure (value type), and is categorized by SoapCore code as very similar to bool, int, long, ... and tries to generate an XML for it and it fails)
I tried adding the [XmlIgnore] attribute to the parameter CancellationToken of the method having the [OperationContract] attribute and it doesn't work
[MessageContract(IsWrapped = false)] cannot be added to parameters of methods
Note: This works with SoapCore with SoapSerializer.DataContractSerializer serializer, but the generated WSDL is bigger enumerating many basic types that I don't use and I want to use SoapSerializer.XmlSerializer if possible.
Program.cs code:
using Microsoft.Extensions.DependencyInjection.Extensions;
using SoapCore;
namespace TestSoapCore;
public static class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSoapCore();
builder.Services.TryAddSingleton<MyService>();
builder.Services.AddMvc();
var app = builder.Build();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.UseSoapEndpoint<MyService>(
"/Services.svc",
new SoapEncoderOptions(),
SoapSerializer.XmlSerializer
// This works with SoapSerializer.DataContractSerializer but I prefer SoapSerializer.XmlSerializer if possible
);
});
app.Run();
}
}
Contract.cs code:
using System.Runtime.Serialization;
namespace TestSoapCore;
[DataContract]
public class Contract {
[DataMember]
public string? TestProperty { get; set; }
}
MyService.cs code:
using System.ServiceModel;
using System.Xml.Serialization;
namespace TestSoapCore;
[ServiceContract]
public class MyService
{
[OperationContract]
public async Task<string> Test(
Contract contract,
// [MessageContract(IsWrapped = false)] cannot be added to parameters
[XmlIgnore] // This doesn't work
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
return contract?.TestProperty + "2";
}
}
Full exception while getting the WSDL at https://localhost:7026/Services.svc?WSDL when SoapSerializer.XmlSerializer serializer is used:
How the WSDL works with SoapSerializer.XmlSerializer serializer without any CancellationToken (but I want the CancellationToken for async methods, it is better to have it):
How the WSDL is bloated and has many basic types I don't use when SoapSerializer.DataContractSerializer serializer is used (that's why I still prefer SoapSerializer.XmlSerializer if possible):
Part 1:
Part 2:
Part 3:
Part 4:
Like Aleksander Ch response, but mixed with the example of official page
Interface
[ServiceContract]
public interface IService
{
public void SetCancellationToken(CancellationToken cancellationToken);
[OperationContract]
public Task<int> CheckStatus();
}
Service (Very important ThreadLocal variable)
public sealed class Service : IService
{
private readonly ThreadLocal<CancellationToken> _threadCancellationToken = new();
Task<int> ILectoService.CheckStatus()
{
if (_threadCancellationToken.IsValueCreated)
_threadCancellationToken.Value.ThrowIfCancellationRequested();
........
}
void ILectoService.SetCancellationToken(CancellationToken cancellationToken)
{
_threadCancellationToken.Value = cancellationToken;
}
}
ServiceOperationTuner
internal sealed class ServiceOperationTuner : IServiceOperationTuner
{
public void Tune(HttpContext httpContext, object serviceInstance, SoapCore.ServiceModel.OperationDescription operation)
{
if (serviceInstance is IService service)
{
service.SetCancellationToken(httpContext.RequestAborted);
}
}
}
Add service operation tuner in Program
builder.Services.AddSoapServiceOperationTuner(new ServiceOperationTuner());
Because the CancellationToken is not working very well with WCF using SoapCore (SoapSerializer.XmlSerializer serializer doesn't generate the WSDL because of CancellationToken and SoapSerializer.DataContractSerializer serializer puts the CancellationToken as an object that needs to be sent with many properties and their types) I ended up removing the CancellationToken entirely and I used the SoapSerializer.XmlSerializer to have less data (WSDL not bloated with all the unused types, many pages of useless data).
PS: This is how the CancellationToken looks with SoapCore and SoapSerializer.DataContractSerializer serializer (not very nice..):
If You want to get SoapCore working with cancellation token there is another approach. SoapCore allows us to define ServiceOperationTuner which can read the cancellationToken from HttpContext and than assign it to service. The example bellow explains the idea of sollution
Define service operation tuner
public class ServiceOperationTuner : IServiceOperationTuner
{
public void Tune(HttpContext httpContext, object serviceInstance, OperationDescription operation)
{
if (serviceInstance != null && serviceInstance is MyService)
{
MyService service = serviceInstance as MyService;
service.SetCancellationToken(httpContext.RequestAborted);
}
}
}
Add service operation tuner in Program
builder.Services.AddSoapServiceOperationTuner(new ServiceOperationTuner());
Modify MyService in order to get cancellationToken
public class MyService
{
CancellationToken _cancellationToken;
public void SetCancellationToken(CancellationToken cancellationToken)
{
_cancellationToken = cancellationToken;
}

Azure WebJob BlobTrigger - Output Blob makes trigger not fire

I have a v3 WebJob that successfully fires when my function method signature is as follows:
public static void ProcessQueueMessage(
[BlobTrigger("process/{name}", Connection = "storage-connection")] Stream blob,
ILogger log
)
However when I add an output blob the BlobTrigger never fires.
public static void ProcessQueueMessage(
[BlobTrigger("process/{name}", Connection = "storage-connection")] Stream blob,
[Blob("output/{name}", FileAccess.Write, Connection = "storage-connection")] Stream processedBlob,
ILogger log
)
The documentation I'm following is here: https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-storage-blob#output
If you want to use Azure WebJob BlobTrigger and also use a Output binding, you can follow my steps.
In my side, it works fine.
1.create a console app and install everything needed. You can follow by this doc. This will tell you how to use the WebJob SDK.
This is my code:
Functions.cs:
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using System.IO;
namespace ConsoleApp1
{
public class Functions
{
public static void ProcessQueueMessage(
[BlobTrigger("images/{name}")]Stream myBlob,
[Blob("form/{name}", FileAccess.Write)] Stream imageSmall,
string name,
ILogger log
)
{
log.LogInformation("webjobs blob trigger works!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
}
}
Program.cs:
using System;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var builder = new HostBuilder();
builder.ConfigureWebJobs(b =>
{
b.AddAzureStorage();
b.AddAzureStorageCoreServices();
});
builder.ConfigureLogging((context, b) =>
{
b.AddConsole();
});
builder.ConfigureWebJobs(b =>
{
b.AddAzureStorageCoreServices();
b.AddAzureStorage();
});
var host = builder.Build();
using (host)
{
host.Run();
}
}
}
}
appsettings.json:
{
"AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=bowmanimagestorage02;AccountKey=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxSAHfYi1d2yc+BU3NG9hkbGEPU/lJP5wtqYZ3pdDq1lGEkdUx7w==;EndpointSuffix=core.windows.net"
}
You can find that I have already add the output binding in the blobtrigger, Just follow the doc you give. I upload a image to the images container and the console show the loginformation, the image also been upload to the form container.
Things works in my side, if you have more questions, please show more details. I hope my answer can give you some help.

Test Brokered Message

Now i'm working at writing unit test on azure service bus trigger function
It's highly needed to mock somehow BrokeredMessage object that pass around into function. Function declaration is given below:
public static void Run(
[ServiceBusTrigger("saas01.queue.dbmigration", AccessRights.Manage, Connection = "connection")]BrokeredMessage message)
Unfortunately, i can't find any applicable way to mock it. It hardly mocking du to this class is sealed and i can't event create wrapper around it. Do you have some ideas about it?
Thanks for helping
,
One solution is to create a wrapper around BrokeredMessage you can test, as is done here. Here's also a MSDN post to the ServiceBus team that talks about using a wrapper too.
Note that Azure Functions V2 uses the Message class, which is public and not sealed.
[FunctionName("ServiceBusFunc")]
public static void Run([ServiceBusTrigger("myqueue", AccessRights.Manage, Connection = "ServiceBus")]BrokeredMessage myQueueItem, TraceWriter log)
{
var message = new MyBrokeredMessage(myQueueItem);
BusinessLogic(message, log);
}
public static void BusinessLogic(MyBrokeredMessage myMessage, TraceWriter log)
{
var stream = myMessage.GetBody<Stream>();
var reader = new StreamReader(stream);
log.Info($"C# ServiceBus queue trigger function processed message: '{reader.ReadToEnd() }'");
}
public class MyBrokeredMessage
{
private BrokeredMessage _msg;
public MyBrokeredMessage(BrokeredMessage msg) => _msg = msg;
public T GetBody<T>()
{
return _msg.GetBody<T>();
}
}

Azure Functions develop locally with input and output bindings

Looking at the examples for developing Azure Functions in Visual Studio 2017 and can see that a new function template can be set up with a trigger.
So for a queue, the template would be the following:
using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
namespace FunctionApp1
{
public static class Function1
{
[FunctionName("QueueTriggerCSharp")]
public static void Run([QueueTrigger("myqueue-items", Connection = "QueueStorage")]string myQueueItem, TraceWriter log)
{
log.Info($"C# Queue trigger function processed: {myQueueItem}");
}
}
}
Are you able to add and run other input and output bindings locally such as:
using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
namespace FunctionApp1
{
public static class Function1
{
[FunctionName("QueueTriggerCSharp")]
public static async Task Run([QueueTrigger("myqueue-items", Connection = "QueueStorage")]string myQueueItem, CloudTable inputTable, IAsyncCollector<string> outputEventHubMessages, TraceWriter log)
{
log.Info($"C# Queue trigger function processed: {myQueueItem}");
TableQuery<TableEntity> query = new TableQuery<FailedEventEntity>().Where(
TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "helloWorld"));
List<TableEntity> entities = inputTable.ExecuteQuery(query).ToList();
await outputEventHubMessages.AddAsync(myQueueItem);
}
}
}
Do they need to be configured in local.settings.json?
Sure thing you are. You need to decorate them with attributes too:
[Table("table-name")] CloudTable inputTable,
[EventHub("event-hub-name")] IAsyncCollector<string> outputEventHubMessages
The config values for local environment will be taken from local.settings.json indeed, so you need to add them there (connection strings etc).
For anyone looking for information about function binding attributes:
https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-class-library
And a completed example from my question:
Function1.cs
using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Azure.WebJobs.ServiceBus; // INCLUDE THIS FOR EVENT HUB ATTRIBUTE
namespace FunctionApp1
{
public static class Function1
{
[FunctionName("QueueTriggerCSharp")]
public static async Task Run([QueueTrigger("myqueue-items", Connection = "QueueStorageConnectionString")]string myQueueItem, [Table("tableName", Connection = "StorageAccountConnectionString")]CloudTable inputTable, [EventHub("eventHubName", Connection = "EventHubConnectionString")]IAsyncCollector<string> outputEventHubMessages, TraceWriter log)
{
log.Info($"C# Queue trigger function processed: {myQueueItem}");
TableQuery<TableEntity> query = new TableQuery<FailedEventEntity>().Where(
TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "helloWorld"));
List<TableEntity> entities = inputTable.ExecuteQuery(query).ToList();
await outputEventHubMessages.AddAsync(myQueueItem);
}
}
}
local.settings.json
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "your_storage_account_connection_string",
"AzureWebJobsDashboard": "your_storage_account_connection_string",
"QueueStorageConnectionString": "your_queue_storage_connection_string"
"StorageAccountConnectionString": "your_storage_account_connection_string"
"EventHubConnectionString": "your_event_hub_connection_string"
}
}
#Chris: This is strange, "my" version of EventHubAttribute doesn't have a Connection property. I am using Microsoft.Azure.WebJobs.ServiceBus 2.0.0.
What version are you using? As far as I can see, the latest available version is 2.0.0.

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)

Categories