Azure Function output binding with Azure.Cosmos.Table - c#

I have a simple azure function as below
public static class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
[Table("MyTable")] CloudTable cloudTable,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
var entity = new CustomerEntity("Jon", "Snow")
{
Email = "babc#email.com",
PhoneNumber = "04026753432"
};
// Create the InsertOrReplace table operation
TableOperation insertOrMergeOperation = TableOperation.InsertOrMerge(entity);
// Execute the operation.
TableResult result = await cloudTable.ExecuteAsync(insertOrMergeOperation);
CustomerEntity insertedCustomer = result.Result as CustomerEntity;
// Get the request units consumed by the current operation. RequestCharge of a TableResult is only applied to Azure Cosmos DB
if (result.RequestCharge.HasValue)
{
Console.WriteLine("Request Charge of InsertOrMerge Operation: " + result.RequestCharge);
}
return new OkObjectResult($"Hello");
}
}
I am using Microsoft.Azure.Cosmos.Table instead of Microsoft.WindowsAzure.Storage.Table. However the "Table" attribute is not being recognize. Is there any extension nuget that I need to include here ?
-Alan-

It sounds like you may be trying to mix and match SDKs. The binding that you are using in the function is not using the Cosmos version of the table SDK, so you either need to use the storage one outright, or not leverage a binding and just use the Cosmos Table SDK directly to create your own instance of a cloudTable

Add Microsoft.Azure.WebJobs.Extensions.Storage package and your done

Related

Azure Functions Synchronous operations are disallowed Why?

I am working on two functions in Azure Functions
Timer Function and Http Trigger Function
My timer functions runs every 1 hour and it executes the http function via an Http Client.
Now I do get an error Synchronous operations are disallowed
And I know how to solve this using the article on stack overflow
But I am curious as why am I getting this error?
Whats the cause of it?
The error doesn't occur when using Postman.
My Timer Code
Azure Functions Core Tools (3.0.2245 Commit hash: 1d094e2f3ef79b9a478a1621ea7ec3f93ac1910d)
Function Runtime Version: 3.0.13139.0
Host configuration file read:
{
"version": "2.0"
}
public static class DoSomeStuffTimer
{
[FunctionName("DoSomeStuffTimer")]
public static void Run([TimerTrigger("0 0 7 * * *")]TimerInfo myTimer, ILogger log)
{
try
{
log.LogInformation($"C# DoSomeStuffTimer executing at: {DateTime.Now}");
string url = Environment.GetEnvironmentVariable(EnvironmentKey.HostKey) + "/api/DoSomeStuff";
HttpClient client = new HttpClient();
client.PostAsJsonAsync(url, new DoSomeStuffRequest());
log.LogInformation($"C# DoSomeStuffTimer executed at: {DateTime.Now}");
}
catch (Exception e)
{
log.LogInformation(e.ToString());
}
}
}
My Http Code
public class DoSomeStuffFunction
{
[FunctionName("DoSomeStuffFunction")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "DoSomeStuff")]
HttpRequestMessage req,
ILogger log)
{
var response = new ContentResult {ContentType = "application/json", StatusCode = 200};
try
{
DoSomeStuffRequest
request = req.Content.ReadAsAsync<DoSomeStuffRequest>().Result;
}
catch (Exception e)
{
log.LogInformation(e.ToString());
}
}
}
Starting with ASP.NET Core 3.0 synchronous calls are disabled by default So any function running on 3.0 will encounter this when it tries to do synchronous calls
I looked into it and found the reason why it happens in your function is that ReadAsAsync<>() somewhere in it's operation does something synchronously. I am not sure exactly why it does this or why it doesn't break when you call the httptrigger directly. That'll require quite a bit more work to figure out.
To make your code work without FUNCTIONS_V2_COMPATIBILITY_MODE set to True you can use one of the other readers, for example ReadAsStreamAsync() instead.
Below you can find the method that works (I tested it locally). However, I would not recommend you call another function in your function app directly and instead follow the recommendations by Microsoft or create an abstraction that contains the logic that both functions can call on independently.
public class DoSomeStuffFunction
{
[FunctionName("DoSomeStuffFunction")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "DoSomeStuff")]
HttpRequestMessage req,
ILogger log)
{
var response = new ContentResult { ContentType = "application/json", StatusCode = 200 };
try
{
var request = await req.Content.ReadAsStreamAsync();
using (StreamReader rd = new StreamReader(request))
{
var result = JsonConvert.DeserializeObject<DoSomeStuffRequest>(await rd.ReadToEndAsync()).;
}
return new OkObjectResult(response);
}
catch (Exception e)
{
log.LogInformation(e.ToString());
return new BadRequestObjectResult("It went wrong");
}
}
}
The solution you mentioned is to set the variable FUNCTIONS_V2_COMPATIBILITY_MODE to true. We can see some information about this variable in this page.
So,did you do the operation to upgraded your function from v2 to v3 ? It may cause this issue.
Update:
I test it in my side on local visual studio. When I create the function app in visual studio, I choose azure function v3(.net core). And below is my code, it works fine without any error.
namespace FunctionApp8
{
public static class Function2
{
[FunctionName("Function2")]
public static void Run([TimerTrigger("0 */1 * * * *")]TimerInfo myTimer, ILogger log)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
string url = "http://localhost:7071/api/triggerFunction";
HttpClient client = new HttpClient();
client.PostAsJsonAsync(url, "");
log.LogInformation($"C# DoSomeStuffTimer executed at: {DateTime.Now}");
}
}
}
namespace FunctionApp8
{
public static class Function1
{
[FunctionName("triggerFunction")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
return (ActionResult)new OkObjectResult($"Hello");
}
}
}
I'm not sure if your function app is related to .net core 2.x. So could you please follow the steps recreate another function app(check if your visual studio has been updated to the lastest version and choose Azure Function v3 .NET core at the beginning) and test if it works fine.

