Optimizely

Optimizely CMS 12 – Dependency Injection(DI) – StructureMap vs ASP.Net Core

Person working at a whiteboard

If you are upgrading to Optimizely CMS 12, one of the breaking changes to consider is the change to the Dependency Injection(DI) framework. Earlier versions of Optimizely CMS had their own DI hosting framework that supported other concrete DI implementations, like StructureMap. With CMS 12 and ASP.Net Core, DI framework is built into the system. We can still register and use other third party implementations, but the recommendation is to use the built in DI framework.

So how do we upgrade an existing CMS 11 site, that extensively used StructureMap for DI, to CMS 12 and the built in DI framework, considering StructureMap is being sunsetted?

Lets look at a few use cases :

1. Concrete implementation registration

With Optimizely CMS and its DI framework, we only registered interface implementations and not concrete classes. But with ASP.Net Core’s DI, we need to register concrete classes as well. For example, if we have a class like

Loggingconfig Class

We’ll need to register this explicitly in our IOC container :

Loggingconfig Reg

2. Delegate Registration

If a concrete implementation is injected as a delegate, then along with above, its delegate needs to be registered too :

Delegatereg

3. Passing Constructor Parameters with DI

Sometimes we have classes with constructors where they inject other services for use later in the logic. Now when we register these classes in our IOC container, just registering the names won’t do. Because as soon as those references will need to be created, the compiler will look for the injected classes on the constructor and won’t be able to resolve those. In CMS 11 implementation, StructureMap aided with these. Lets see with an example :

Lets say we have a custom ILoggerFactory implementation setup like this :

Loggerfactory

This is injecting the LoggingConfiguration class from above in the constructor. With StructureMap, we were supplying the Type parameter to this constructor like this :

Loggerfactory Reg Strucmap

With ASP.Net Core’s DI, this would be achievable by doing something like this :

Loggerfactory Reg Newdi

Some interesting facts to note in the new DI registration :

  • We are using a namespace called Microsoft.Extensions.DepenedencyInjection. This allows us to use System.IServiceProvider object to obtain previously registered services via the GetRequiredService() call and use them for other service registrations. In our example above, we use this to supply LoggingConfiguration object to the LoggerFactory constructor.
  • The other is the use of Extensions. As you note the highlighted epiServices above, instead of registering directly on context.Services, we are registering on a ServiceCollectionExtension instead. Reason for doing this was because the Add<Lifetime> calls on context.Services is ambiguous between Microsoft.Extenesions.DependencyInjection and Episerver.ServiceLocation and there is no way to distinguish between those on direct calls.

So now the above logic lets us register a service with specific lifetime and allows us to pass constructor parameters to it.

4. Registering multiple implementations of same type

Sometimes our logic requires one interface that can be implemented multiple different ways for use in different scenarios. Registering such services requires additional logic as if we simply use the standard approach and register all classes against the same interface, only the last one actually gets picked every time the interface is referenced, thus not being quite effective. Lets understand with an example :

Scenario 1 : normal interface with multiple implementations 

Multi Interface

StructureMap way of registration :

Multi Interface Reg Strucmap

.Net Core DI equivalent :

Multi Int Ref New

Scenario 2 : generic interface with multiple implementations and generic attribute types

StructureMap way :

Generic Int Reg Strucmap

ASP.Net Core DI equivalent :

Generic Int Reg New

We can do this the way Scenario 1 is done too, but that works for open generic types or closed generic types with fixed Argument types. I had scenarios where my generic type arguments were also decided at runtime for different implementations, so this is what worked for them.

5. Named Registrations

Lets consider the example of Event based logic. We have different events, even in CMS context, and there are cases when we need to take different actions per event under a common context, like Indexing or Search. So how to register multiple event based implementations of a single interface so the correct service can be used for the corresponding event?

We use named registration concept here. It ties service registrations to names, in this case event names and uses that to map the right call later on.

Here’s the way StructureMap enabled us to achieve this :

Named Reg Strucmap

And here’s its ASP.Net Core DI equivalent :

Named Reg New

You basically register all the concrete implementations first and then map the interface implementation to the right one based on the key, which in our case is the event name.

This may not be the complete list and these are not the only ways of achieving the end result. I myself encountered different approaches on different forums and posts and I can say that its definitely a fair bit of trial and error, for those of us not too experienced with ASP.Net Core way of doing things. I had to look through several different places to help translate each individual use case, so hoping this one stop guide comes in handy for others on the same path.

 

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Ritu Madan, Lead Technical Consultant

Ritu Madan is an Optimizely CMS and Commerce certified developer with over 15 years of experience working with .net and SQL. She enjoys exploring and keeping up with the latest and greatest in technology, online/offline tools that help with day-to-day work and share that knowledge.

More from this Author

Subscribe to the Weekly Blog Digest:

Sign Up
Follow Us
TwitterLinkedinFacebookYoutubeInstagram