LINQ library – Part 6

Last time I finished covering all the various SQL-Server based LINQ providers for my CG.Linq library. After 5 installments related to my LINQ library, I was going to move on with another topic, but then I remembered that I hadn’t demonstrated any kind of provider that wasn’t SQL-Server specific. That seems like a bit of a cop-out on my part. After all, I’ve mentioned how easy it is to write providers for other database stacks. It seems only reasonable, looking at it now, that I should at least cover one kind of provider that has nothing at all to do with SQL-Server.

I’ll do that now.

 

The library that I’ll cover here is named CG.Linq.Mongo and it extends the LINQ based unit-of-work that I’ve covered in previous articles so that it can be used with a MongoDB backend. If you’ve not yet read the articles I wrote for my LINQ library, here is a link: http://codegator.com/?p=637

The library itself is a provider that is designed to work with by reusable Builder library. If you’re not familiar with my library, here’s a link to an article I wrote about it:

http://codegator.com/?p=330

 

The library also makes use of my Strategy extension for my Builder library. If you’re not familiar with my use of that pattern, here’s a link to an article I wrote about it:

http://codegator.com/?p=405

 

So, having covered all the background, let’s start with the strategy interface for this library, the code for which is shown here:

 

The interface doesn’t need any additional methods because we aren’t introducing any new behaviors to the ILinkUnitOfWorkStrategy type. We’re simply making a Mongo specific version of that type. There is a concrete class associated with this interface, and the code for that class is shown here:

 

There’s a bit going on with this class, since this represents the logic for the MongoDB specific bits of the unit-of-work. Let’s just walk down the listing and cover things one at a time.

We’ll start by denoting that the class derives from the LinqUnitOfWorkStrategyBase class, and that it implements the IMongoUnitOfWorkStrategy interface we presented earlier. That means all all the stuff we’d need to implement, in order to get the class to coexist peacefully with a Builder, has been taken care of. For the purposes of this class, we can simply focus on implementing our MongoDB strategy.

We use an embedded type for this strategy named _Seq. The _Seq class contains two properties: Name and Value. We’ll use this type later on in order to generate sequential identifiers for the things we’ll store in MongoDB. We’ll cover that process in a bit. For now, just remember that the _Seq type exists.

The MongoUnitOfWorkStrategy class contains four properties:

  1. DatabaseName, which is used to contain the name of the database we’ll be connecting to.
  2. Client, which contains a reference to a Mongo specific client object.
  3. Database, which contains an object reference to the Mongo database we’ll be connecting to.
  4. Cache, which contains a property cache object, to speed up our reflection activity.

The only constructor accepts two parameters, just like all other strategy types we’ve presented in recent articles. Those two parameters are supplied by the provider so we won’t worry too much about them, other than to note that they exist and are passed in from the provider. The first thing the constructor does is create an instance of the PropertyCache class. It then fetches the name of the database from the setup (we’ll cover the setup class here in a bit). Then it fetches a url from the setup and uses that to construct a MongoClientSettings object. Finally, it creates a MongoClient instance, which is the object we’ll use to interact with MongoDB at runtime. Having created the MongoClient object we extract a database reference and save for later reference.

The first public method on the strategy is the SaveChangesAsync method, which doesn’t actually do much of anything. That’s because MongoDB doesn’t internally cache it’s updates and I didn’t need that kind of caching when I wrote the strategy. So, this method is pretty thin for this strategy. If I ever came back and added caching for pending work then this method would become much more interesting. Perhaps I’ll cover that in a future article …

Moving down in our listing, we come across the InsertAsync method. The first thing we do in that method is use the type of the entity to generate a collection name. I used to use my port of the Microsoft pluralization service to generate a pluralized name for the collection, but I took that code out at some point. It’s possible, I might add that back again but, for now, the collection names are not pluralized. This collection name is used to map between an entity type and a corresponding Mongo collection type in the backing store.

Once we have a reference to a MongoDB collection, the next thing we do is check for any previously cached key information for the entity type. Afterwards, we check to see if the underlying type for the entity’s key property is an integer. If it is, we call the NextValueAsync method to generate a unique value for that key. Once we have that unique value we inject it using the SetMethod method of the key information object. It’s worth noting here that any previously supplied key value is overwritten. That’s one of the other things I might go back and make optional. I don’t usually supply key values for entity instances but I can see where that might come in handy.

If the entity’s key property is a GUID type, then we do something similar, generating a new key value using the Guid.NewGuid method, and injecting the results using the same SetMethod call on the key information object.

Finally, If the entity’s key property is an ObjectId type, then we generate a new ObjectId instance and inject that value using – you guessed it – the very same SetMethod call on the key information object.

Having taken steps to ensure that the entity has a valid key property value, the next step is to call the InsertOneAsync method on the collection object, to actually insert the entity to Mongo. The result is that we’ve inserted a new entity to the Mongo backing store. We end the method by returning the entity, including it’s newly injected key property.

