6

UPDATED WITH LATEST RESULTS AND QUESTION

I've converted everything to pipeline and integrated Simple Injector. As I mentioned, I've never used SI before, and I'm having trouble with the controller using EntityService. I can't seem to get the SI configuration for that controller to work. I get the following error: "Make sure the controller has a parameterless public constructor. All of the other registrations are working fine with SI. I debug and see those being populated.

Here is the controller:

    namespace ProjectA.Controllers
{
    [EnableCors(origins: "*", headers: "*", methods: "*")]
    [ServicesController]
    public class ProductServiceController : EntityService<Product>
    {
        public ProductServiceController(IRepository<Product> repository) : base(repository)
        {
        }
    }
}

And here is the registration with the working ones:

public void Process(PipelineArgs args)
    {
        // Create the container
        var container = Container.CreateContainer();
        container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();

        var assemblies = AppDomain.CurrentDomain.GetAssemblies().Where(a => a.FullName.StartsWith("ProjectA.") || a.FullName.StartsWith("ProjectB."));
        container.RegisterWebApiControllers(GlobalConfiguration.Configuration);
        container.RegisterMvcControllers(assemblies.ToArray());
        // Register Mvc filter providers
        container.RegisterMvcIntegratedFilterProvider();

    //these all work
        container.Register<IProductsReader, ProductsReader>();
        container.Register<IProductsUpdater, ProductsUpdater>();
        container.Register<IProductsCreator, ProductsCreator>();
        container.Register<IProductMapper, ProductMapper>();
        container.Register<IFieldUpdater, FieldUpdater>();
        container.Register(typeof(IRepository<Product>), typeof(ProductRepository));

        //tried this for EntityService Controller, but no good
        //container.Register<IRepository<Product>>(() => new ProductRepository(container.GetInstance<IProductsReader>(), container.GetInstance<IProductsUpdater>(), container.GetInstance<IProductsCreator>()));

        container.Verify();

        GlobalConfiguration.Configuration.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);

        // Set the ASP.NET dependency resolver
        DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));

    }

I've used a lot of Ninject in the past, but was hoping to get SI working here. I'm sure it will end up being a missing registration line ;) Thanks for the help!


ORIGINAL QUESTION

Hoping someone can help. I am using EntityService and have implemented Simple Injector. I know Simple Injector defaults to allow a single constructor and that is the recommended approach. With that in place, I'm getting the following error:

"An error occurred when trying to create a controller of type 'ProductServiceController'. Make sure that the controller has a parameterless public constructor."

Should I add a default constructor to the ProductRepository and if so, what do I add to global.asax for Simple Injector? Below is my related code.

Sample from Global.asax

var container = new Container();
        container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();

        // Register your types
        container.Register<IProductsReader, ProductsReader>();
        container.Register<IProductsUpdater, ProductsUpdater>();
        container.Register<IProductsCreator, ProductsCreator>();
        container.Register<IProductMapper, ProductMapper>();
        container.Register<IFieldUpdater, FieldUpdater>();
        container.Register(typeof(IRepository<Product>), typeof(ProductRepository));
        container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
        container.RegisterMvcIntegratedFilterProvider();
        container.Verify();

        DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));

Controller

 [EnableCors(origins: "*", headers: "*", methods: "*")]
[ServicesController]
public class ProductServiceController : EntityService<Product>
{
    public ProductServiceController(IRepository<Product> repository) : base(repository)
    {
    }
}

Sample from Repository

public class ProductRepository : Sitecore.Services.Core.IRepository<Product>
{
    private IProductsReader productsReader;
    private IProductsUpdater productsUpdater;
    private IProductsCreator productsCreator;

