Estimated reading time:
Recently there was this blog post about 9 Grievances that make Sitecore development a challenge, and while I agree at times,
Sitecore development can be a challenge (although, isn’t that really why we are developers - because of the challenge?), I wanted to address one of the grievances and how to solve that without too much effort.
One of the greivances is that there is no concept of a “Page Object” that you can use to store something. For example, a dashboard that pulls in user information on 3 renderings, because of the way that Sitecore works, each controller action will get that data. So you end up getting the data 3 times. The common answer there would be to cache the data, but that isn’t always the best option and depending on how your cache is invalidated, could lead to stale data being presented on the site.
The thing is that if you follow some good development practices, this perceived problem doesn’t have to be a problem at all. Let’s take the example of a dashboard with some user information that we are getting from a CRM. For the purposes here, we assume that the repository to call the CRM is written and called
The first thing to make sure is that we are following good practice and not doing everything in the controller. So we would have a service class that is responsible for getting user CRM data - lets call that
IUserCRMService. The interface might look like this:
What about the implementation, well this can have a dependency on the
ICRMRepository and use that to get the data for the user. But instead of just using
IUserCRMService as a stateless object, lets add some state to it. Lets add the user object to the service:
Now we can implement that:
So now we can use the
SetUserId to tell the service which user we are getting and then use the
User property to get the data. Caveat - this is not supposed to be technically perfect and I’m sure there are neater ways to setup the stateful class, this is just to convey the idea!
The magic happens when we register the implementation with our container. For this I’ll use Sitecore’s builtin IoC services:
Instead of the usual
Transient registration, we are using
Scoped. This means it is registered as a singleton per web request. So now we can inject that dependency (
IUserCRMService) into as many controllers as we want and be sure that the CRM repository is only called once.
Ah - but what about race conditions? What if a second controller calls the service before the first one finishes? Well that shouldn’t happen in Sitecore, controllers are not called asynchronusly. But we should is never a guarantee! So lets code against that.
Because we are a singleton per web request, we can safely use a lock here and not have to worry about requests blocking other requests coming in. So lets see the updated implementation of
Now if multiple controllers try to set the user Id at the same time, the first will lock and the others will wait until it is complete. The others will then see the
User property populated and use that rather than calling the
I’m sure this is only one option of many to solve this problem, but its one that I have used a number of times and had great results with. Feel free to post comments on how you solve this below!