Click here to Skip to main content
15,892,298 members
Articles / Programming Languages / C#

Deciding caching mechanism through Dependency Injection with Unity

Rate me:
Please Sign up or sign in to vote.
4.79/5 (6 votes)
3 Nov 2016CPOL11 min read 11.7K   5  
Using unity in order to decide which cache provider to use and to decouple components.

Introduction

This is a second part of an article previously released about A simple C# cache component using Redis as provider. On my first article I've created a simple client component that interacts with Redis and it implements an interface - ICacheProvider. In the article's future work I started thinking it would be great if there would be something that would allow me to decide which is my cache provider without creating another class or component such as a factory that I would need to maintain everytime I create a new cache provider just do decide which one to use.

The purpose of this article is to add another cache provider and a Dependency Injection (DI) container to build up things, by using Unity.

Background

My previous article focuses on caching, this one is more focused on the way we build components, from simpler ones to others which are more complex. There are two things that all of us developers have to deal soon or later in our companies:

  • Things change
  • How close are our components or logic in order to address changes

One thing is for sure - we don't guess what will change or how much will future impact things we have right now. But you can take this for granted: the more well built your components and your architecture, the better you will deal with change.

SOLID principles are a good reference to follow good practices and ensure code maintanibility. As consequence, you will get a well defined architecture, where a component will be just a small piece of something bigger but will have a specific and well defined purpose.

One challenge you will find in your way is decouple things and make sure they are well organized and ready for scaling as things get serious or bigger. Yet, you will not guess everything from the beginning - moving projects from a place to another, refactoring, changing namespaces - although are things that will not happen every day they will happen. Things evolve and one thing that made more sense where you had it an year ago today may make more sense to be else where. If you recall some smaller or bigger projects you've made and you think of your architecture at the first sprint and if you compare it with your last sprint before rollout... damn!!!! What a difference!!!

But let's focus on Dependency Injection. With a good sepparation of concerns, in order to ensure also a good decoupling we need 'glue' to relate things - if I have a classic 3 tier architecture, and my Business Layer (BL) needs to consume a Data Access (DA) component, the BL must know somehow about the DA component right? How should it be aware? Hard coded? What if the DA component today is Sql Server and tomorrow is anything else?

Unity of another DI container aren't the solution to ensure your architecture is well designed, secure, scalable and so on, but among other features, it allows you to implement inversion of control for resolving dependencies.

In this article we will focus on Unity, which was originally released in 2008 by Microsoft and it appears to be under OSS movement so it is supported by external contributors. We will explore this a little bit longer in the end of the article but it is no the goal, just a note. Also keep in mind I'm using Unity and there are other good DI containers out there. But this seems to me the more straight forward Microsoft-technologies oriented solution out there, and it works like a charm (at least for me).

Using the code

Let's identify what we need.

As the title refers, we will be using Unity. According to Microsoft - the 'original' owner of this component, Unity states for "a lightweight, extensible dependency injection container that supports constructor injection, property injection and method call injection".

I began using Unity a few years ago in a large project with a very low coupled architecture where things were connected with Unity. My first impression was not positive until I saw the whole picture and realized it allowed me to easily change which component or provider I wanted to use without changing any code at all.

This sounds great, but keep in mind that things must be previously done in a low coupled way. If you are reading this and you are struggling with high coupled components... I don't have good news for you, because this comes after that issue is solved (and this is not mandatory).

But let's go back to Unity. Keep in mind that it is a sepparate component that does not require Enterprise Library at all to work. So if you think you need to install several packages or several references to make it work, you don't.

In my previous article I used a cache provider named RedisCacheProvider that implemented the interface ICacheProvider. This was not made by chance, my purpose was to come back here later. Now we will:

  • Create another cache provider (other than Redis)
  • Use Unity with xml configuration in order to declare which cache provider we will use

In order to continue, you should use the code of the previous article.

In Common and UintTests projects, install the following packages (both provided by Microsoft):

  • CommonServiceLocator v1.3.0
  • Unity v4.0.1

Check your assembly references on both Common and UnitTest projects, the following assemblies should have been added:

  • Microsoft.Practices.ServiceLocation
  • Microsoft.Practices.Unity
  • Microsoft.Practices.Unity.Configuration
  • Microsoft.Practices.Unity.RegistrationByConvention

Now we are good to go. In Common project, add a new class, name it SystemWebCacheProvider and implement ICacheProvider interface. Use the following code:

C++
using System;
using System.Web;
using System.Web.Caching;

namespace Common
{
    public class SystemWebCacheProvider : ICacheProvider
    {
        HttpContext _context;

        public SystemWebCacheProvider()
        {
            _context = HttpContext.Current;
        }

        public void Set<T>(string key, T value)
        {
            this.Set(key, value, TimeSpan.Zero);
        }