azure function c# http trigger blob output

Can someone describe me how I can configure a C# azure function which uses an HTTP input trigger and a blob storage output trigger?
Maybe also with an example code snippet and an example function.json. I don't get it to work locally with the azure functions core tools.
This is a combined HTTP triggered function with a output blob binding:
[FunctionName("HttpTriggeredFunction")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest httpRequest,
[Blob("blobcontainer", Connection = "StorageConnectionString")] CloudBlobContainer outputContainer,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
await outputContainer.CreateIfNotExistsAsync();
var requestBody = await new StreamReader(httpRequest.Body).ReadToEndAsync();
var blobName = Guid.NewGuid().ToString();
var cloudBlockBlob = outputContainer.GetBlockBlobReference(blobName);
await cloudBlockBlob.UploadTextAsync(requestBody);
return new OkObjectResult(blobName);
}
It uses the CloudBlobContainer output type to get a reference to the blob container which then enables you to use methods such as .GetBlockBlobReference("blobPath") to get a reference to a blob.
Once you have a reference to a blob, you can use different methods to upload:
cloudBlockBlob.UploadFromByteArrayAsync()
cloudBlockBlob.UploadFromFileAsync()
cloudBlockBlob.UploadTextAsync()
cloudBlockBlob.UploadFromStreamAsync()
To get it running locally, you need set some things up. Notice in my example the attribute [Blob("blobcontainer", Connection = "StorageConnectionString")]
"blobcontainer" this can be whatever you want and will be the name of the container that will be created in your storage account by this line outputContainer.CreateIfNotExistsAsync(); (if it doesn't exist already).
Connection = "StorageConnectionString" this can be a setting in your local.settings.json for the connection string of your storage account. When developing locally I would recommend setting this to "UseDevelopmentStorage=true" so that you can take advantage of the storage emulator. Then when you are ready to deploy onto Azure, you would create a setting in the function app containing the real connection string.
local.settings.json
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"StorageConnectionString": "UseDevelopmentStorage=true"
}
}
to make an http function that saves to Blob Storage use this code:
#r "Newtonsoft.Json"
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
public static async Task<IActionResult> Run(HttpRequest req, ILogger log,TextWriter outputBlob)
{
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
outputBlob.WriteLine(requestBody);
string result = "{ 'result': 'ok' }";
dynamic data = JsonConvert.DeserializeObject(result);
return new OkObjectResult(data);
}
You need to set the output binding:
You can then run a test posting content on the test window
Everything you need is there in the Official docs page,
(i) Http and WebHooks
(ii)Output binding blob storage
Http Trigger Sample code
[FunctionName("HttpTriggerCSharp")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]
HttpRequest req, ILogger log)
Blob Storage Output binding
[FunctionName("ResizeImage")]
public static void Run(
[BlobTrigger("sample-images/{name}")] Stream image,
[Blob("sample-images-sm/{name}", FileAccess.Write)] Stream imageSmall,
[Blob("sample-images-md/{name}", FileAccess.Write)] Stream imageMedium)

