November Happy Hour will be moved to Thursday December 5th.

Module dependency and ServiceConfiguration

Vote:
 

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?

#120340
Apr 15, 2015 13:37
Vote:
 

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
#120348
Apr 15, 2015 14:19
Vote:
 

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.

#120361
Apr 15, 2015 16:28
Vote:
 

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?

#120362
Apr 15, 2015 16:43
Vote:
 

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.

#120364
Edited, Apr 15, 2015 17:37
Vote:
 

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();
    }
}
#120369
Apr 15, 2015 21:42
Vote:
 

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.

#120402
Edited, Apr 16, 2015 9:47
Vote:
 

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.

#120406
Apr 16, 2015 10:48
Vote:
 

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. 

#120430
Apr 16, 2015 16:46
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.