        public void Set<T>(string key, T value, TimeSpan timeout)
        {
            if (_context == null)
                return;

            _context.Cache.Add(key, value, null, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
        }

        public T Get<T>(string key)
        {
            if (_context == null)
                return default(T);

            return (T)_context.Cache.Get(key);
        }

        public bool Remove(string key)
        {
            if (_context == null)
                return false;

            return (bool)_context.Cache.Remove(key);
        }

        public bool IsInCache(string key)
        {
            if (_context == null)
                return false;

            return _context.Cache.Get(key) != null;
        }
    }
}

I'm relying on System.Web.Caching cache mechanism as an alternative to Redis. Of course in my unit test project I don't have an HttpContext, but this does not keep me from going forward with this article because it's purpose is to decide in runtime which provider to use, and if I had applications that use Redis and others that use System.Web cache providers, I could choose. And I could also use both. Let's bring this up later.

Ensure that Common project compiles.

Now Unity.

Inside Unity configuration section you can declare several distinct kind of things, but for a scenario like this you need:

  • To declare an alias for each type you want to use (the type must be the full qualified name);
  • To declare as much containers as you need. In this case we only need one, named 'Common'. Unity relies on containers as a way to wrap/ organize components. In a simple architecture I would say you would have a DataAccessLayer container, a BusinessLayer container, a ServiceLayer container and so on;
  • Inside containers, you will build your dependencies, registering types, in a way you map one type to another. In a simple example, you would relate an Interface with a specific type, giving this relation a unique name. You can also have extensions, or inside registered types or relations, declare parameters for a constructor and inject it (out of scope on this article).

Let's build our configuration, your App.config should look like this:

XML
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="RedisConfiguration" type="Configuration.RedisConfigurationSection, Configuration, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
  </configSections>
  <RedisConfiguration host="localhost" port="6379"/>
  <unity>
    <alias alias="ICacheProvider" type="Common.ICacheProvider, Common, Version=1.0.0.0, PublicToken=null"/>
    <alias alias="RedisCacheProvider" type="Common.RedisCacheProvider, Common, Version=1.0.0.0, PublicToken=null"/>
    <alias alias="SystemWebCacheProvider" type="Common.SystemWebCacheProvider, Common, Version=1.0.0.0, PublicToken=null"/>
    <containers>
      <container name="Common">
        <register type="ICacheProvider" mapTo="SystemWebCacheProvider"/>
      </container>
    </containers>
  </unity>
</configuration>

Notice that inside unity section, first we have alias. By providing a fully qualified type you can give it an alias, which will be used inside containers instead of the full qualified name. To keep it simple, I named all alias with the class name.

Then you have containers, with a 'Common' container which is all we need for this sample. The way we relate things is registering a type and then map it into another type, naming the relation. In my case, I'm stating that my cache provider will be SystemWebCacheProvider.

Now, how does this work? The only thing I know is that my cache component decision is handled by Unity and it is of type ICacheProvider. I don't know to which type it will be maped until I run some sort of code to interact with Unity.

To make this work, you need to load Unity Container(s) and then with the container resolve the type you need.

Let's go back to Common project, add a new class named ServiceLocator, it should look like this:

C++
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using System;
using System.Collections.Generic;

namespace Common
{
    public class ServiceLocator
    {
        private static Dictionary<string, UnityContainer> containers = new Dictionary<string, UnityContainer>();

        private static UnityContainer ResolveContainer(string containerName)
        {
            return new UnityContainer().LoadConfiguration(containerName) as UnityContainer;
        }