Azure function blob BlobPath to lowercase

I have an Azure function like this
public async Task<IActionResult> GetProducts
(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "products/{prod}/all")] HttpRequest req,
[Blob("%prodPath%", FileAccess.Read, Connection = "AzureWebJobsStorage")] Stream myBlobRead,
ILogger log, string prod)
{
var result = await _function.get(myBlobRead).ConfigureAwait(false);
return result;
}
)
The blob reads the file path from config json file.
Inside the config json file, there is a variable and a value like this:
"prodPath": "products/{prod}-all.csv"
if the user do a get request like this => www.xxx.com/api/products/table/all
everything is fine because the blob file name called table-all.csv
but if the user tries to do a get request like this => www.xxx.com/api/products/Table/all, its will fail becuase the name does'nt match.
Can you please help me with that?
I tried to change the variable in the config file to
[tolower("prodPath")]: "products/{prod}-all.csv"
but same problem.
How to change the prod to lowercase ?
Thank you
I may suggest using the runtime binding technique (article for c# and c# script). The idea is that instead of using the blog attribute you use the IBinder parameter and then invoke this binding in your function body. Obviously, you can now evaluate any parameter you want. Note, that you should remove the expression from the json file if you use this technique.
Here is a sample from MSDN combined with bit of your code:
public static class IBinderExample
{
[FunctionName("CreateBlobUsingBinder")]
public static void Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "products/{prod}/all")] HttpRequest req,
IBinder binder)
{
var prodPath = $"products/{prod}-all.csv".ToLower();
// provide connection string and optionally change TextWriter to whatever you need
using (var writer = binder.Bind<TextWriter>(new BlobAttribute(
prodPath, FileAccess.Write)))
{
writer.Write("Hello World!");
};
}
}

Azure function insert/read from cosmosdb table

I want to read and write to a table from a cosmosdb using azure function on .NET, this code try to read from Test Table using ExecuteQuerySegmentAsync but it doesn't work.
Its almost working, its writing logs in a new table that it has created. I changed from TestTable to LogTable, and it successfully retrieved some entities, I tried to manually add a new entity to the log table and it this code is only retrieving the log entities, but I couldn't see anything different from the to the entity I added, I'm lost.
public static class HttpTrigger {
[FunctionName("HttpTrigger")]
public static HttpResponseMessage Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = null)]HttpRequest req,
[Table("TestTable")] CloudTable tab,
TraceWriter log) {
log.Info("Kicking off.");
// Load data from TableStorage using CloudTable
var querySegment = tab.ExecuteQuerySegmentedAsync(new TableQuery(), null);
//StringContent responseContent = null;
foreach (DynamicTableEntity item in querySegment.Result) {
log.Info($"Data loaded: '{item.PartitionKey}'");// | '{item.Familia}' | '{item.IsDeleted}'");
//responseContent = new StringContent(JsonConvert.SerializeObject(item), Encoding.UTF8, "application/json");
}
log.Info("Done.");
return new HttpResponseMessage(HttpStatusCode.OK) {
Content = responseContent
};
}
}
[EDIT]
I discovered that it is reading data from a Table Storage, and i need to read from cosmosDB Table

