It seems like you're using an older browser. Things might not work as expected.

If you run Avensia Storefront as-is you don’t have to think about dependency management. It’s only when you want to create your own extensions that you need to consider how they should manage their dependencies.

Let’s start with an example! Let’s say that we want to create a custom patcher, ExamplePatcher. The patcher will need to call an external service over http. We want to keep the patcher code “clean” so we don’t want to handle the http calls from the patcher. We decide to implement a separate service, ExampleRatingService, for it. Now we have a patcher and a service and we need to consider the “lifecycle” of each. To be able to do this we need to understand the dependency model in Avensia Storefront.

Avensia Storefront’s Connector uses StructureMap, the same dependency injection framework that Episerver uses by default. We try to limit the dependencies in the “main”, global, container since Episerver uses it throughout the site. We do it by creating nested containers. They have access to the parent container’s dependencies, but we can also register new dependencies in them. The new dependencies are never shared with the parent container, only with child containers. This means that we don’t have to worry that our registrations will affect the global container. We can throw away the nested containers when we’re done with them. It looks something like this:

  1. Global container – main container for Episerver. We only register the bare minimum to start the integration engine, log and report information to Episerver. If you register something in the global container it will live for as long as Episerver runs. You should be careful when registering dependencies in the global container. You might affect the rest of the site’s performance. Conceptually a job should be able to run in isolation in most cases
  2. Module container – container for a module/job ex catalog import job. If you register something in a module container it will live for as long as that specific job runs. This is the go-to place for most dependency registrations since the dependencies will only live for as long as the job runs, and since the dependencies cannot affect the global container. It is usually a sweet spot since it’s “cheap” to register something once per job compared to once per task and it’s safer than registering on the global container. It also allows you to share state between tasks.
  3. Task container – container for a task ex product pipeline task. If you register something in a task container it will live for as long as the specific task runs. You can use it to register dependencies that needs to be unique for every single task. We use it for our pipeline steps. There can only be on IReader in each task for example

If we assume that ExampleRatingService has a minimal state, ex a connection context, or a state that can be shared between tasks, ex a rating score cache, it would be best to register it in a module container. If we assume that its state needs to be unique for each task, ex a list of processed items that needs to be empty before each run, then it would be best to register it in a task container.

When we have decided where to register our dependencies we must know how. Registrations in the global and in module containers are done in the same way. Registrations in task containers are done separately.
Global and module registrations are done in two steps. First you implement the interface IProjectDependencyRegistration. It has two methods, one for global registrations, RegisterDependenciesInGlobalContainer, and one for module registrations, RegisterDependenciesInModuleContainer. Then you register the IProjectDependencyRegistrationin Bootstrapper.cs. In Avensia Storefront Starter you will already have an implementation of IProjectDependencyRegistration called ShopDependencyRegistration that you can reuse. If we’re doing it from scratch it should look like this:

public class ExampleDependencyRegistration : IProjectDependencyRegistration
{
    public void RegisterDependenciesInGlobalContainer(ConfigurationExpression configuration)
    {
    }

    public void RegisterDependenciesInModuleContainer(RegistrationContext context)
    {
        context.ConfigurationExpression.For().Use();
    }
}

And in Bootstrapper.cs

protected override void RegisterProjectDependencies(ProjectDependencyRegistrations projectDependency)
{
    // Default registrations
    projectDependency.AddRegistration(new Connector.D36580.ProjectDependencyRegistration());
    projectDependency.AddRegistration(new Connector.Epi.ProjectDependencyRegistration());
    projectDependency.AddRegistration(new Connector.ProjectDependencyRegistration());
    projectDependency.AddRegistration(new ShopDependencyRegistration());

    // Our registration
    projectDependency.AddRegistration(new ExampleDependencyRegistration());
}

If we want to register our dependencies directly in a task container we must choose which task. We also need to add our registrations directly to the main registrations. We do it through a method called AddConfguration. It works similar to UseReaderUseWriter etc. The main difference is that we have direct access to the container through StructureMap’s api. The registrations should look like this:

m.AddMultiLanguagePipelineTask<ProductDto, ProductToSave<WebProduct, WebVariant>>(
t =>
{
    // Other registrations
    t.Name("Product");
    t.UseReader<ProductReader>();
    t.UseTransformer<ProductTransformer<ProductDto, WebProduct, WebVariant>>();
    t.UseWriter<ProductWriter<ProductDto, WebProduct, WebVariant>>();

    // Other registrations
    t.AddConfiguration(configuration => configuration.For().Use());
});