LINQ library – Part 2

Last time 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.

This time I’ll introduce the entity-context type that we’ll use to perform actual CRUD operations with. I’ll also cover the internals of that abstraction and how it ties in with the unit-of-work I presented last time.

The entity-context type is defined by the IEntityContext interface, which is shown here:

 

Notice that we have a method for creating, reading, updating and deleting entity types. The corresponding EntityContext class, where these methods are actually implemented, is shown here:

 

The first thing to note is that the EntityContext class contains a UnitOfWork property. The value for that property is passed in as a parameter in the constructor. We’ll see how instances of EntityContext are created a bit later. For now, just know that the EntityContext class integrates with the unit-of-work type through this property.

The other methods on this type all perform their various functions by first calling an extension method on the unit-of-work type, called GetStrategy, to obtain an ILinqUnitOfWorkStrategy object. From there, the class simply defers to a matching method on the strategy object. So, for instance, the InsertAsync method begins by getting a strategy reference, and then calling the matching InsertAsync method on that strategy. The other methods follow that same pattern to defer to the strategy at runtime. This is how we can get away with a single entity-context class that works with any number of databases, by deferring to a strategy at runtime.

The EntityContext class is marked as internal, which would normally prevent any callers from outside the CG.Linq namespace from creating a new instance of the class. So, how does one go about creating an EntityContext object? We use an extension method on the ILinqUnitOfWork type. Those methods are contained within the ILinqUnitOfWorkExtensions class, which is shown here:

 

The first extension method is CreateEntityContext, which is used to create EntityContext instances. This is where we call the constructor for the EntityContext type, passing in the ILinqUnitOfWork reference.

The second method is one we’ve mentioned before, called GetStrategy. This method leverages the fact that the Strategy property in the LinqUnitOfWorkProduct class is marked as internal to gain access to the underlying strategy. This is a hack, one that I’ve never been terribly happy with, but it’s still better (in my opinion) than exposing the strategy publicly through the unit-of-work interface.

The ILinqUnitOfWorkExtensions class itself actually has more public method, that expose additional types that are designed to work closely with the unit-of-work type. But, for now, we’ll just focus on the EntityContext type and save the others for a future article. Adding these access methods as extension methods off the ILinqUnitOfWork type makes it easy to add additional capabilities without complicating the LinqUnitOfWorkProduct class in the process.

Any exceptions thrown by a strategy, while performing work for the EntityContext class, are wrapped up with a high-level explanation of the operation in progress, and rethrown as a LinqException. I do that so that I’ll have consistent exceptions our of the EntityContext class, no matter what sort of strategy is in use.

The source for the LinqException class is shown here:

 

So that’s pretty much it for the EntityContext class internals. The type works closely with the ILinqUnitOfWorkStrategy type to add a thin layer of CRUD functionality on top of the existing unit-of-work abstraction.

So, how do we us it? Remember the example from the last article, with the two TODO placeholders in it? Let’s show that again here, but this time let’s fill in one of those TODO placeholders with some code to create and use an entity-context:

 

Notice that we’ve added a second class to our example, “Customer”, to simulate a typical entity type that might be used in any application. Next, look inside the using block for the unit-of-work, we’ve added code to get an entity-context from the unit-of-work, then we’ve created a instance of our Customer entity and passed that into a call to the InsertAsync method on the EntityContext type. Finally, we’ve called the SaveChangesAsync method on the unit-of-work, to save our changes for us.

There’s only one more TODO block left in our example code, and we’ll look at replacing that in the next article, where I plan to demonstrate how the database specific strategies work, and how they get added to the build and used with the unit-of-work at runtime.

Until 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/