The next method we’ll look at is the UpdateAsync method. This method also starts by grabbing a collection reference from the Mongo database object using the entity type as an argument. Also, just like in the previous method, the next step is to get key information for the entity type, using the cache object. Using the GetMethod method on the key information, we’re able to extract the value of the Key property on the entity object. Using that key value, we’re then able construct a filter object for Mongo and pass that filter into the FindOneAndReplaceAsync method on the Mongo client in order to replace the entity in the MongoDB backing store. The result is that we’ve updated the corresponding entity in the Mongo backing store. From there, we return the updated entity type to the caller.

The next method in the strategy is called DeleteAsync. By now, the sequence of getting a Mongo collection using the entity’s type, then grabbing key information for the entity from the cache, then using that key information to get the entity object’s Key property, should all be familiar. In this case, after performing those steps, we build a Mongo filter using the Key property value and then pass that filter into the DeleteOnAsync method on the Mongo collection. The result is that we’ve removed the entity from the Mongo backing store.

The next method is AsQueryable. Here we get a collection reference from the database reference, just like we’ve always done. Then, we simple return the results of calling AsQueryable on that collection reference. The end result is an IQueryable reference that can be further refines by the caller. Returning an IQueryable object from our AsQueryable method might be deemed controversial by some, under the premise that doing so causes us to expose implementation details to callers. My take is: (1) this library is intended as a thin CRUD wrapper over existing LINQ packages, so, returning a LINQ interface doesn’t seem to be much of a stretch, and, (2) returning an IEnumerable here, instead of an IQueryable, might be safer but it’s also a heck of a lot less powerful when it comes to constructing queries. If I were to return IEnumerable here, I’d also have to add parameters for things like filtering, ordering, paging, etc, that all have to be done BEFORE the IEnumerable is returned. That’s doable, in fact I have code in another library that does exactly that, but it results in a fatter, chattier interface. One that’s far less intuitive to use, at least in my opinion. No, at this level, given that this is, after all, a LINQ specific library. I chose to return IQueryable here and I feel confident of that choice.

There is one final private method on the strategy called NextValueAsync. This is the method that generates unique key values for entity insertions with integer Key property types. Before I cover how I implemented this method I’ll say that generating sequential numbers for database operations is a topic that exceeds the context of my little CRUD strategy class. What I mean by that is, there are larger issues to consider when generating streams of unique values, such as: (1) how many round trips to the db does it take to generate a given set of numbers, or (2) what should be done with numbers that are generated but never used – either because the operation is canceled by the caller before it finishes, or because of an error condition, or even (3) what to do with sharded database architectures that might have multiple copies of the sequence table, resulting in more complicated logic to guarantee unique, non overlapping numbers, and so on. The entire list is long. Long enough, in fact, that it exceeds the scope of this article. Because of that, I chose to generate this method internally, as a general solution, while I continue to consider something better and more flexible, in the form of an additional NUGET package.

Having said all that, the NextValueAsync method gets a collection reference for the “_Seq” type, which we’ll all recall is an embedded class in our strategy that I quickly covered in the beginning of the article. _Seq is essentially a name-value pair used to associated a numeric sequence with an entity type in the Mongo backing store. The convention is that we use a collection named “_sequences” for storing these counts. So, the first thing we do is get a reference to the _sequences collection in the method. From there, we take some steps to ensure that the _sequences collection has the indexes we’ll need for performing quick look up operations. We created that index by calling the CreateOneAsync method, on the collection reference. Once we know that the index exists, we call the FindOneAndUpdateAsync method to pull the last value from the underling backing store and replace it with our new count. Once that’s done, we generate an enumerable range of values for whatever sequence we’ve been asked to produce, then we return that sequence to the caller. The end result is, we’ve generate a sequence of unique numeric values and we’ve updated the backing store so that the next time we ask for a sequence for any given entity type, we’ll get the next numbers in that sequence.

That’s about it for the strategy, which is most of the actual logic for this provider library. The rest of the classes glue the strategy to the builder at runtime. For instance, the strategy is created by a provider class named MongoUnitOfWorkProvider. The listing for that class is shown here:

 

This class is a pretty typical provider. It’s called by the product during initialization, when the product asks for any strategies that were configured by the caller, prior to calling the Build method on the builder. As we can see, the class has one method, GetStrategy, that returns a new instance of our strategy class.

The class implements the IMongoUnitOfWorkProvider interface, which is shown here:

There is also a setup class for this provider, named MongoUnitOfWorkSetup, which is shown here:

internal sealed class MongoUnitOfWorkSetup :

 

This class contains methods to set and get the parameters required of the Mongo strategy. The class implements the IMongoUnitOfWorkSetup interface, which is shown here:

 

The setup interface derives from the ILinqUnitOfWork interface, and contains the methods needed in order to allow the caller to properly configure the strategy before it is created by the builder.

There is also an extension method that injects the setup into a builder. That method is shown here:

 

This method creates a new MongoUnitOfWorkSetup instance, adds it to the builder’s Setups property, then returns the setup object so the caller can configure things before the builder’s Build method is called.

So that’s about it for a working Mongo provider. Using the provider is pretty easy. Here is a quick example of how to create a LINQ unit-of-work object that is tied to a copy of this Mongo provider:

 

The source and binaries for this provider are available online, for free, from here:

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

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

 

So I think I’ve covered everything now for my LINQ library. I hope the article and the code prove helpful. Thanks for reading.

 

 

Photo by Kevin Ku from Pexels