Issue using StructureMap Nested Containers in Episerver

Vote:
 

I'm wondering if anyone has tried using the Nested Container feature from StructureMap in an Episerver website.

StructureMap recommends using nested containers to ensure object lifecycles are scoped to an http request, instead of using some of the legacy lifecycles such as HybridHttpOrThreadLocalScoped.

I tried to implement this in the Alloy sample website using the latest StructureMap.MVC5 nuget package and integrate it with the Container provided through the ConfigureContainer method in an IConfigurableModule module. The website seems to work fine until I hit one of the landing pages where I get this error. I suspect it is failing over when there are child controllers involved in the request pipeline.

Any thoughts?

#174097
Jan 18, 2017 16:11
Vote:
 

We're using it sucessfully. Are you sure the error is related? We're not using the integration package for mvc, this is how we usually set it up:

namespace EV
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web.Mvc;
    using StructureMap;

    public class StructureMapDependencyResolver : IDependencyResolver
    {
        private readonly IContainer container;

        public StructureMapDependencyResolver(IContainer container)
        {
            this.container = container;
        }

        public object GetService(Type serviceType)
        {
            if (serviceType.IsInterface || serviceType.IsAbstract)
            {
                return this.container.TryGetInstance(serviceType);
            }
            else
            {
                try
                {
                    // Can't use TryGetInstance here because it won’t create concrete types
                    return this.container.GetInstance(serviceType);
                }
                catch (StructureMapException)
                {
                    return null;
                }
            }
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            return this.container.GetAllInstances(serviceType).Cast<object>();
        }
    }
}
namespace DV
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web.Http.Dependencies;
    using StructureMap;

    public class StructureMapWebApiDependencyResolver : IDependencyResolver
    {
        private readonly IContainer container;

        public StructureMapWebApiDependencyResolver(IContainer container)
        {
            this.container = container;
        }

        public IDependencyScope BeginScope()
        {
            var childContainer = this.container.GetNestedContainer();

            return new StructureMapWebApiDependencyResolver(childContainer);
        }

        public object GetService(Type serviceType)
        {
            if (serviceType.IsInterface || serviceType.IsAbstract)
            {
                return this.container.TryGetInstance(serviceType);
            }
            else
            {
                try
                {
                    // Can't use TryGetInstance here because it won’t create concrete types
                    return this.container.GetInstance(serviceType);
                }
                catch (StructureMapException)
                {
                    return null;
                }
            }
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            return this.container.GetAllInstances(serviceType).Cast<object>();
        }

        public void Dispose()
        {
            this.container.Dispose();
        }
    }
}

And then in an module

namespace DV
{
    using System.Web.Http;
    using System.Web.Mvc;
    using EPiServer.Framework;
    using EPiServer.Framework.Initialization;
    using EPiServer.ServiceLocation;

    [InitializableModule]
    [ModuleDependency(typeof(ServiceContainerInitialization))]
    public class ContainerConfigurableModule : IConfigurableModule
    {
        public void ConfigureContainer(ServiceConfigurationContext context)
        {
            // Configure for MVC
            DependencyResolver.SetResolver(new StructureMapDependencyResolver(context.Container));

            // Configure for Web API
            GlobalConfiguration.Configuration.DependencyResolver = new StructureMapWebApiDependencyResolver(context.Container);
        }

        /// <summary>
        ///     Exists because IConfigurableModule, not used at present time
        /// </summary>
        /// <param name="context">the InitializationEngine context</param>
        public void Initialize(InitializationEngine context)
        {
        }

        /// <summary>
        ///     Exists because IConfigurableModule, not used at present time
        /// </summary>
        /// <param name="context">the InitializationEngine context</param>
        public void Uninitialize(InitializationEngine context)
        {
        }
    }
}
#174100
Jan 18, 2017 17:57
Vote:
 

I.e. web api is using nested containers at least.

#174101
Jan 18, 2017 17:58
Vote:
 

Hey Johan, it's ASP.NET MVC that I'm after and which doesn't seem to work. The nested container approach works fine within WebAPI.

The nested container being created within

System.Web.Http.Dependencies.IDependencyResolver.BeginScope()

in your example only gets used by WebAPI. The other System.Web.Mvc.IDependencyResolver which gets passed to MVC uses the parent container from the context (ServiceConfigurationContext).

#174102
Jan 18, 2017 18:57
Vote:
 

I'm actually very close to releasing a package on NuGet that can do it for MVC and WebAPI. The source is open on github if you want to try before then.

Here is the link to the docs: https://bmcdavid.github.io/DotNetStarter/.

The examples show how to use the Episerver container. Feel free to give it a try if you like.

-Brad

#174103
Jan 18, 2017 19:08
Vote:
 

Nice! I'll check it out :)

I still think Episerver should be supporting nested containers for MVC out of the box though...

#174118
Jan 19, 2017 10:19
Vote:
 

Here is a stripped down Gist that does what you need as well with the other stuff in the package.

https://gist.github.com/bmcdavid/ddceeb01cbddb3d8b62f70b5ffd4cf53

-Brad

#174146
Jan 19, 2017 14:30
Vote:
 

Hey Tamim

Did yo manage to get it working? I tried it about a year - year and a half ago and could not get it working.

/David

#175570
Feb 23, 2017 9:35
Vote:
 

Hey, I checked with Episerver who basically said they don't support nested containers. So one option is to use child containers instead - this is what Brad (see previously reply) has used in his solution.

I worked around the issue by only resolving my application specific dependencies through the nested container, essentially excluding all Episerver services, which seems to work.

#176016
Mar 07, 2017 17:43
* 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.