Autofac dependency resolution fails when IDependency not used on interface

Topics: Writing modules
Sep 13, 2011 at 12:19 AM

Using this interface...

namespace Teamaton.Search.Models {
	public interface ISearchCriteriaSectionsProvider {
		IEnumerable<ISearchCriteriaSection> GetSections();

with the following implementation...

namespace Discoverize.Models {
	public class DiscoverizeSearchCriteriaSectionsProvider : ISearchCriteriaSectionsProvider, ISingletonDependency {
		public IEnumerable<ISearchCriteriaSection> GetSections() {
			return ...

and this constructor...

namespace Teamaton.Search.Drivers {
	public class SearchFormPartDriver : ContentPartDriver<TeamatonSearchFormPart> {
		private readonly ISearchCriteriaSectionsProvider _criteriaSectionsProvider;

		public SearchFormPartDriver(ISearchCriteriaSectionsProvider criteriaSectionsProvider){
			_criteriaSectionsProvider = criteriaSectionsProvider;

... will throw an exception: None of the constructors found with policy 'Orchard.Environment.AutofacUtil.DynamicProxy2.ConstructorFinderWrapper' on type 'SearchFormPartDriver' can be invoked with the available services and parameters.

When I pull the IDependency inheritance up into the interface, it works, though:

namespace Teamaton.Search.Models {
	public interface ISearchCriteriaSectionsProvider : ISingletonDependency {
		IEnumerable<ISearchCriteriaSection> GetSections();

This not what I would expect, but the code in ShellContainerFactory does exactly that: it registers services only as such interfaces that inherit from IDependency (and not all types inheriting from IDependency):

foreach (var interfaceType in item.Type.GetInterfaces()
    .Where(itf => typeof(IDependency).IsAssignableFrom(itf) // <-- shouldn't it be typeof(IDependency).IsAssignableFrom(item.Type) ?
                  && !typeof(IEventHandler).IsAssignableFrom(itf))) {
    registration = registration.As(interfaceType);

Is this is a design decision or maybe a bug?

Sep 14, 2011 at 11:34 AM
Edited Sep 14, 2011 at 11:35 AM

I would think this is really by design. It needs to register the dependency as the interface type that you're going to request. The alternative would be to find any type that implements IDependency (either directly or indirectly) and register all implemented interfaces, but this is a bit less specific and not really guaranteed to be what you want - and can lead to subtle classes of bugs. It is the interface that you're registering primarily, along with an implementation, so it does make sense to be explicit about it in this case in my opinion. 

I'm going to add a note to my blog post which talks about this to make it clear as I can see it catching more people out!

Sep 14, 2011 at 4:27 PM

Could you add a link to the blog post you mentioned?

The one problem I see with the current approach (beyond forgetting to add the interface) is that you have to define the lifetime scope of your implementation in the interface (through inheritance of one of the IDependency interfaces). Why shouldn't the implementation define its scope itself? I find this somewhat unclear.

On the other hand, I can see your point with the registration of interfaces that are not explicitly marked as IDependency. This really looks like a design decision, now that you've mentioned it.

Thanks anyway for commenting on this, I now have a better feeling about this :-)

Sep 14, 2011 at 4:53 PM

Sure, I've updated the blog post, it's over here: 

In terms of having the implementation define its scope, well... Hmmm. It's perhaps a valid point, I can imagine situations where one implementation of an interface should be request scoped and the otherapplication scoped maybe, but at the moment it isn't possible here as you say. It's a difficult one, and at the moment it looks like the balance has been struck between reasonable simplicity for people that are not too comfortable with IoC perhaps, and complete control. A trade off, but probably reasonable for now.

Glad you're enjoying things!