LINQ library – Part 3

Last time I covered the internals of the entity-context type and then I demonstrated how that type works with the unit-of-work and strategy types to perform basic CRUD operations. This time, I plan to lay a foundation for a discussion of how the individual database strategy classes themselves are implemented. I’ll do that by starting with a base class that all my EF database strategy classes derive from.

As we’ve seen in previous articles, the unit-of-work product defers most of it’s internal logic to an ILinqUnitOfWorkStrategy object at runtime. As one might imagine, the actual implementation of that object varies wildly, depending on what sort of database is being targeted. I won’t try to present all the possible options, since there really are quite a few. Instead, I’ll just focus on the one that I use the most, which targets Microsoft SQL-Server through the Entity Framework library.

The latest Entity Framework NUGET packages support three interesting variations: (1) classic SQL-Server, which we all know and love, (2) A SQL-Lite variation, which, BTW, is really good for micro-services and bounded contexts, and (3) an in-memory version that presents some seriously interesting possibilities for unit-testing or any kind of small, quick databases that don’t otherwise need to be copied to long-term storage.

Because I wanted to support all three Entity Framework variations in my LINQ library, I started by creating a common base class. Here is a look at the trimmed source for that class:

 

There’s a bit going on in this class so let’s discuss the parts one at a time. We’ll start with the fact that the class derives from our LinqUnitOfWorkStrategyBase class. That means this class is designed to work closely with a builder product, which in this case is our LinqUnitOfWorkProduct class.

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 class contains two properties: (1) Cache, which is used to hold reflection information about entity class key types, and (2) Context, which is a reference to a Entity Framework database context object.

The constructor is pretty standard for a strategy class, passing the product and provider parameters to the base class. This is also where we create the PropertyCache instance and assign it to the Cache property. We’ll look at the implementation of the PropertyCache class a bit later.

The SaveChangesAsync method is the first public method we’ve overriden from the base class. This method defers to the SaveChangesAsync method on the object returned from the Context property. So, for Entity Framework at least, we’re simply relying on the EF framework to track our changes.

The InsertAsync calls the InsertAsync method on the object returned from the Context property. It then takes the return of that method, which is actually an EntityEntry object, and returns the Entity property on that object, which is the newly insert entity that was passed into the EntityContext from the caller.

The UpdateAsync method is a little more complicated than most of the other methods since Entity Framework doesn’t have a notion of an “update method” for us to call on the Context. Instead, we’ll break down the update into a series of steps …

First, we need to be able to find the existing entity within the EF framework. In order to do that, we also need to be able to find the unique identifier on the entity type. Now, there are several ways this could have been handled. The way I chose was to rely on a convention. A convention that assumes that all entity types used in this library have a public property on them named “Key”. The type of that property is flexible but the name isn’t. Is must be named “Key”. Why not “Id”, instead of “Key”? Well, I found, over time, that the “Id” property conflicted more often than the “Key” property did, so I stopped using “Id”.

Determining that the entity type has a “Key” property, and then obtaining the value of that property, both require the used of reflection. The problem with reflection is that it’s pretty slow. So, to compensate for that, I cache the reflection information after I get it the first time. That’s what the PropertyCache object is for, in the Cache property. Using the cache, the next time we need to know the “Key” value for an entity type, the information required to get that value will already exists and we won’t have to create it again.

Once we know the key value for the entity, we can use it to look for the corresponding entity object in the EF framework. We do that be calling the FindAsync method on the object returned from the Context property. If that search returns null then there’s nothing to update. On the other hand, if we do find a matching entity then we need to update the properties on that object, since it’s possible that the entity object passed in from the caller isn’t a tracked entity, and so, doesn’t have any of it’s changes marked as changed, within the EF framework. To sync up the property values we yse the Entry method on the Context, to find the actual entity reference, then we use the CurrentValues property to get the property values for the entity, then, finally, we call SetValues on to force the values to match those on the entity that was passed from the caller.

At this point, we’ve copied any state information from the entity object that was passed in from the caller to the entity object that we pulled out of the EF framework. From there, EF takes care of tracking that change and making sure that the underlying data store is updated when the SaveChangesAsync method is called. That’s about it for the UpdateAsync method.

The DeleteAsync method works much like the UpdateAsync method, in that we start by looking for reflection information in the Cache, then we look for a matching object in the EF framework … But, instead of updating EF, like we did for the update method, we call the Remove method, on the object returned from the Context property. The result is that, assuming a match was found in EF, we tell EF to remove the matching object from the underlying data store.

The only method left to discuss is the AsQueryable method, which is pretty simple. The intent of this method is to return an IQueryable object containing a queryable sequence of entities from the EF framework. To do that, we simply return the value of the entity set that matches the entity type.

The PropertyCache class, which we’ve used to hold the reflection information for our entity identifiers, is actually part of another library called CG.Reflection. That library like all my other NUGET packages, is available on both GitHub and NUGET.org. Here is a link for CG.Reflection:

https://github.com/CodeGator/CG.Reflection

https://www.nuget.org/packages/CG.Reflection

The trimmed down source for the PropertyCache class is shown here:

 

The PropertyCache class contains a single public indexer that accepts two parameters: (1) a type argument, and (2) a property name. The full name of the type is then combined with the property name to create a (hopefully) unique key. That key is then used on the ConcurrentDictionary object in the _cache field to locate any previously cached reflection information. If matching information was found, it is then returned. If not, reflection is used on the type parameter to locate the named property. If the type doesn’t have a matching property with that name, an exception is thrown. On the other hand, if it does contain a matching property then the method information for that property is added to the cache before it is then returned to the caller.

PropertyCache represents a very simple reflection cache, but it does what it needs to do.

That covers all the common functionality for the Entity Framework base type. This class will be derived from in order to implement a full-blown Entity Framework database strategy for my LINQ library. We’ll look at those derived classes in the next article.

 

The code for this article is part of my NUGET packages: CG.Linq, CG.Reflection and CG.Linq.EntityFramework, which can be downloaded for free here:

https://github.com/CodeGator/CG.Linq

https://github.com/CodeGator/CG.Linq.EntityFramework

https://github.com/CodeGator/CG.Reflection

 

The source code for the CG.Linq, CG.Reflection and CG.Linq.EntityFramework packages live on Github and can be obtained for free here:

https://www.nuget.org/packages/CG.Linq/

https://www.nuget.org/packages/CG.Linq.EntityFramework

https://www.nuget.org/packages/CG.Reflection

 

 

Photo by Kaleidico on Unsplash