        private static UnityContainer GetContainer(string containerName)
        {
            try
            {
                if (!containers.ContainsKey(containerName))
                {
                    lock (containers)
                    {
                        if (!containers.ContainsKey(containerName))
                            containers.Add(containerName, ResolveContainer(containerName));
                        else
                        {
                            if (containers[containerName] == null)
                                containers[containerName] = ResolveContainer(containerName);
                        }
                    }
                }
                else
                {
                    if (containers[containerName] == null)
                    {
                        lock (containers)
                        {
                            if (containers[containerName] == null)
                                containers[containerName] = ResolveContainer(containerName);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw;
            }

            return containers[containerName];
        }

        public static T Resolve<T>(string containerName)
        {
            return (T)GetContainer(containerName).Resolve<T>();
        }

        public static T Resolve<T>(string containerName, string name)
        {
            return (T)GetContainer(containerName).Resolve<T>(name);
        }

    }
}

In order to avoid loading configuration everytime I want to resolve a dependecy, I've loaded the configuration into a static property, so each time we want to resolve anything it won't need to load it again (which would be a bad performance issue). I could also save the configuration in cache, but let's keep it simple again.

The 'Resolve' method is what we will consume from the outside. You've may noticed that there is an overload with the parameter 'Name'. This can happen if there is more than one mapping for the same type. Let's discuss this later.

Now, I have SystemWebCacheProvider configured as my cache provider.

Let's go to CacheTests class inside UnitTest project, and ensure code looks as the following:

C++
using Common;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;

namespace UnitTests
{
    [TestClass]
    public class CacheTests
    {
        private string unityContainerName = "Common";

        ICacheProvider _cacheProvider;

        [TestInitialize]
        public void Initialize()
        {
            //_cacheProvider = new RedisCacheProvider();

            _cacheProvider = ServiceLocator.Resolve<ICacheProvider>(unityContainerName);

            Console.WriteLine(_cacheProvider.GetType().Name);
        }

        [TestMethod]
        public void Test_SetValue()
        {
            List<Person> people = new List<Person>()
            {
                new Person(1, "Joe", new List<Contact>()
                {
                    new Contact("1", "123456789"),
                    new Contact("2", "234567890")
                })
            };

            _cacheProvider.Set("People", people);
        }

        [TestMethod]
        public void Test_GetValue()
        {
            var contacts = _cacheProvider.Get<List<Contact>>("People");

            Assert.IsNotNull(contacts);
            Assert.AreEqual(2, contacts.Count);
        }
    }
}

You will notice that I've commented the line of code that decided manually that my cache provider would be RedisCacheProvider. Instead, I've used my ServiceLocator Resolve method, providing my interface type, the unity container name and the name of the relation to resolve.

If you run Test_SetValue and check the output it should give you 'SystemWebCacheProvider.'

Now go to your App.config file, and change the 'mapTo' attribute of the relation to 'RedisCacheProvider' and run Test_SetValue again. It should give you 'RedisCacheProvider'.

This is how Unity makes it happen.

What about having both cache providers working side by side with Unity?

Let's explore this scenario, where Redis would be the global cache provider, and System.Web would be the local cache provider, and it would be decided that global objects stored in cache would be stored in Redis, and local objects would be stored in System.Web for a specific Web application.

We would need to change our configuration to look like this:

XML
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="RedisConfiguration" type="Configuration.RedisConfigurationSection, Configuration, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
  </configSections>
  <RedisConfiguration host="localhost" port="6379"/>
  <unity>
    <alias alias="ICacheProvider" type="Common.ICacheProvider, Common, Version=1.0.0.0, PublicToken=null"/>
    <alias alias="RedisCacheProvider" type="Common.RedisCacheProvider, Common, Version=1.0.0.0, PublicToken=null"/>
    <alias alias="SystemWebCacheProvider" type="Common.SystemWebCacheProvider, Common, Version=1.0.0.0, PublicToken=null"/>
    <containers>
      <container name="Common">
        <register type="ICacheProvider" mapTo="SystemWebCacheProvider" name="Local"/>
        <register type="ICacheProvider" mapTo="RedisCacheProvider" name="Global"/>
      </container>
    </containers>
  </unity>
</configuration>

Now, if you decide to run the unit test Test_SetValue you will get an ugly error from Unity - "Initialization method UnitTests.CacheTests.Initialize threw exception. Microsoft.Practices.Unity.ResolutionFailedException: Microsoft.Practices.Unity.ResolutionFailedException: Resolution of the dependency failed, type = "Common.ICacheProvider", name = "(none)".
Exception occurred while: while resolving.
Exception is: InvalidOperationException - The current type, Common.ICacheProvider, is an interface and cannot be constructed. Are you missing a type mapping?"

What you need to do is change the way you resolve your dependency by providing the name.

If you pass the parameter "Global", you will get Redis, but if you pass the parameter "Local" you will get "SystemWeb".

Now imagine you are in a windows service where is no System.Web caching and you want to use your own custom in-memory mechanism to store local data. You could write your in memory cache provider, implementing ICacheProvider interface, add its alias on Unity and the resolve it registering the type ICacheProvider mapping into your InMemoryCacheProvider. If you name it 'Local' everything is done, just like that!

Points of Interest

As I told before, my first impression on this was not the best, because it takes a few time to understand how to build a set of components with Unity. Troubleshooting at the beginning is not great, because when we are debugging and we hit 'Resolve' call, the only thing we get is an exception, because we don't control what happens inside Unity 'core'. But as time passes and you begin to see your unitary components totally decoupled and related with a simple thing as an xml configuration you begin to feel something as if you can't live without this anymore (just kidding!).

Please notice that I'm not defending you should design or re-design your architecture in order to be able to use Unity or other DI container. I've seen other architectures without this on them and still work like a charm.

But one thing is for sure - large companies with many people working on same projects, usually we get many and many code flavours. Now picture a full architecture resolving dependencies decisions with something like this, you only have one chance - or you get it right, or you will get it wrong. When it comes to determining dependencies, there won't be no flavours at all, and I think the major advantage of Unity even above the fact it is a great DI container.

Future work:

  • Use AOP in order to automatically set/get data automatically into/from cache.

To be continued!

Unity support - as mentioned at the beginning of the article, Unity is supported now by external contributors, instead of the 'original' Patterns & Practices. If you check this post you will get further explanation and you will see a few comments with questions about the future of this project (the last one that appears there today is actually mine). But if you look into Nuget package you will see that the last release was on October 2015, which seems to me to be up to date.

At least for me I don't see need for more than what it does today and what it does, works fine (at least for me). But I felt in the obligation to share this information with you and let it up to you to use ir or not. For what is worth, I'll keep on using it, and if someday I change, since the architecture is low coupled, I believe it won't be a big trouble.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Portugal Portugal
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --