Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Orchard CMS, is it possible to register IEventHandler implementations in modules to be called later than those that reside in Orchard.Framework?

I am writing a module for Orchard CMS that contains an event handler class that implements the IOrchardShellEvents interface.

When a stock implementation of Orchard is set up using the Core recipe, and I enable my module, there are just three active implementations of IOrchardShellEvents. By default they are called in this order:

  1. My custom event handler (in my custom module)
  2. AliasHolderUpdater (from the Orchard.Alias module)
  3. AutomaticDataMigrations (from Orchard.Framework).

I would like to set these up so that when the applicable event is fired, my class is called later than AutomaticDataMigrations. I've tried playing with both the Dependencies and Priority fields in the Modules.txt of my module, but I cannot get my class to run later than AutomaticDataMigrations.

Things I have tried:

  • I have tried adding Dependencies: Orchard.Framework to my module's Module.txt. This seems to do nothing. However, when I add Dependencies: Orchard.Alias to Module.txt, my class is indeed called later than AliasHolderUpdater. It just does not work for AutomaticDataMigrations, which is part of Orchard.Framework.

  • I have tried adding Priority: 1 to my module's Module.txt. This does indeed cause my class to get called later than other IOrchardShellEvents implementations that exist in modules, such as AliasHolderUpdater. However, even then it still calls AutomaticDataMigrations last.

Looking at the ExtensionManager.AvailableFeatures method, it can be seen that all features are loaded in order of dependency and priority. This means that the actual IDependency objects from each module are registered with AutoFac in this order.

(Details can be seen in DependencyOrdering.OrderByDependenciesAndPriorities, CompositionStrategy.Compose, and ShellContainerFactory.CreateContainer methods)

I do not see a way to control the order of registration of classes that reside in modules relative to those that reside in Orchard.Framework, which is not an "Orchard Module" and does not follow the normal rules of module loading.

Because the purpose of AutomaticDataMigrations is to ensure that the latest database migrations have been run, I would like for my class to be called later than it, so that when my event handler runs I can be sure it can make use of the database tables set up by the migrations.

How can I register my class to run later than AutomaticDataMigrations? Or, will this require modification of Orchard itself?

like image 237
Katsuyuki Omuro Avatar asked Nov 11 '22 19:11

Katsuyuki Omuro


1 Answers

I think I have found the answer to this problem and I believe it's actually a bug in Orchard.

Orchard.Environment.ShellBuilders.CompositionStrategy contains the following code:

    var enabledFeatures = _extensionManager.EnabledFeatures(descriptor);
    var features = _extensionManager.LoadFeatures(enabledFeatures);

    if (descriptor.Features.Any(feature => feature.Name == "Orchard.Framework"))
        features = features.Concat(BuiltinFeatures());

Feature descriptors are returned in "dependency" order from EnabledFeatures() -- items earlier in the list should not depend on items later in the list. This order is used when eventually building the dependency container.

Because Orchard.Framework should not depend on anything, and everything can depend on Orchard.Framework, this should come first ... and the code should look like this (only the last line here has been modified):

    var enabledFeatures = _extensionManager.EnabledFeatures(descriptor);
    var features = _extensionManager.LoadFeatures(enabledFeatures);

    if (descriptor.Features.Any(feature => feature.Name == "Orchard.Framework"))
        features = BuiltinFeatures().Concat(features);

This way, when dependencies are injected as part of a list (such as event handlers), built-in features should come first, since they have no dependencies.

This resolves my issue because I can rely on Orchard.Framework-provided IOrcharShellEvents implementations to be called before any provided by custom modules.

I have filed the above as a suggested fix in the Orchard issue tracker: http://orchard.codeplex.com/workitem/20286

like image 55
Katsuyuki Omuro Avatar answered Nov 15 '22 13:11

Katsuyuki Omuro