4

I'm having trouble figuring out how to access CosmosDB with in and out binding at the same time in at Azure Function 2.0.

I can from one HttpTrigger function get a json object from my cosmosDB collection and from another HttpTrigger function, write the json object to the collection

What I can't figure out is how to first read the json object from the cosmosDB collection, make some changes to it and write it back again, from within the same Function.

The code below should outline my question

[FunctionName("WebrootConnector")]
        public static void Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            [CosmosDB(
                databaseName: "customersDB",
                collectionName: "customers",
                ConnectionStringSetting = "CosmosDBConnection", 
                CreateIfNotExists = true,
                Id = "999",
                PartitionKey = "/id")] 
                Customers customersObject, // in binding
                out dynamic customersDocumentToDB, // out binding
                ILogger log)
        {
            // Chect if a customersObject is recieved from cosmosDB
            if (customersObject == null)
            {
                // Create a new Customers object
                customersObject = new Customers();
                // Set the id of the database document (should always be the same)
                customersObject.Id = 999;
                // Create a new empty customer list on the customers object
                customersObject.customers = new List<Customer>();

                // Add some customers to the list

            } 
            else
            {
                // if a object is received from the database
                // do something with it.
            }

            if (customersObject.customers != null)
            {
                // Write the object back to the cosmosDB collection
                customersDocumentToDB = customersObject;
                log.LogInformation($"Data written to customerDB");
            }
            else
            {
                customersDocumentToDB = null;
                log.LogInformation($"Nothing to write to database");
            }
         }

2 Answers 2

7

You have to use two separate bindings, one for in (your query), one for out. The complete list is on the official docs for the Bindings.

[FunctionName("WebrootConnector")]
public static void Run(
    [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
    [CosmosDB(
        databaseName: "customersDB",
        collectionName: "customers",
        ConnectionStringSetting = "CosmosDBConnection", 
        CreateIfNotExists = true,
        Id = "999",
        PartitionKey = "/id")] 
        Customers customersObject, // in binding
     [CosmosDB(
        databaseName: "customersDB",
        collectionName: "customers",
        ConnectionStringSetting = "CosmosDBConnection"] 
        out dynamic customersDocumentToDB, // out binding
        ILogger log)

If you want to store more than 1 document, you can use the IAsyncCollector:

[FunctionName("WebrootConnector")]
public static void Run(
    [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
    [CosmosDB(
        databaseName: "customersDB",
        collectionName: "customers",
        ConnectionStringSetting = "CosmosDBConnection", 
        CreateIfNotExists = true,
        Id = "999",
        PartitionKey = "/id")] 
        Customers customersObject, // in binding
     [CosmosDB(
        databaseName: "customersDB",
        collectionName: "customers",
        ConnectionStringSetting = "CosmosDBConnection"] 
        IAsyncCollector<dynamic> customersDocumentToDB, // out binding
        ILogger log)

And when you want to save a document call await customersDocumentToDB.AddAsync(newDocument).

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks, I had actually two separate binding in my code in the beginning, but when the json object are written to the cosmosDB i get the below error message and thought it was related to that the bindings was wrong. But as you have confirmed that the bindings as correct i guess that the error is related to json object that I'm writing to cosmosDB. Error: WebrootConnector. Microsoft.Azure.WebJobs.Host: Error while handling parameter customersDocumentToDB after function returned:. Microsoft.Azure.DocumentDB.Core: Message: {"Errors":["One of the specified inputs is invalid"]}
There is something odd on your document. Are you using dynamic? Or a custom class with custom serialization?
I found, that i was using id as int. After changing to string the error disappeared :-)
1

Just for future reference if others were to have the same problem

This was what worked for me.

[FunctionName("WebrootConnector")]
        public static void Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
            [CosmosDB(
                databaseName: "customersDB",
                collectionName: "customers",
                ConnectionStringSetting = "CosmosDBConnection",
                Id = "999"
            )]
                Customers customersObject, // in binding
            [CosmosDB(
                databaseName: "customersDB",
                collectionName: "customers",
                CreateIfNotExists = true,
                ConnectionStringSetting = "CosmosDBConnection"
            )]
                out Customers customersDocumentToDB, // out binding
                ILogger log)
        {
            if (customersObject == null)
            {
                // Create a new Customers object
                customersObject = new Customers();
                // Set the id of the database document (should always be the same)
                customersObject.Id = "999";
                // Create a new empty customer list on the customers object
                customersObject.customers = new List<Customer>();

            } 
            else
            {
                // if a object is received from the database
                // do something with it.
            }

            if (customersObject.customers != null)
            {
                // Write the object back to the cosmosDB collection
                customersDocumentToDB = customersObject;
                log.LogInformation($"Data written to customerDB");
            }
            else
            {
                customersDocumentToDB = null;
                log.LogInformation($"Nothing to write to database");
            }
         }

The Customers class:

public class Customers
    {
        [JsonProperty("id")]
        public string Id { get; set; }
        [JsonProperty("lastUpdated")]
        public System.DateTime lastUpdated { get; set; }
        [JsonProperty("customers")]
        public List<Customer> customers { get; set; }
    }

public class Customer
    {
        [JsonProperty("customerId")]
        public int customerID { get; set; }
        [JsonProperty("customerName")]
        public string customerName { get; set; }
        [JsonProperty("customerKeycode")]
        public string customerKeyCode { get; set; }
    }

After adding the bindings, one for input and one for output and changed my customersObject class id parameter to string instead of int, everything was working fine, except that the in binding always returned customersObject = null even though I had a document in the collection with id = "999", that was created by the out binding.

I found that the solution for me, was to delete the collection in my cosmosDB on the Azure portal and add CreateIfNotExists = true to the out binding. This allow the out binding to create the collection without a PartitionKey (which are not possible from the Azure portal through the web interface, as this are required) and then remove the PartitionKey = "/id" from the in binding.

Now everything is working as expected :-)

Maybe I was using the PartitionKey wrong?...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.