The idea behind my open source Trackable Entities project is quite simple: track changes to an object graph as you update, add and remove items, then send those changes to a back end service where they can be saved in a single transaction. It’s an important thing to be able to do, because it’s difficult to wrap multiple round trips in a single transaction without holding locks for a long time. On the other hand, you could break up related operations into multiple transactions, but then you lose the benefit of atomicity, which enables you to roll back all the changes in a transaction should one of them fail.
To get started with Trackable Entities for Entity Framework Core, download the NuGet package and check out the project repository. You can also clone the sample applications and follow the instructions.
For example, you update an order with related details. Some details are unchanged, while others are modified, added or removed. All these changes should be atomic, that is, they should all succeed or none. The problem with regard to Entity Framework is that, in the context of a web service, updates must be conducted in a disconnected manner, which means that you need to inform EF about which entities require updating and what kind of changes it needs to perform. In the case of an order with multiple details that need to be updated all at once, there needs to be a way to communicate this information to the web service so that you can attach entities to a
DbContext and individually set the state of each entity. The way Trackable Entities accomplishes this is by means of a simple enum called
To track changes, entities implement an
ITrackable interface, which includes a
ModifiedProperties property to support partial updates.
In addition, Trackable Entities provides an
IMergeable interface to correlate updated entities so they can be merged back into the original object graph on the client.
Once entities have been marked as Added, Modified or Deleted, all you have to do is call the ApplyChanges extension method for DbContext.
Trackable Entities for EF Core
Because Trackable Entities is an extension of Entity Framework, it has only been available for the full .NET Framework running on Windows. But now that EF has been ported to .NET Core and can run on Linux and MacOS, it’s time for Trackable Entities to come along for the ride! This makes it possible to create a Web API with ASP.NET Core that can run both locally on a Mac and in a Docker container running on a Linux VM in the Cloud. How cool is that?!
To get started you can either use Visual Studio for Windows, Visual Studio for Mac, or if you’re adventurous, Visual Studio Code. It doesn’t matter which option you choose, because in the end your web app will run anywhere .NET Core will run. In this blog post I’ll walk you through a demo using VS for Mac — just for fun. But if you prefer, feel free to check out the sample I created using the “classic” version of Visual Studio on Windows.
Note: You’ll need to install the SDK for .NET Core 2.0 or higher.
Start by creating a new project in VS using the ASP.NET Core Web API project template.
Then add a .NET Standard Class Library for server-side trackable entities.
Add classes that implement
IMergeable interfaces. You’ll also need to add a
using directive for
System.ComponentModel.DataAnnotations.Schema, so that you can decorate
EntityIdentifier properties with a
[NotMapped] attribute, to indicate these properties do not belong to the database schema.
EF Core Migrations
Although it is possible to generate entities based on database tables (Database First), in this demo we’ll go the other way and start with entities that will be used to generate database tables and relationships (Code First). Both approaches use the EF .NET Core CLI, which you install by adding the Microsoft.EntityFrameworkCore.Design package and manually editing the .csproj file for the Web API project to insert a
DotNetCliToolReference and change the project target from
netcoreapp2.0 (which is required to run the EF CLI). You’ll also need to add a package for the EF provider you’re using, which in this case is Microsoft.EntityFrameworkCore.Sqlite, as well as a reference to the server-side entities project you created earlier. Then run
dotnet restore from the command line. (Visual Studio does not yet automatically restore tooling packages.) Here is what your .csproj file will then look like.
Next, add a specific
DbContext-derived class to the Web API project.
Lastly, you’ll need to add a class that implements
IDesignTimeDbContextFactory to create your context class with the appropriate options and connection string.
Now all you need to do to create a database from your entities is to execute the following two commands, after which a northwindslim.db file will appear in the project directory.
Configure Dependency Injection
To inject your EF context class into controllers, you’ll need to register it with ASP.NET Core’s dependency injection system. Add code to the
ConfigureServices method of the
Startup class in which you call
services.AddDbContext, passing options that include the provider and connection string. You’ll also want to configure the JSON serializer to preserve references in order to accommodate cyclical references in object graphs.
Web API Controllers
Next add the TrackableEntities.EF.Core NuGet package to the Web API project. Then you can add a controller to the project in which you use LINQ to execute queries against the database and return objects from your GET actions.
Then to utilize Trackable Entities for persisting object graphs with changed entities, all you need to do is call
Note that after calling
context.SaveChangesAsync() there is a line of code that calls
context.LoadRelatedEntitiesAsync, passing the root entity. This will traverse the object graph, loading reference properties so that entities returned to the client will have them populated. For example, when updating an
Order it isn’t necessary to send the entire
Customer entity when all you have to do is set the order’s
CustomerId property. But the client will usually want to have the
Customer property populated when it is returned from the service, which is what
LoadRelatedEntitiesAsync does. Lastly, the call to
context.AcceptChanges is there to reset
TrackingState on each entity to
Unchanged, so that the client can can have a fresh start before making additional changes.
You can now execute
dotnet run on the command line and the Web API will start listening for requests on the default port 5000.
You can then open a browser and navigate to an API endpoint to make sure you can retrieve entities. You can also use a REST client such as Postman to make POST, PUT and DELETE requests.
Trackable Entities on the Client
On the client side Trackable Entities provides change-tracking for .NET apps by means of a
ChangeTrackingCollection. You can create a .NET Core console app to which you can add the TrackableEntities.Client NuGet package. Even though this package was written for the full .NET Framework version 4.6.1, you can use it in a .NET Core app because both are compliant with .NET Standard 2.0.
Because client-side entities have a different set of concerns than server-side entities, firing events on property changes and using change tracking collections for navigation properties, you’ll probably want to generate client entities from the database schema you created earlier using the EF Core CLI tooling. The easiest way to accomplish this is to use T4 templates. Fortunately, Trackable Entities has a package for that — TrackableEntities.CodeTemplates.Client.Net45. You’ll need to add it to a traditional .NET Class Library project in Visual Studio for Windows, so that you can add an ADO.NET Entity Data Model that will use it to generate the client entities.
You can then write client-side code that changes entities, gets only the changed entities, and sends them to your Web API service for applying changes and saving them to the database. See the sample app’s repository for the complete code.
To run the console app, keep the Web API running and execute
dotnet run. Follow the prompts to retrieve, add, update and delete entities at various places in the object graph.
The cool thing is that all this is happening on a Mac, and there’s nothing to stop you from deploying it to a container service such as Amazon EC2, Microsoft Azure or Google Container Engine. With Trackable Entities and EF Core the future is now.