    public ProductRepository(IProductsReader productsReader, IProductsUpdater productsUpdater, IProductsCreator productsCreator)
    {
        this.productsReader = productsReader;
        this.productsUpdater = productsUpdater;
        this.productsCreator = productsCreator;
    }

Thanks! Dan

2 Answers 2

3

While @dnstommy is correct, what you are doing is not best practice and you really should refactor if you can to use a pipeline approach. The answer to your question is in this line:

container.RegisterMvcControllers(Assembly.GetExecutingAssembly());

With that you are only registering MVC Controllers that live in the website project. Its hard to tell because you have not included namespaces in your examples, but if your controller is in a different assembly, it will not get registered with Simple Injector and give you the exact error you are getting.

To register all controllers in your solution you can use this code from @dnstommys answer:

// Register Mvc controllers (all assemblies in your solution)
var assemblies = AppDomain.CurrentDomain.GetAssemblies()
    .Where(a => a.FullName.StartsWith("PATTERN TO YOUR PROJECT ASSEMBLIES"));
container.RegisterMvcControllers(assemblies.ToArray());

This will work either in the Global.asax or in the pipeline method. You should not add a default constructor to your controller as then none of your dependencies would be injected into the controller and it still would not work. It just needs registering with the container properly.

1
  • Thanks guys, I was planning to convert to pipeline, but was using global as a quick test for Simple Injector (haven't used it before). I'm not using Sitecore 8.2, so figured it was the best option for Ioc. I was following along with 'Professional Sitecore 8 Development' for the pipeline setup and SI. Sounds like Richard is suggesting a similar setup. Thanks for the tips. I see that I forgot to register SearchService;) Much appreciated! Commented May 11, 2017 at 14:09
1

It's not Sitecore best practice to modify the global.ascx file. In 8.2 global.ascx has been moved private so you no longer allowed to modify the global.ascx anymore. You should build it all and register it with the initialization pipeline.

If you are on 8.2, you can use the DI built into Sitecore itself. See it here.

If you still want to use Simple Injector, the code below should work for you. I have used it in 7.2 up to 8.2.

InitializeDependencyInjectionArgs.cs

using Sitecore.Pipelines;

namespace Foundation.DependancyInjection.IoC
{
    public class InitializeDependencyInjectionArgs : PipelineArgs
    {
        public SimpleInjector.Container Container { get; set; }

        public InitializeDependencyInjectionArgs(SimpleInjector.Container container)
        {
            this.Container = container;
        }
    }
}

InitializeDependencyInjection.cs

using SimpleInjector.Integration.Web;

namespace Foundation.DependancyInjection.IoC
{
    using System;
    using System.Linq;
    using System.Web.Mvc;
    using Sitecore.Pipelines;
    using Services;
    using SimpleInjector;
    using SimpleInjector.Integration.Web.Mvc;

    public class InitializeDependencyInjection
    {
        public void Process(PipelineArgs args)
        {
            // Create the container
            var container = Container.CreateContainer();
            container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();

            // Register Mvc controllers (all assemblies in your solution)
            var assemblies = AppDomain.CurrentDomain.GetAssemblies()
                .Where(a => a.FullName.StartsWith("Client.Web"));

            container.RegisterMvcControllers(assemblies.ToArray());

            // Register Mvc filter providers
            container.RegisterMvcIntegratedFilterProvider();

            container.Register<ISearchService, SearchService>();

            // Verify if the container
            container.Verify();

            // Set the ASP.NET dependency resolver
            DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));

        }
    }
}

container.cs

namespace Foundation.DependancyInjection.IoC
{
    using System;

    public static class Container
    {
        private static SimpleInjector.Container configuration;

        public static SimpleInjector.Container CreateContainer()
        {
            configuration = new SimpleInjector.Container();
            return configuration;
        }

        public static T Resolve<T>() where T : class
        {
            return configuration.GetInstance<T>();
        }

        public static object Resolve(Type type)
        {
            return configuration.GetInstance(type);
        }
    }
}

The config to bring it all together

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <initialize>
        <processor type="Foundation.DependancyInjection.IoC.InitializeDependencyInjection, Foundation.DependancyInjection"
                patch:before="processor[@type='Sitecore.Mvc.Pipelines.Loader.InitializeControllerFactory, Sitecore.Mvc']" />
      </initialize>
    </pipelines>
  </sitecore>
</configuration>

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.