Hi!
You can specify which dependencies should be run before your class by defining the ModuleDependency attribute. For instance, the CMS UI takes a dependency to the CMS core/web parts:
[ModuleDependency(typeof(EPiServer.Shell.UI.InitializationModule))] [ModuleDependency(typeof(EPiServer.Web.InitializationModule))] public class InitializableModule : IInitializableModule
Hi Linus!
Has all services been scanned if I depend on the modules you list? I'm well aware of the ModuleDependencyAttribute but I don't know in which module EPiServer scans for the ServiceConfigurationAttribute.
As far as I understand, ServiceConfigurationAttribute tells StructureMap that the attribute decorated class should be inserted in StructureMaps container.
I'm pretty sure that all scanning is done before the initialization system kicks in since pretty much all EPiServers own Initialization classes depends on the scanning being run before they do their stuff. Is this a theoretical question or are you seing issues about things not being scanned at initialization?
Yeah, I'm having some issues.
I'm using AutoMapper in my project and I try to create my AutoMapper profiles using StructureMap, so that I can utilize dependency injection. A profile is simply a class that inherits an abstract class called Profile. The class contains the mapping configuration. The relevant information is that I need to instantiate this profile/class in my module.
In my profile I inject IContentLoader and IPriceService.
For IContentLoader, StructureMap says that it can't find a default implementation for LanguageResolver. The implementation for LanguageResolver is LanguageResolverImplementation and it is registered through the ServiceConfigurationAttribute.
For IPriceService, StructureMap says that it can't find a default implementation for ISynchronizedObjectInstanceCache. The implementation for ISynchronizedObjectInstanceCache is RemoteCacheSynchronization and it is also registered through the ServiceConfigurationAttribute.
This combined leads me to think that EPi has not yet performed scanning before my module runs.
By the way, my module depends on a module that depends on CommerceInitialization.
What is the content of `content`?
[InitializableModule] [ModuleDependency(typeof(CommerceInitialization))] public class TheModuleInitialization : IConfigurableModule { public void Initialize(InitializationEngine context) { } public void Uninitialize(InitializationEngine context) { } public void Preload(string[] parameters) { } public void ConfigureContainer(ServiceConfigurationContext context) { var content = context.Container.WhatDoIHave(); } }
Hi Valdis!
So actually StructureMap has both LanguageResolver and ISynchronizedObjectInstanceCache. It seems though that ServiceLocator.Current don't have them!
I can do this:
context.Container.GetInstance<ISynchronizedObjectInstanceCache>()
... and get the correct LanguageResolverImplementation. But I can't do:
context.Container.GetInstance<IPriceService>()
If I dig into the implementation of IPriceService, I find that its implementation calls ServiceLocator.Current.GetInstance<ISynchronizedObjectInstanceCache>() in its constructor:
public PriceServiceDatabase(ICatalogSystem catalogSystem, IChangeNotificationManager changeManager) : this(catalogSystem, changeManager, ServiceLocator.Current.GetInstance<ISynchronizedObjectInstanceCache>()) { }
So I try to run this:
ServiceLocator.Current.GetInstance<ISynchronizedObjectInstanceCache>()
And it's actually here I run into the problem. It's EPiServers service locator than can't resolve my profiles dependencies.
Here's a summary of what I can and can't do in my module. (You'll also see what solved it)
var cacheObj = ServiceLocator.Current.GetInstance<ISynchronizedObjectInstanceCache>(); //doesnt work var languageResolver = ServiceLocator.Current.GetInstance<LanguageResolver>(); //doesnt work var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>(); //doesnt work var priceService = ServiceLocator.Current.GetInstance<IPriceService>(); //doesnt work cacheObj = context.Container.GetInstance<ISynchronizedObjectInstanceCache>(); // works languageResolver = context.Container.GetInstance<LanguageResolver>(); // works contentLoader = context.Container.GetInstance<IContentLoader>(); //doesnt work priceService = context.Container.GetInstance<IPriceService>(); //doesnt work ServiceLocator.SetLocator(context.Container.GetInstance<IServiceLocator>()); cacheObj = ServiceLocator.Current.GetInstance<ISynchronizedObjectInstanceCache>(); // works languageResolver = ServiceLocator.Current.GetInstance<LanguageResolver>(); // works contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>(); // works priceService = ServiceLocator.Current.GetInstance<IPriceService>(); // works cacheObj = context.Container.GetInstance<ISynchronizedObjectInstanceCache>(); // works languageResolver = context.Container.GetInstance<LanguageResolver>(); // works contentLoader = context.Container.GetInstance<IContentLoader>(); // works priceService = context.Container.GetInstance<IPriceService>(); // works
So, to sum it up, re-setting the service locator solved my problem.
Just curious, if you move those calls from service locator that do not work in init module further down in request pipeline - call them from let's say page controller. Is it able to resolve them then? I'm still speculating that it *may* be dependency chain issue, but not sure yet.
ServiceLocator.Current is not ready for use until all IConfigurableModule instances have executed. Then the continer from the ServiceConfigurationContext is assigned to ServiceLocator.Current.
Meaning you should not call ServiceLocator.Current inside a IConfigurableModule.ConfigureContainer call.
Hello!
I'm setting up a IConfigurableModule and I have a question about module dependency. My module depends on EPi to have scanned all services that are registered through the ServiceConfigurationAttribute. How can I assert that my module runs after this has been done?