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
Related
I have the following Azure function using C# that creates/adds a student to cosmosdb and it works just fine
[FunctionName("CreateStudent")]
public static async Task<IActionResult> CreateStudent(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "student/create")] HttpRequest req,
[CosmosDB(
databaseName: "school",
collectionName: "students",
ConnectionStringSetting = "CosmosAccountEnpoint")] IAsyncCollector<Student> studentCollector,
ILogger log)
{
log.LogInformation("Creating a new student.");
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
var student = JsonConvert.DeserializeObject<Student>(requestBody);
await studentCollector.AddAsync(student);
return new OkObjectResult(student);
}
Now the 'issue' with this approach is that if the student doesn't exist it creates it which is the expected behavior, but if the student already exists it updates the record with the latest data I'm submitting.
Did some searching and IAsyncCollector only supports the method AddAsync which it doesn't have any type of validations. I found that using IMongoCollection should work because it has methods which will let me put some validations in place.
I changed the code to the following
[FunctionName("CreateStudent")]
public static async Task<IActionResult> CreateStudent(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "student/create")] HttpRequest req,
[CosmosDB(
databaseName: "school",
collectionName: "students",
ConnectionStringSetting = "CosmosAccountEnpoint")]
IMongoCollection<Student> studentCollection,
ILogger log)
{
log.LogInformation("Creating a new student");
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
var student = JsonConvert.DeserializeObject<Student>(requestBody);
if (student == null)
{
return new BadRequestObjectResult("Please pass a valid student in the request body");
}
var count = await studentCollection.CountDocumentsAsync(s => s.id == student.id);
if (count > 0)
{
return new BadRequestObjectResult($"Student with ID {student.id} already exists");
}
var studentexist = student.id;
await studentCollection.InsertOneAsync(student);
return new OkObjectResult(student);
}
After this code change i don't get any errors and everything seems to be ok, the problem is that when i run it i get the following error
Function 'CreateStudent' failed indexing and will be disabled.
[2023-02-13T15:14:55.796Z] The 'CreateStudent' function is in error: Microsoft.Azure.WebJobs.Host: Error indexing method 'CreateStudent'. Microsoft.Azure.WebJobs.Host: Can't bind CosmosDB to type 'MongoDB.Driver.IMongoCollection`1[School.Student]'.
what am i doing wrong?
The microsoft azure is for me complete new programming topic.
Programming base language is C#.
I have to use Azure Funtion Http trigger from logic App (when new e-mail arrive).
I give all possible data from the incoming e-mail in the log app to
to the azure function call.
Then I had first the problem nothing (no data) was requestable from the HttpRequest req.
I found out that C# .NetCore3.1 has problem with JSON-Object and get null-object back.
Then I build the project with .NetCore2.1 and it worked generally.
I get correct informations like req.Host and i see a vaild JSON-object in data after such a call
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
But I don't know how I an parse the data like (from, to, subject, content, attachment,..) from the JSON-object.
I have tried many samples i found , but i fond nothing what worked, e.g. get null, exception,..
Can please someone here post a small code snippet that works for azure function compiled with VS2017/2019 .NetCore2.1..
And other question is there eventually a way get the MimeMessage-obj direct from the http-request?
then i can get the informations direct from that object, but helping me with one of this both solution
where fine.
Otherwise I spilit the the JSoN Structure on myself to get the the informations on a very awkward way.
So I hope someone can help me.
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation($" host : {req.Host}");
string name = req.Query["name"];
// read the contents of the posted data into a string
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
// use Json.NET to deserialize the posted JSON into a C# dynamic object
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
For Example i had such a Json-content (is in dtaa after thes 2 lines of code):
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
Json-Content for a short email to parse (other request could differn in more ore lesser paramters(fields))
{ {
"Conversion Id": "AAQkADRmZDAwMTMwLTI3MTMtNGI0Ny1iMzFiLTQzYWJiZDY0YWI1ZQAQAE2OZM06t0uQqpd9MuONKNQ=",
"From": "test1#testxyz.com",
"Has Attachment": false,
"Internet Message Id": "<AM0PR07MB44178000A0B37882D6BC01D99ACC0#AM0PR07MB4417.eurprd07.prod.outlook.com>",
"Is Html": false,
"Is rRad": false,
"Message Id": "AAMkADRmZDAwMTMwLTI3MTMtNGI0Ny1iMzFiLTQzYWJiZDY0YWI1ZQBGAAAAAAAvlJZPJxnGS4f6YWxh7zGsBwBnLziSnuG8R6h5C2SVmlYlAAHViSWpAACAWl3JfUo4SI7D5g-MgfEiAAJiPJQeAAA=",
"Received Time": "2020-12-09T14:05:06+00:00",
"Subject": "test1",
"To": "test2#testxyz.com",
"emailBody": "1234\r\n",
"importance": "normal"
}
}
For this requirement, you need to create an inner class in your function code to parse the request body to an object. Please refer to my code below:
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace FunctionApp2
{
public static class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
//dynamic data = JsonConvert.DeserializeObject(requestBody);
EmailItem data = JsonConvert.DeserializeObject<EmailItem>(requestBody);
log.LogInformation(data.From);
log.LogInformation(data.Has_Attachment.ToString());
return new OkObjectResult("completed");
}
}
public class EmailItem
{
public string From { get; set; }
public Boolean Has_Attachment { get; set; }
//Add other fields which you want, please note add all of the fields which your request may contains in this inner class.
}
}
As you mentioned other request could differn in more ore lesser paramters(fields), so please add all of the fields which your request may contains in the EmailItem class.
By the way, the code I provided above was test with .net core 2.1 as you required. But I think the code can also work in .net core 3.1
I currently use Gembox.Document to read content from PDF documents. I have a class library that houses it all and a self hosted .NET Core 3.1 service that references it. I query the service with the PDF data and it responds with the content. I now want to move this functionality to an azure function (v3) instead, but I am running into the following error:
Could not load file or assembly 'PresentationCore, Version=4.0.0.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35'. The system cannot
find the file specified.
To simplify it I have moved only the essential parts into the azure function which you can see below:
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
ComponentInfo.SetLicense("FREE-LIMITED-KEY");
ComponentInfo.FreeLimitReached += (sender, args) => args.FreeLimitReachedAction = FreeLimitReachedAction.ContinueAsTrial;
try
{
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
ParseRequest data = JsonConvert.DeserializeObject<ParseRequest>(requestBody);
StringBuilder sb = new StringBuilder();
using (var ms = new MemoryStream(data.Data))
{
// Load document from file's path.
var document = DocumentModel.Load(ms, LoadOptions.PdfDefault);
foreach (var childElement in document.GetChildElements(true, ElementType.Paragraph))
{
sb.AppendLine(childElement.Content.ToString());
}
}
return new OkObjectResult(sb.ToString());
}
catch (Exception e)
{
return new OkObjectResult("Unable to read document");
}
}
Is this a restriction of azure functions? I have read several conflicting things which suggest it can and can't be done as it's using a WPF dll. For the record, the GemBox website provides an example for creating a PDF document in an azure function: https://www.gemboxsoftware.com/document/examples/create-word-pdf-on-azure-functions-app-service/5901. So I don't see why I wouldn't be able to read one too!
Thanks!
EDIT 1:
As per mu88's comment, I have changed the .csproj file to the below and it hasn't helped.
I reached out to GemBox support and they responded with the following:
Unfortunately, GemBox.Document uses WPF for reading PDF files. So,
even though you can write PDF files on Azure Functions, I’m afraid you
cannot read them.
But also, I should point out that PDF reader in GemBox.Document never
left the BETA stage, it has limited usage. For more information,
please check the Support level for reading PDF format (beta) section.
Instead, I would suggest you try out GemBox.Pdf, see its Reading
example. With GemBox.Pdf you can read and write PDF files on Azure
Functions.
Last, in the long term, we plan to replace the current (internal)
implementations of both PDF reader (BETA) and PDF writer in
GemBox.Document with a newer implementation that’s contained in
GemBox.Pdf without changing the public API of GemBox.Document. But
that will not be done in the current year and for later I cannot say
at this moment.
Alas, it is not possible with GemBox.Document.. yet.
GemBox.Document has this issue (it might be dependent on .net Framework component), but GemBox.Pdf works fine as below.
I tested it using the GemBox.Pdf nuget with the below Function and it works fine for both creating and loading pdf in deployed Function app.
Create PDF:
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
ComponentInfo.SetLicense("FREE-LIMITED-KEY");
using (var document = new PdfDocument())
{
// Add a page.
var page = document.Pages.Add();
// Write a text.
using (var formattedText = new PdfFormattedText())
{
formattedText.Append("Hello World!");
page.Content.DrawText(formattedText, new PdfPoint(100, 700));
}
var fileName = "Output.pdf";
var options = SaveOptions.Pdf;
using (var stream = new MemoryStream())
{
document.Save(stream, options);
return new FileContentResult(stream.ToArray(), "application/pdf") { FileDownloadName = fileName };
}
}
}
LoadPDF:
[FunctionName("Function3")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
ComponentInfo.SetLicense("FREE-LIMITED-KEY");
try
{
StringBuilder sb = new StringBuilder();
using (var document = PdfDocument.Load(req.Body))
{
foreach (var page in document.Pages)
{
sb.AppendLine(page.Content.ToString());
}
}
return new OkObjectResult(sb.ToString());
}
catch (Exception e)
{
return new ExceptionResult(e, true);
}
}
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
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.