Generic IDependency not resolving

Topics: Troubleshooting, Writing modules
Developer
Sep 27, 2011 at 7:07 PM
Edited Oct 3, 2011 at 9:10 AM

Hi all!

I'm not sure whether this is and Orchard or an Autofac issue.

I have the following interface:

public interface IMyService<TPart, TRecord> : IDependency
    {
        ...
    }

 

...and the following class implementing it:

public class MyService<TPart, TRecord> : IMyService<TPart, TRecord>
    {
      ...
    }

 

Now this fails:

_orchardServices.WorkContext.Resolve<IMyService<MyPart, MyRecord>>();

with the message "The requested service 'MyModule.Services.IAssociativyService`2[[MyModule.Models.MyPart, App_Web_3z5ipzqi, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[MyModule.Models.MyRecord, App_Web_3z5ipzqi, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]' has not been registered."

Trying to inject the IMyService instance in the constructor fails equally.

As I have seen with IMapper<TSource, TTarget> generic IDependencies are supported. The above code only works (but then it works) if the interface is not generic.

Any help would be greatly appreciated, as I'm quite clueless now.

Coordinator
Sep 27, 2011 at 7:15 PM

CAn you try a work around ? Creating another interface IMySpecificervice : IMyService<Tpart, TRecord>, IDependency, and have MyServicePart: IMySpecificService ?

Developer
Sep 27, 2011 at 7:28 PM

Thanks, this works. However this is not a real workaround for me unfortunately. The service class is generic for a reason, but making a specific class and interface for all types it is used with defeats the purpose :-(. Where can the problem lay? I have no clue why IMapper is working, but this is not.

Developer
Sep 27, 2011 at 8:44 PM
Edited Oct 3, 2011 at 9:11 AM

This thread may be related, but IRepository is not marked with any marker interface, as it is registered directly with Autofac in Orchard\Data\DataModule.cs. Maybe I should use also such an approach?

Developer
Sep 27, 2011 at 11:04 PM

I have solved the issue but this definitely shouldn't be one and should be handled as usual with IDependency.

The solution is remarkably simple, but it took me a while to dig through Autofac docs and Orchard sources:

 

    public class MyHandler : ContentHandler
    {
        private readonly IComponentContext _componentContext;

        public MyHandler(IComponentContext componentContext)
        {
            _componentContext = componentContext;
            var builder = new ContainerBuilder();
            builder.RegisterGeneric(typeof(MyService<,>)).As(typeof(IMyService<,>));
            builder.Update(_componentContext.ComponentRegistry);
        }
    }

This handler could be used for anything, but now its sole purpose is to add the service to the IoC container. This way the service can be used as any other dependency, e.g. injected in the ctor.

As I think this functionality not working with IDependency is an issue, I have opened a new one here.

 

Developer
Sep 29, 2011 at 6:57 PM

Strangely it seems that although this works most of the time, sometimes Autofac can't find the registered types (notably when running the site on development server, but debugging stopped). Could someone advise a better place for the registration code so that Orchard always picks it up, as early as possible?

Oct 3, 2011 at 12:56 AM
Edited Oct 3, 2011 at 12:57 AM

i have the same problem here: http://orchard.codeplex.com/discussions/273571

 

i thought it was fixed but it came back ^^

Developer
Nov 19, 2011 at 2:13 PM
Edited Nov 19, 2011 at 3:34 PM

I dived a bit into the code to see what have to be modified for generic dependencies to work. It seems that in ShellContainerFactory.CreateContainer() there is a loop where all types that implement the IDependency interface are registered. The problem lies there that the method RegisterType() called from there can only register non-generic types, as it calls Autofac's ContainerBuilder.RegisterType() (for generic types, ContainerBuilder.RegisterGeneric() should be called).

Now it would be simple to detect whether a type is generic or not in the loop, as item.Type.IsGenericType gives the answer. However unfortunately it wouldn't be sufficient only to change which registration method gets called. DynamicProxyExtensions and a whole bunch of other classes would have to be modified to adapt to that ContainerBuilder.RegisterGeneric() returns not an IRegistrationBuilder<object, ConcreteReflectionActivatorData, SingleRegistrationStyle> as ContainerBuilder.RegisterType() does, but a IRegistrationBuilder<object, ReflectionActivatorData, DynamicRegistrationStyle>.

ConcreteReflectionActivatorData is a derivation of ReflectionActivatorData, so it might be possible to change the generic constraints in subsequent method calls accordingly, but DynamicRegistrationStyle and SingleRegistrationStyle have nothing in common regarding type hierarchy, so this might be more difficult. Fortunately it seems that TRegistrationStyle doesn't anywhere has a constraint.

I'd like to ask core developers whether these ideas are valid?

Developer
Dec 22, 2011 at 9:53 AM

Just for those who stumble upon this discussion: here it's described an alternative, using an Autofac module.

May 20, 2013 at 5:24 PM
Piedone wrote:
I have solved the issue but this definitely shouldn't be one and should be handled as usual with IDependency. The solution is remarkably simple, but it took me a while to dig through Autofac docs and Orchard sources:   public class MyHandler : ContentHandler { private readonly IComponentContext _componentContext; public MyHandler(IComponentContext componentContext) { _componentContext = componentContext; var builder = new ContainerBuilder(); builder.RegisterGeneric(typeof(MyService<,>)).As(typeof(IMyService<,>)); builder.Update(_componentContext.ComponentRegistry); } } This handler could be used for anything, but now its sole purpose is to add the service to the IoC container. This way the service can be used as any other dependency, e.g. injected in the ctor. As I think this functionality not working with IDependency is an issue, I have opened a new one here.  
Now, once you added it in the MyHandler class, did this then work after?
_orchardServices.WorkContext.Resolve<IMyService<MyPart, MyRecord>>();


I tried, and I still seem to get an error.
Developer
May 20, 2013 at 6:01 PM
Yes, but an Autofac module is a better way to do this, see the link.