DocumentDB with Azure Functions

I'm trying to connect an Azure DocumentDB and save documents using Azure Functions but I don't know how to create the connection.
You can do it using the Azure Portal.
After you created the DocumentDB -
Create new Azure Function.
Go to the Integrate Tab.
You can choose Azure Document DB as an output for your function.
Choose your Document DB/Database Name/Collection you want to use.
Document parameter name is the Output of your function.
For example
using System;
public static void Run(string input, out object document, TraceWriter log)
{
log.Info($"C# manually triggered function called with input: {input}");
document = new {
text = $"I'm running in a C# function! {input}"
};
}
you need to provide out object which is the same as you defined in the output tab.
You can just use the document client directly:
var endpoint = "https://XXXXX.documents.azure.com:443/";
var authKey = "XXXXX";
using (var client = new DocumentClient(new Uri(endpoint), authKey))
{
var sqlCountQuery = "select value count(1) from c";
IDocumentQuery<dynamic> query = client.CreateDocumentQuery<dynamic>(UriFactory.CreateDocumentCollectionUri("YOUR_DB_ID", "YOUR_COLLECTON_ID"), sqlCountQuery).AsDocumentQuery();
....
}
Azure Functions supports Document DB (Cosmos DB) out-of-the-box. You can just simply add an environment variable called AzureWebJobsDocumentDBConnectionString in V1 or AzureWebJobsCosmosDBConnectionString in V2.
Then just use a CosmosDBTrigger binding attribute for input binding like (in C# for example):
public static class UpsertProductCosmosDbTrigger
{
[FunctionName("ProductUpsertCosmosDbTrigger")]
public static void Run(
[CosmosDBTrigger(
// Those names come from the application settings.
// Those names can come with both preceding % and trailing %.
databaseName: "CosmosDbDdatabaseName",
collectionName: "CosmosDbCollectionName",
LeaseDatabaseName = "CosmosDbDdatabaseName",
LeaseCollectionName = "CosmosDbLeaseCollectionName")]
IReadOnlyList<Document> input,
TraceWriter log)
...
For output binding use DocumentDB output binding attribute in V1 and CosmosDB in V2 like:
[FunctionName("ProductUpsertHttpTrigger")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "products")]
HttpRequestMessage req,
[DocumentDB(
databaseName: "%CosmosDbDdatabaseName%",
collectionName: "%CosmosDbCollectionName%")] IAsyncCollector<Product> collector,
TraceWriter log)
...
I've written a blog post about this: https://blog.mexia.com.au/cosmos-db-in-azure-functions-v1-and-v2
var EndpointUrl = "EndpointUrl";
var PrimaryKey = "PrimaryKeyValue"
this.client = new DocumentClient(new Uri(EndpointUrl), PrimaryKey);
Database database = await this.client.CreateDatabaseIfNotExistsAsync(new Database { Id = cosmoDbName });
you can get the End-point-URL and Primary-Key value from the azure portal in the keys section.
Assume C# has similar SDK like Java. The below is for Java
There are two ways you can connect to documentDB from an Azure function.
Using SDK
DocumentClient documentClient = new DocumentClient(
"SERVICE_ENDPOINT",
"MASTER_KEY",
ConnectionPolicy.GetDefault(),
ConsistencyLevel.Session);
Refer - [https://learn.microsoft.com/en-us/azure/cosmos-db/sql-api-java-samples][1]. This has .Net Samples too.
Binding
#FunctionName("CosmosDBStore")
#CosmosDBOutput(name = "database",
databaseName = "db_name",
collectionName = "col_name",
connectionStringSetting = "AzureCosmosDBConnection")
Please make sure you have a variable in the name of "AzureCosmosDBConnection" in your application settings and local.settings.json(if you want to test locally)
Refer - [https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-cosmosdb-v2][1]
The above link has C# example too.

Categories

Resources