Adatis BI Blogs

Class Hierarchies, SOLID Code and Json.NET Serialization Part 2

Back in Part 1 of this series we saw how Json.NET allows us to handle creating derived classes through deserializing a document that contains multiple class types definitions, with the help of our own class derived from the Json.NET CustomCreationConverter class. In this post we are going to complete our discussion of the implementation by covering further the deserialization of the classes and how we can wire all this up with an Inversion of Control (IoC) container approach to instantiating our classes. If you are unfamiliar with the general ideas behind IoC Containers you can read about them here and here. For C# and .NET specifics, the excellent book “Dependency Injection in .Net” by Mark Seemann and Steven Van Duersen is available here and well worth a read. Picking up where we left off, we now have the ability to return our specified sub-class types for the various Hive data validation rule types we want to implement, with the aid of our JsonDataRuleBaseConverter class. So how do we actually make use of this class within our code in line with our Dependency Injection and Inversion of Control approach? Json.NET gives us some examples using the IoC Container library Autofac, which serves very well for the basic requirement of giving the control over object creation specifics to the calling code. There are many other suitable IoC Container frameworks out there but this one serves us well, with the added bonus of having some examples to work from.IoC ContainersWhy Bother?Fair question I’m sure. They do add complexity at first and appear to be quite tricky to the uninitiated. Well, in essence, they allow us to avoid the direct instantiation of objects deep within our code, which can be very problematic when it comes to making a change. If you use new(), your code will become brittle. Not good like Peanut Brittle, bad like crystal-decanter-falling-off-the-mantle-piece brittle.  Pee-ew new()…“Why, what’s the big deal about new()?“ I hear you ask, after all we’ve all been happily new()ing away for all this time. Well, using new() is considered a ‘code smell’ if done beyond our Composition Root. Whenever there is a new() call, we are saying “create a concrete class tied to a specific implementation”. At this point we have introduced a dependency within our code on the actual implementation which may itself be rather volatile. Each time it changes with a new version of the code we will need to recompile as our dependency is no longer valid. Any changes to the implementation details anywhere in this (potentially deep and involved) dependency graph will ripple up through your code, causing all manner of rework. If we can avoid these new() calls, we can avoid the implementation dependencies being littered throughout our code. If we have only interface references, and leave the actual instantiation of the specific implementation classes that subscribe to these interfaces to our IoC Container, we can then remove these dependencies between our assemblies from everywhere except where we are using our IoC Container. This is a very big deal in medium to large code bases, and in even small sized projects will quickly pay dividends when it comes to considerations of adaptability, such as with maintenance and extension. Using new() in all but the top level code will lead to your peers looking strangely at you, holding their noses and reaching for the Propeller Hat. Instead we use Dependency Injection with interface parameters in our constructor methods to provide the required objects, determining the concrete instance required via our IoC Container, thereby leaving the Propeller Hat to gather dust and sticking to our SOLID principle of coding against interfaces and not implementations. Right, convinced? Maybe, maybe not, but if you’re still reading I’m guessing your interested. Okay a tincy wincy bit more theory and then back to our code.Basic IoC Container Framework ConceptsComposition RootThis is basically the entry point for our application, such as the Main() function in a console app or the Application_Start of an ASP.NET application. We want to place our IoC container code as close to this point as is possible, so as to provide the required object resolution services to the application at a concise early point. It will give us “a central point at which we do all our connecting of collaborating classes” (to paraphrase from the above book). This is going to make maintenance a whole lot easier should things need to change.Registration and ResolutionThese are the two main steps we need to consider when coding up our IoC container. First we need to map what actual concrete implementations are going to be created whenever we refer to an interface within our code. This is referred to as Registration and is done within a “Container”. Secondly we want to control the actual instantiation of the objects, including their lifetime scope, by “resolving” the contracts that the interfaces provide against the registered implementing classes. These key concepts of Registration and Resolution, together with the third R, being Release, are often referred to as the 3 Calls Pattern. You ‘Register’ your mappings, ‘Resolve’ the interfaces referenced in the code into actual concrete classes, typically just those at the top of the dependency graph, and then ‘Release’ the objects when no longer required (or alternatively leave this to the garbage collector). Okay, with that idea served up, on to our first R.RegisterThis is actually pretty straight forward in our case. Autofac does offer a lot in the way of options here, but for our purposes all we are doing is mapping a single interface to the respective class for each of our IDataRule and IDataValidationRuleSet interfaces. We are going to place this into a Singleton class that we can use from within Json.NET when it comes to deserializing our docs, which I’ve named AutofacRegister. First we need some fields for our Autofac objects, being a Container and a ContractResolver, which I’ll get onto shortly. /// /// Singleton Registration class for Autofac service contracts /// public class AutofacRegister { private static AutofacRegister instance; private static AutofacContractResolver contractResolver; private static IContainer container;ContainersAll IoC Containers frameworks  have the notion of a Container (bonkers right?) which is used for holding the registration details and creating the instances of the registered classes. Not all classes need be created with the IoC container, and we can still use new() if we so desire, but the important thing to remember is to avoid creating nasty chains of dependencies on actual implementations by using these new() calls within dependent assemblies beyond our top level entry point at Composition Root.Autofac uses the notion of a ContainerBuilder, which not surprisingly is used for creating a container for us. For our classes and interfaces, our relatively simple registration code therefore looks like the following. /// /// Registers service contracts for this instance. /// private void Register() { ContainerBuilder builder = new ContainerBuilder(); builder.RegisterType<DataRuleBase>().As<IDataRule>(); builder.RegisterType<HiveDataValidationRuleSet>().As<IDataValidationRuleSet>(); container = builder.Build(); contractResolver = new AutofacContractResolver(container); } So we map our two interfaces to their concrete classes, build a new Container, and then we pass this Container off to the constructor of a custom class we have created called AutofacContractResolver. This contract resolver will serve to assist with the deserializing of our JSON doc into the required classes. We expose this contractResolver as a property on the above class, making sure to have called Register() on our Singleton before actually using it, as below. public AutofacContractResolver ContractResolver { get { if (contractResolver == null) Register(); return contractResolver; } }ResolveNow let’s take a quick peak at our custom contract resolver class.AutofacContractResolverAs shown below, this derives from the Newtonsoft.Json.Serialization.DefaultContractResolver class, and will override some of the standard methods given therein. /// /// Resolves contracts using the Autofac Dependency Injection framework /// /// <seealso cref="Newtonsoft.Json.Serialization.DefaultContractResolver"> public class AutofacContractResolver : DefaultContractResolver { private readonly IContainer container; public AutofacContractResolver(IContainer container) { this.container = container; }So we pass an IContainer object into our constructor, which we are going to use for our IoC-related shenanigans. Okay, digging in further, we have a private method which is concerned with determining the actual object contract that will apply for our deserialized JSON. Note that this is defined as ‘new’ and as such ‘hides’ the base class method of the same name. /// /// Resolves the contract. /// /// Type of the object. /// private new JsonObjectContract ResolveContract(Type objectType) { // attempt to create the contract from the resolved type IComponentRegistration registration; if (container.ComponentRegistry.TryGetRegistration(new TypedService(objectType), out registration)) { Type viewType = (registration.Activator as ReflectionActivator)?.LimitType; if (viewType != null) { return base.CreateObjectContract(viewType); } } // fall back to using the registered type return base.CreateObjectContract(objectType); }So this checks to see if we have registered the objectType type, and then gets the most specific type that the component type can be cast to, using the .LimitType property. It will then get the respective JsonObjectContract via the base class call to CreateObjectContract(), with either the more specific viewType variable or, in the case of the objectType type not being registered in the IoC container, the less specific objectType variable. This JsonObjectContract will be used to determine the deserialization behaviour into the underlying type. So with a feel for what this is going, let’s see where it is being used. The overridden CreateObjectContract method in our class below calls ResolveContract() so as to get a reference to a contract. This is then used to specify a class converter that will be used to deserialize into the various subclasses. /// /// Creates a <see cref="T:Newtonsoft.Json.Serialization.JsonObjectContract" /> for the given type. /// /// Type of the object. /// /// A <see cref="T:Newtonsoft.Json.Serialization.JsonObjectContract" /> for the given type. /// protected override JsonObjectContract CreateObjectContract(Type objectType) { // use Autofac to create types that have been registered with it if (container.IsRegistered(objectType)) { JsonObjectContract contract = ResolveContract(objectType); //set the required class converter to allow deserialising the various subclasses switch (objectType.FullName.ToUpper()) { case "DATAVALIDATION.IDATARULE": contract.Converter = new JsonDataRuleBaseConverter(); break; default: break; } contract.DefaultCreator = () => container.Resolve(objectType); return contract; } return base.CreateObjectContract(objectType); }In the case of our objectType being a DataValidation.DataRule class, we use the JsonDataRuleBaseConverter class we saw in Part 1, which contains the required logic to handle the deserializing into one of the Hive data validation rule derived classes. For all other classes we don’t need to handle these tricky multiple subclass types (we have only one derived class for IDataValidationRuleSet for example, being the HiveDataValidationRuleSet) and so we don’t need a Converter to be attached to the Contract. For any objects that are registered in our IoC container, we then call the container.Resolve() method to attach a DefaultCreator Function reference to the contract that will be used as the method for creating our object. For non-registered object types we simply return a contract created by calling the base DefaultContractResolver CreateObjectContract() method (no frilly class handling logic required). So quite an undertaking to get to be able to resolve an IDataRule to one of the Hive Data Validation Rule derived classes. It should be noted that we should really be using what Autofac terms “Lifetime Scopes” rather than directly resolving from the Container itself. As their name suggests, these govern the lifetime of objects and are defined over the scope for which the object lifetime is required. This results in more control over the disposal of the object. You can read about using Lifetime Scopes when resolving your objects in the Autofac documentation here.ReleaseOnce we’re done with our objects, they should be released so as to reclaim memory effectively. With Autofac we use the aforementioned Lifetime Scope in order to define the code over which the object exists, with a “using” block. I won’t go into details here as it is pretty straight forward, but you can find out more at the Autofac documentation here and here. In our case we are only using the container for a single short-lived method call in our calling code and as such this is not a concern.Now that’s all very fine and dandy but how do we actually make any use of all this?Deserializing with JsonSerializerSettingsYou may remember from Part 1 that we used our JsonDataRuleBaseConverter directly in the call to JsonConvert.DeserializeObject() as below:IDataRule dataRule = JsonConvert.DeserializeObject<IDataRule>(JsonIn, new JsonDataRuleBaseConverter());Well there is another overload to this method that allows us to pass in a JsonSerializerSettings object, which can contain a ContractResolver object. This will allow us to use our Autofac IoC container and our JsonDataRuleBaseConverter to handle interface to concrete class mappings and also the derived class handling for each of our HiveDataValidationRule types. This is achieved with the following. HiveDataTypeDataRule actual = JsonConvert.DeserializeObject(JsonIn, new JsonSerializerSettings { ContractResolver = AutofacRegister.Instance.ContractResolver });This effectively wires in our above container.Resolve(objectType) whenever we need an object created from our serialization, thereby providing the deserialization process with the specifics for our class hierarchy.So there you have it, all the benefits of IoC Containers, interfaces and inheritance for our class libraries deserialized from JSON documents. Pretty powerful stuff, courtesy of Newtonsoft and Autofac.