In this post I'll be taking a first look at the new (unreleased) Cosmos DB SQL Provider for Entity Framework Core, getting it up and running in an Azure Functions V2 project with the awesome Function Monkey library.

In the previous post we looked at getting the new Cosmos DB provider for EF Core up and running with a single entity, inside an ASP.NET Core application.

I've been working quite a bit with Azure Functions recently and I've been loving them so far. I've found Function Monkey to be an excellent library for working with them - which, as the author states, allows you to:

Use a clean fluent API to create functions with all the boilerplate taken care of and runtime support for authorization and dependency injection.

You can find more information, including a walkthrough here.

I'll cover setting up a new project using Function Monkey, adding in the new EF Core Cosmos DB provider, and then we'll play around with related entities.

To start with, make sure you've got everything you need to get this working - see this page for details of what you need for Azure Functions, and you've also built the EFCore Cosmos SQL project (see previous post if not).

I'll start by creating a new empty V2 Function project.

2018-08-13-15_05_02-Window

2018-08-13-15_05_32-Window

And this is the (very) empty Function project.

2018-08-13-15_06_02-Window

You can use Nuget to add our packages, or just copy and paste the package list below into your csproj file:

2018-08-13-15_20_29-Window

Note - You'll also need to add a reference to the EfCore.Cosmos.Sql project. I have also set the EF version to 2.1.0 rather than 2.1.1 - the newer version seems to have compatibility issues with Azure Functions V2 at this time.

Once that's done, I've created two new classes - FunctionAppConfiguration, which holds the configuration details for the functions, and an AutoMapperProfile class. I'll leave these both blank for now.

I've also copied in the Entities folder from the previous Web API project, changed the namespace in the files, and added in a second entity called (rather imaginatively) TestEntity2. Ive also created a navigation property to this entity on our TestEntity1.

Function Monkey uses commands and command handlers, so I'm creating two folders to hold these in.

Let's see some screenshots.

Project files:

2018-08-13-15_28_05-Window

TestEntity1.cs:

2018-08-13-15_29_11-Window

TestEntity2.cs:

2018-08-13-15_29_23-Window

Now, we need some commands and corresponding command handlers. I'm going to keep this simple, so we'll just create two commands which will map to two functions - CreateTestEntity1Command & GetTestEntity1Command. We'll return a string with the ID from the Create function, and the full entity POCO from the Get function. I'd normally create Response classes (essentially ViewModels/DTO's) for the responses rather than return full POCO's.

Here they are - our Get command has an ID property and returns a TestEntity1 POCO, and our Create command has a Name and Description property and returns a string.

2018-08-13-15_36_32-Window

2018-08-13-15_36_46-Window

Now we need the handlers. This is the basic boilerplate needed for a new handler:

2018-08-13-15_41_20-Window

We pass in the DbContext via the constructor, create a new entity and save it, before passing back the generated ID.

2018-08-13-16_30_18-Window

And the Get handler:

2018-08-13-16_31_27-Window

Looking good! Now, In order to have the DbContext available for injection, we need to setup the main FunctionAppConfiguration class. I love how all of this is setup & configured in Function Monkey.

2018-08-13-16_37_14-Window

There are a few things going on in this screenshot, and if you're unfamiliar with Function Monkey I would recommend following the getting started guide first. I've added the DbContext in exactly the same way as we did in the Web API project. Further down I've registered the two functions and the corresponding commands, with the GetTestEntity1Command having the route parameter {id}. The functions are both anonymous only, but it's very easy to add JWT token validation.

Now we can debug:

2018-08-13-16_41_24-Window

A quick test of the Get function using Postman:

2018-08-13-17_36_30-Window

And a quick test of the Create function:

2018-08-13-17_00_42-Window

With the corresponding entity in Cosmos DB:

2018-08-13-17_01_20-Azure-Cosmos-DB-Emulator

Now, interestingly it hasn't included the navigation property for TestEntity2 at all in the saved document, not even as null...so let's explore this a bit more.

I'm writing this post with the benefit of hindsight, but it took a bit of trial and error to figure out entity relations. I'm sure the way I'm doing things here isn't necessarily correct or the only way to do it, so I'll provide updates as I figure this out further.

At first I thought I'd see if I could use complex types, storing relating entities inside a single document in Cosmos Db. I experimented with both the [Owned] attribute and with the Fluent API but neither worked. In the end I had to have an id on the related entity (otherwise it threw an error saying a key needed to be defined) & add the entity into the DbContext. This is how my DbContext looks with both TestEntity1 & 2 setup:

2018-08-13-17_26_44-Window

Let's go back to our Create handler and set it to create the second entity. A bit of a contrived example, I know...forgive me.

2018-08-13-17_24_28-Window

Testing generated an id...and we can see this in the Cosmos Db Explorer:

2018-08-13-17_27_43-Window

We've got our navigation property, with an ID! Which corresponds to the related entity record in Cosmos DB:

2018-08-13-17_28_00-Window

Pretty awesome. You can see the 'Discriminator' property is set to the different entity names. Now let's fetch the parent entity via our Get command:

2018-08-13-17_37_38-Window

But our navigation property is still null. Hmm, but I'm not using EF Proxies...I guess that's just how it works, at least right now, so let's explicitly include it in our context call. However, we can't use 'Include' with the 'Find' command. So, we can test this out by using Where, to start with - as below:

2018-08-13-17_43_01-Window

And...it works!

2018-08-13-17_43_32-Window

Just for sake of completeness, we can also revert back to the Find method, and then manually load the property as well, as below:

2018-08-13-17_45_30-Window

So, to finish off, how about a collection of related entities. Here's our revised TestEntity1:

2018-08-13-17_50_44-Window

And our updated create handler:

2018-08-13-17_51_13-Window

And after we post the entity, our Cosmos DB documents:

2018-08-13-17_52_02-Window

Interesting...so no collection of Id's in the TestEntity1 document for the collection. What about on one of the collection documents?

2018-08-13-17_52_52-Window

Nice, we've got a new property - 'TestEntity1id' - referring back to the parent entity. Now we manually load the collection property in our Get handler, like so:

2018-08-13-17_53_45-Window

And in Postman we now get back our parent entity with all of the related entities included:

2018-08-13-17_54_24-Window

Beautiful!

I'll be continuing to experiment with this and I'll update or create new posts with my findings.

And yes, I forgot to use AutoMapper in this example, so the code to include it was completely unnecessary. I'll create a new post on using this in the future...