LINQ library – Part 1

Background

Most of the time when I’m writing database code on behalf of a customer, I do what most .NET developers probably do, which is to write a repository class and then implement it using either ADO.NET classes, or Entity Framework. Occasionally, depending on project needs, I may use some other API, such as Azure, or MongoDB, but even then, it’s still wrapped up in a repository class and isolated from the rest of the project.

That approach works well when the code is part of a larger customer project, where the choice of database technology is probably written in stone and highly unlikely to change anytime soon. But, that approach doesn’t work as well when writing a library (or NUGET package), where the database needs of the end user aren’t as well known ahead of time. In that scenario, even if one turned to individual repository classes to provide implementations for, say, Entity Framework and MongoDB, that would still result in having to create two repository classes – not to mention having to maintain those two classes over the lifetime of the library.

It gets worse if the library in question is expanded to include other types of database technologies. Thinking of supporting CouchDB? That’s another repository implementation. Want to add support for AWS? That’s another repository implementation. I’m sure everyone’s getting the picture …

Keep in mind it’s not JUST the issue of writing the same repository methods over and over. If one writes multiple repository libraries then one also has to be very careful not to expose implementation details, or project references, in such a way as to inadvertently tie users of that library to any third party packages. Thinking of using the MongoDB ObjectId type for an entity identifier? Think again, exposing that proprietary type through one’s entity classes forces everyone who uses that repository to make a reference to the MongoDB assembly. Want to decorate properties on an entity class with Entity Framework specific attributes? That might not be a great idea if the attributes in question create a back-door reference to the Entity Framework assemblies.

So what should a library developer do to alleviate these problems? Well, there are lots of possibilities out there but most of them are either too complicated, too expensive, or, in my case, not part of the open source Universe. In my specific case, I started looking into this problem back in the early 2000’s, when .NET was still shiny and new, and I had a nice side hustle going on turning my old C and C++ libraries into .NET gold (well, silver anyway). Over time, I’ve collected a bit of code that works for this specific scenario, where I need a very thin isolation later between my repository classes and the code that actually performs CRUD stuff in the database.

My solution works well for libraries that must work reasonably well with a variety of database technologies. Using my code, I can write libraries that I know will marry to a variety of databases, provided I don’t do something goofy in my entity classes, like using an ObjectId for an entity identifier…

My solution isn’t a replacement for Entity Framework, or any other brand of database API for that matter. It’s really more of a thin, basic CRUD wrapper that works with any DB that can read or write C# entity types to/from a database. That’s an important point to consider before choosing to use my library. Basic CRUD support means giving up Entity Framework’s very nice stored procedure support, since I no longer try to expose SPROC’s through my API. There are lots of other examples using any number of other technologies. So, my point is, decide whether you need something more sophisticated than simple CRUD operations before choosing to use my library.

Design

My design revolves around a unit-of-work type. Martin Fowler defines a unit-of-work as an object that “… maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems.” I always thought that was just a fancy way of saying that a unit-of-work is an object that keeps track of the “stuff” you’ve changed and writes those changes out for you, whenever you get around to telling it to, of course.

That’s essentially what my unit-of-work is for. Internally, it wraps my database connection, as well as any db specific reading, writing, and retry logic. With those details handled, the other parts of my library are free to focus on their own behavior in a database agnostic fashion. That means supporting additional database technologies is easier because there’s less to re-invent for each new DB type. Also, by exposing the unit-of-work separately, I make it easy to add behavior later, through the use of extension methods. We’ll see some of that unfold as we move through the library internals.

Here is a quick look at my unit-of-work type:

As we can see, it derives from the IBuilderProduct type, so, we know that it will support my builder library.

If you’re not familiar with my use of the Builder design pattern, here’s a link to an article I wrote about it: http://codegator.com/?p=330

So we know that any ILingUnitOfWork type will be creatable through a builder. We also know, from looking at the interface, that the type supports a single operation: SaveChangesAsync. That’s in keeping with what we know about the unit-of-work type: that it supports tracking our changes and writing those changes out to a database, at some point.

Since the ILinqUnitOfWork type is also a builder product, we know there will be a corresponding builder class somewhere in the library. Here is the trimmed down code for that class:

As we can see, the builder creates a new instance of the LinqUnitOfWorkProduct class. This is where we start to actually implement the unit-of-work behaviour. Here is the trimmed down source for that class:

The first thing to note is the Strategy property, which returns an instance of the ILinqUnitOfWorkStrategy type. Because this library is expected to work with a variety of different database types, the LinqUnitOfWorkProduct class defers behavior, as much as possible, using this strategy reference.

If you’re not familiar with my use of the Strategy design pattern, here’s a link to an article I wrote about it: http://codegator.com/?p=405

The Strategy property is initialized during the OnInitialize method, which is called by the builder during the construction process. Looking at the OnInitialize method, we see that it looks for a unit-of-work provider in the Providers collection. If a matching provider isn’t found, an exception is thrown. That’s because it really doesn’t makes any sense to go to all the trouble of creating a unit-of-work product, just to have it contain a NULL Strategy reference. If that happens, it probably means the caller didn’t properly configure the builder, so, we’ll be nice and throw an exception rather than creating a malformed unit-of-work product.

On the other hand, if a unit-of-work provider is found, then we call it’s GetStrategy method and save the results in the Strategy property. We’ll need that Strategy reference later when we start performing database operations.

The SaveChangesAsync method simply defers to the strategy, for saving any tracked changes. Again, that’s done because the unit-of-work product is expected to work with a wide variety of database types and the Strategy pattern allows us to defer our implementation to runtime.

The Strategy property is cleaned up as part of the Dispose method, which is called by the framework whenever the product is cleaned up.

So that’s really is for the product class. To callers, it looks like an instance of ILinqUnitOfWork, which means we can hang useful extension methods off that type to accomplish useful database operations, like CRUD stuff.

Let’s cover the unit-of-work strategy type next. Here is the trimmed down source for that interface:

Whereas the unit-of-work type was very simple and only had the one SaveChangesAsync method, this strategy type is more complex because the strategy represent everything needed to support CRUD operations on any given database type. We’ll see how these methods are implemented in a future article. For now, just be aware that the unit-of-work strategy supports these operations and will be used by other abstractions in the library.

So how are all these pieces used together, in a project? Well, here is a quick example, with some bits left out (for now), which we’ll cover as we move along:

Obviously, this example isn’t very useful by itself, but it does demonstrate how easy it is to create a builder and use that tool to easily construct a unit-of-work instance. Keep this example in mind because I’ll fill it out later, as I cover more of the abstractions and types in my LINQ library.

Summary

So in this article I talked about the background and purpose for my LINQ library, which provides a thin CRUD layer over a variety of database technologies. I covered the unit-of-work part of that library, along with the internals for the corresponding unit-of-work builder, product and strategy types. I also provided a demonstration, of sorts, for how everything might be created, configured and used in a project.

In the next article, I’ll introduce an entity-context type that we’ll use to perform actual CRUD operations. I’ll also cover the internals of that abstraction and how it ties in with the unit-of-work.

Finally, I’ll cover at least one of my database strategy assemblies and demonstrate how to plug it into the builder so that the unit-of-work will load it and use it at runtime.

Those are both good topics for the next couple of articles. I’ll see everyone then!

The code for this article is part of my NUGET package CG.Linq, which can be downloaded for free at  https://github.com/CodeGator/CG.Linq

The source code for the CG.Linq package lives on Github and can be obtained for free at  https://www.nuget.org/packages/CG.Linq/

 

Photo by Laura Mitulla on Unsplash