Accessing Cosmos DB JSON Properties in Azure Functions with Dynamic C#

This is the eighth part in a series of articles.

When working with the Cosmos DB Microsoft.Azure.Documents.Document class, if you need to get custom properties from the document you can use the GetPropertyValue method as we saw used in part six of this series and replicated as follows:

[FunctionName("PizzaDriverLocationUpdated1")]
public static void RunOperation1([CosmosDBTrigger(
    databaseName: "pizza",
    collectionName: "driver",
    LeaseCollectionName = "PizzaDriverLocationUpdated1",
    CreateLeaseCollectionIfNotExists = true,
    ConnectionStringSetting = "pizzaConnection")] IReadOnlyList<Document> modifiedDrivers,
    ILogger log)
{
    if (modifiedDrivers != null)
    {
        foreach (var modifiedDriver in modifiedDrivers)
        {
            var driverName = modifiedDriver.GetPropertyValue<string>("Name");

            log.LogInformation($"Running operation 1 for driver {modifiedDriver.Id} {driverName}");
        }
    }
}

In the preceding code, the Azure Function is triggered from new/updated documents and the data that needs processing is passed to the function by way of the IReadOnlyList<Document> modifiedDrivers parameter. If you have multiple functions that work with a document type you may end up with many duplicated GetPropertyValue calls such as repeatedly getting the driver’s name: var driverName = modifiedDriver.GetPropertyValue<string>("Name"); Also notice the use of the magic string “Name” to refer to the document property to retrieve, this is not type safe and will return null at runtime if there is no property with that name.

Another option to simplify the code and remove the magic string is to use dynamic as the following code demonstrates:

[FunctionName("PizzaDriverLocationUpdated2")]
public static void RunOperation2([CosmosDBTrigger(
    databaseName: "pizza",
    collectionName: "driver",
    LeaseCollectionName = "PizzaDriverLocationUpdated2",
    CreateLeaseCollectionIfNotExists = true,
    ConnectionStringSetting = "pizzaConnection")] IReadOnlyList<dynamic> modifiedDrivers,
    ILogger log)
{
    if (modifiedDrivers != null)
    {
        foreach (var modifiedDriver in modifiedDrivers)
        {
            var driverName = modifiedDriver.Name;

            log.LogInformation($"Running operation 2 for driver {modifiedDriver.Id} {driverName}");
        }
    }
}

Notice in the preceding code that the binding has been changed from IReadOnlyList<Document> modifiedDrivers to IReadOnlyList<dynamic> modifiedDrivers and the code has changed from var driverName = modifiedDriver.GetPropertyValue<string>("Name"); to var driverName = modifiedDriver.Name;

While this removes the magic string, it is still not type safe and an incorrectly spelled property name will not error at compile time. Furthermore if the property does not exist, rather than return null, an exception will be thrown in your function at runtime.

If you’re not familiar with dynamic C# be sure to check out my Dynamic C# Fundamentals Pluralsight course.

SHARE:

Comments (1) -

  • Stanimir

    6/12/2019 6:34:54 AM | Reply

    A better option for ensuring type safety may be introducing a separate method (generic will be best), which accepts the return type and an enumeration, which have all required properties defined. This way, we can limit the custom properties we can get to the set defined inside the enumeration.

Add comment

Loading