Dependency injection in controller classes

Vote:
 

I would like a parameterless constructor on my mvc controller class to automatically recieve an IContentRepository implementation. How would I go about setting up the dependency resolving in application startup?

My controller works well if I fetch the content repository implementation by going through ServiceLocator.Current.GetInstance<IContentRepository>(), but I would really like to get it injected via the constructor.

In addition it would be cool if I could register my own rules for resolving my IAwesomeness interface to an AwesomeImplementation class and hook it all up on startup.

Any best practices for this out there? I’m working with a clean and (almost) empty EPiserver 7 MVC project at the moment – any help would be appreciated.

-- 
Regards,
Tarjei Olsen

 

#63515
Nov 21, 2012 11:40
Vote:
 

Hello Tarjei.

If you just add a parameter of type IContentRepository to your controller constructor it should be handled by StructureMap automatically.

The most flexiblle way to configure your rules is to implement IConfigurableModule:

    [InitializableModule]
    [ModuleDependency(typeof(EPiServer.Shell.ShellInitialization))] // You can provide dependencies by specifying other initialization modules types
    public sealed class MyInitialization : IConfigurableModule
    {
        public void ConfigureContainer(ServiceConfigurationContext context)
        {
            context.Container.Configure(ce =>
            {
                ce.For<IAwesomeness>().Use<AwesomenessImplementation>();
            });
        }
    }

    

Other way to configure it is to use ServiceConfigurationAttribute:

[ServiceConfiguration(ServiceType = typeof(IAwesomeness), Lifecycle = ServiceInstanceScope.HttpContext)]
    public class AwesomenessImplementation: IAwesomeness

    

#63527
Nov 21, 2012 14:06
Vote:
 
public class StructureMapDependencyResolver : IDependencyResolver
    {
        private readonly IContainer _container;
 
        public StructureMapDependencyResolver(IContainer container)
        {
            _container = container;
        }
 
        public object GetService(Type serviceType)
        {
            if (serviceType.IsInterface || serviceType.IsAbstract)
            {
                return GetInterfaceService(serviceType);
            }
            else
            {
                return GetConcreteService(serviceType);
            }
        }
 
        private object GetConcreteService(Type serviceType)
        {
            return _container.GetInstance(serviceType);
        }
 
        private object GetInterfaceService(Type serviceType)
        {
            return _container.TryGetInstance(serviceType);
        }
 
        public IEnumerable<object> GetServices(Type serviceType)
        {
            return _container.GetAllInstances(serviceType).Cast<object>();
        }
    }

 [ModuleDependency(typeof(ServiceContainerInitialization))]
    [InitializableModule]
    public class MvcTemplatesInitializer : IInitializableModule
    {
 
        public void Initialize(InitializationEngine context) {
           DependencyResolver.SetResolver(new StructureMapDependencyResolver(context.Container)); }
}
#63528
Edited, Nov 21, 2012 14:14
Vote:
 

@Sergii Vorushylo - my first issues is that simply adding a parameter of type IContentRepository to my controller constructor results in an error at runtime: "No parameterless constructor defined for this object.". I agree that it should be handled by StructureMap automatically via some sort of initialization by EPi, but that is not the case.

@Jonas Bergqvist - The initialization module contains som code that will not compile because the Container property is marked as internal on the EPiServer.Framework.Initialization.InitializationEngine class:

DependencyResolver.SetResolver(new StructureMapDependencyResolver(context.Container));

Is there some other way to set the dependency resolver?

Thanks to both of you for helping out so far!

-- 
Regards,
Tarjei Olsen

#63529
Nov 21, 2012 14:51
Vote:
 

I got it now.

I made a StructureMapDependencyResolver, identical to the one in the post from Jonas Bergqvist. Then I changed the IInitializableModule to an IConfigurableModule based on the post from Sergii Vorushylo.

The resolver:

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 GetInterfaceService(serviceType);
            
        return GetConcreteService(serviceType);
    }

    private object GetConcreteService(Type serviceType)
    {
        return container.GetInstance(serviceType);
    }

    private object GetInterfaceService(Type serviceType)
    {
        return container.TryGetInstance(serviceType);
    }

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

The configurable module:

[ModuleDependency(typeof(ServiceContainerInitialization))]
[InitializableModule]
public class MvcTemplatesInitializer : IConfigurableModule
{
    public void ConfigureContainer(ServiceConfigurationContext context)
    {
        context.Container.Configure(x =>
        {
            x.For<IProvideAwesomeness>().Use<AwesomeStuff>();
        });

        DependencyResolver.SetResolver(new StructureMapDependencyResolver(context.Container)); 
    }

    public void Initialize(InitializationEngine context) { }
    public void Uninitialize(InitializationEngine context) { }
    public void Preload(string[] parameters) { }
}

And finally, my controller class:

public class StartController : PageController<StartPage>
{
    private IContentRepository dataFactory;
    private IProvideAwesomeness awesomeStuff;


    public StartController(IContentRepository dataFactory, IProvideAwesomeness awesomeStuff)
    {
        this.dataFactory = dataFactory;
        this.awesomeStuff = awesomeStuff;
    }

    // ...
}

Is this the right way to do it? It seems to me I now can have dependency injection in my controllers, and mix & match interfaces from EPi, like the IContentRepository, and my own contracts and classes.

 

-- 
Regards,
Tarjei Olsen

#63531
Nov 21, 2012 15:35
This thread is locked and should be used for reference only. Please use the Episerver CMS 7 and earlier versions forum to open new discussions.