Five New Optimizely Certifications are Here! Validate your expertise and advance your career with our latest certification exams. Click here to find out more

Daniel Ekeblad
Mar 21, 2025
  164
(0 votes)

What is ConstructorParameterResolver?

A few weeks ago I stumbled on a little-known feature and searching for it didn't reveal much about it. I managed to figure out how it's supposed to work and now I'm sharing my findings.

What is the feature?

The feature consists of two types, the interface EPiServer.Construction.IConstructorParameterResolver and the static class EPiServer.Construction.ConstructorParameterResolver.

The type names should give you an idea about what they do, something related to dependency injection via constructors. To be exact: You need to use this feature to enable dependency injection with constructors on Content Types (like pages and blocks).

Constructors, on Content Types?

If you had asked me before discovering this feature I would never considered adding a constructor on a something like a page or block type and attempting to inject a service. I mean, they are data classes, they should have little or no logic. If I had to use a service I would have picked using ServiceLocator or Injected<>.

I got the idea of trying to add a constructor not via services but when thinking about default values for properties. As you should know, you can't set default values for properties on content types with property initialization:

public class ExamplePage : PageData
{
	// Does not work
	public virtual string Heading { get; set; } = "Default Heading";

	// Do this instead
	public virtual string Heading { get; set; }
	public override void SetDefaultValues(ContentType contentType)
	{
		Heading = "Default Value";
	}
}

I thought: "If property initialization does not work, then initializing them inside a constructor probably does not work either." quickly followed by "Constructors, on Content Types?" before arriving at "I assume they don't support dependency injection".

Instead of just stopping there I tried to see what happens. What happens if I try to inject, say, IContentLoader into the constructor of ExamplePage?

public class ExamplePage : PageData
{
	private readonly IContentLoader _contentLoader;
	public ArticlePage(IContentLoader contentLoader)
	{
		_contentLoader = contentLoader;
	}

	public virtual string Heading { get; set; }
}

Starting the site and trying to load a page of this types throws, as I expected, an exception. But I did not see the last part of the exception message coming: InvalidOperationException: Type 'SomeSite.Models.Pages.ExamplePage' does not have any public default constructor and no IConstructorParameterResolver that handles it.

How do I use IConstructorParameterResolver?

Upon seeing the above message I tried searching for the type on the Internet. Except for finding the CsClassLibraries page and it being mentioned in the middle of a stacktrace of an otherwise unrelated question here on Optimizely World I ended up empty-handed.

From the CsClassLibraries page I found the static class ConstructorParameterResolver. From these two pages and some help from Visual Studio I arrived at the following implementation:

public class MyConstructorParameterResolver : IConstructorParameterResolver
{
	private readonly IServiceScopeFactory _serviceScopeFactory;

	public MyConstructorParameterResolver(IServiceScopeFactory serviceScopeFactory)
	{
		_serviceScopeFactory = serviceScopeFactory;
	}

	public bool TryResolveArguments(Type typeToConstruct, Type[] constructorArguments, out object[] instances)
	{
		using var scope = _serviceScopeFactory.CreateScope();
		instances = new object[constructorArguments.Length];
		for (int i = 0; i < constructorArguments.Length; i++)
		{
			var serviceInstance = scope.ServiceProvider.GetRequiredService(constructorArguments[i]);
			instances[i] = serviceInstance;
		}

		return true;
	}
}

[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class MyInitModule : IInitializableModule
{
	public void Initialize(InitializationEngine context)
	{
		var scopeFactory = context.Locate.Advanced.GetRequiredService<IServiceScopeFactory>();
		var paramResolver = new MyConstructorParameterResolver(scopeFactory);
		// Note: If you content types area spread out over multiple
		// projects/assemblies then you need to register them all one by one.
		ConstructorParameterResolver.Instance.RegisterResolver(paramResolver, typeof(ExamplePage).Assembly);
	}

	public void Uninitialize(InitializationEngine context)
	{
	}
}
    

Assuming I haven't done a copy-paste-edit mistake this compiles and the site starts.

Can I use it?

According to CsClassLibraries these classes have existed since CMS 7, however...

When I tried to load a page of the ExamplePage type again to test the code above I got a different exception: MissingMethodException: Constructor on type 'SomeSite.Models.Pages.ExamplePage_DynamicProxy' not found. The constructor does not exist on the generated DynamicProxy class?

I asked the support and they replied the implementation above should work, this is considered a bug and has been assigned id CMS-39932 and has existed since at least episerver.cms.core 12.19.0. (The earliest 12.x version I have tested with.)

Should I use it?

Personally, I will continue using ServiceLocator, Injected<> or providing a service as a method parameter in the rare cases I want one inside a content type. I would avoid constructor injection, it requires this mysterious IConstructorParameterResolver to be implemented and registered somewhere. Not only does constructors (at least to me) look strange on content types, something that looks normal and should just work ends up throwing exceptions for content types specifically.

However, I can also imagine there being scenarios where a ConstructorParameterResolver is needed to get a very special thing to work the way you want. In the test implementation above I just try resolving all requested types from a service scope but there may be cases where you can't register some types for DI or that you want to resolve the service and apply some special configuration to it before handing the instance to the content type. That kind of logic can be placed in this class.

Even if you will use it or not, you probably just learned about a new feature in Optimizely CMS. If you used IConstructorParameterResolver before, feel free to share how you used it in the comments.

Mar 21, 2025

Comments

Please login to comment.
Latest blogs
Managed Identity for Connecting your Optimizely site to a Database in Azure

Are you using a connection string with username and password to connect to your Azure database? Use managed identity instead!

Tomas Hensrud Gulla | Mar 24, 2025 |

A day in the life of an Optimizely Developer - Creating a Cloudflare Turnstile Form Element Block

Hello and welcome to another installment of a day in the life of an Optimizely developer. Today I am going to show how to create a Cloudflare...

Graham Carr | Mar 24, 2025

Links in Optimizely Cms: A guide

In Optimizely CMS there are several ways to link to internal or external content using different property types, and there are several ways to rend...

Torunn Surnflødt | Mar 21, 2025