In a recent Glass release I was working on an issue that led to Mike making a comment on how I work as a programmer. The particular issue in question was actually quite straight forward to solve – I wrote unit tests that showed the issue, wrote some sandbox tests that showed how code behaved in the underlying Sitecore library, then fixed the tests approriately. All typical of TDD. The normal thing for me though is then to write a unit test that performs this simple fix 1,000,000 times emitting the result of stopwatch timings at various points. Mike jokingly commented at the time that this was ‘typical Nat’ which, to be fair, he was right on.
In this post I will look at the effect on work done in the constructor and how it has affects your development life in the context of modern DI practice – in particular – the DI principle.
During this same period I also had read a great blog post entitled Dependency-injection anti-pattern: Injecting runtime data into components (follow @dotnetjunkie on twitter – he is a wealth of information on DI). It got me thinking about how I use Glass Mapper in my solutions, and whether in fact I should be wrapping ISitecoreContext in a provider to prevent its overuse and allow for lazy loading.
Whilst technically not quite the same as the post from Steven, it did lead me to consider a similar principle. The data it provides is contextual based upon when / where it is accessed. I also considered that since the context has to do a bit of work to figure this out, any methods that do not use it – for example maybe form post handlers that might redirect the user on successful completion, don’t actually need to do this work. CUE THE TESTS!
It is worth noting that firstly – SitecoreContext should be in production resolved on a per web request basis, and will therefore be used at least once. This would cover the majority of use cases and makes these tests unrealistic in many real world scenario’s for Glass specifically.
These tests were also performed on a client implementation complete with customisations. I have repeated the same tests on Glass without this and it takes 1/20th of the times shown
I quickly set about writing some tests to show what happens if I put a Glass Mapper based ‘SitecoreContextProvider’ in as a dependency vs depending the ISitecoreContext itself. The tests were designed (using xUnit) to provide 3 tests passes instantiating my dependent class 1000 times each – that’s actually quite a small number considering the amount you would probably use the Glass ISitecoreContext during a page lifecycle, and the amount of traffic you might need to use. I also made it access the context 50% of the time. The gist can be found here https://gist.github.com/cardinal252/43e3eef5d472ce1c68e5.
The results of this were a little surprising:
Generating class with direct dependency on ISitecoreContextProvider
Pass 1: 9240ms
Pass 2: 6768ms
Pass 3: 6648ms
Generating class with direct dependency on ISitecoreContext
Pass 1: 21123ms
Pass 2: 12606ms
Pass 3: 15225ms
Use of providers to alleviate the issue
In general, I would recommend that fixing the culprit code should be your first choice. However, in the modern development era, it is commonplace to use third party libraries to achieve faster results with less development effort. The addition of a provider to enable lazy loading of these components specifically for use with DI seems a very easy and logical approach to this issue (if not the perfect world scenario).
Even a product with the wide code coverage and testing that it has shows that we can still fall foul of this issue.
The chances are following this in future releases of Glass, some work will be done to change the behaviour of the SitecoreContext object or a formal ContextProvider being introduced. But – in reality, this situation can be faced by developers using any third party library and should be considered when you instantiate your own code. My obsession with writing unit tests to at least perform basic stress tests on our codebase really begins to highlight scenarios like the one above.
If you are using Glass currently in this manner – I would consider this approach, but only if you are limited in your upgrade path / speed (for which we do provide consultancy & support offerings)
For the reasons above – my crazy ‘lets instantiate it 1000(0000) times’ tests actually highlight why work done in the constructor can start to bite you in the ass when you attempt to scale your code, ESPECIALLY when you look at the widespread use / over use and abuse of DI containers (and worse still – the service locator pattern) in the wild. The gist I have created would probably in the wild be more like only a 20% difference – but that’s still 4 seconds of wasted clock time per 3000 uses of the object. Given Sitecore’s nature and use, 3000 uses could easily be achieved in just 1000 requests!!