Robin Hellen
Robin Hellen

Reputation: 23

Autofac: adding a decorator to a delegate registration that takes parameters

I'm trying to manage registration of my WCF clients using autofac.

I need to resolve Func<MachineEndpoint, IFooService>, so I had registered the service using:

builder.Register((c, p) => FooService(c, p.TypedAs<MachineEndpoint>())).UseWcfSafeRelease()

Where FooService is:

private IJobService FooService(IComponentContext c, MachineEndpoint endpoint) {...}

This works fine, but I want to add a decorator to this to handle certain security errors, so I tried the following:

builder.Register((c, p) => FooService(c, p.TypedAs<MachineEndpoint>())).UseWcfSafeRelease().Named<IFooService>("simple foo service");
builder.RegisterDecorator<IFooService>((c, p, inner) => Wrap(inner, p.TypedAs<MachineEndpoint>(), c.Resolve<CertificateLookupCache>()), "simple foo service");

but I get a DependecyResolutionException:

Autofac.Core.DependencyResolutionException: An exception was thrown while executing a resolve operation. See the InnerException for details. ---> System.InvalidOperationException: Sequence contains no elements
   at System.Linq.Enumerable.First[TSource](IEnumerable`1 source)
   at MyModule.<Load>b__4(IComponentContext c, IEnumerable`1 p) in MyModule.cs:line 30 (the first line above)
   at Autofac.Builder.RegistrationBuilder.<>c__DisplayClass1`1.<ForDelegate>b__0(IComponentContext c, IEnumerable`1 p)
   at Autofac.Core.Activators.Delegate.DelegateActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters)
   at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters)
   at Autofac.Core.Resolving.InstanceLookup.Execute()
   at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.Features.LightweightAdapters.LightweightAdapterRegistrationSource.<>c__DisplayClass3.<RegistrationsFor>b__1(IComponentContext c, IEnumerable`1 p)
   at Autofac.Builder.RegistrationBuilder.<>c__DisplayClass1`1.<ForDelegate>b__0(IComponentContext c, IEnumerable`1 p)
   at Autofac.Core.Activators.Delegate.DelegateActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters)
   at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters)
   at Autofac.Core.Resolving.InstanceLookup.Execute()
   at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters)
   --- End of inner exception stack trace ---

Server stack trace:
   at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
   at lambda_method(Closure , MachineEndpoint )

How can I register these functions so that I can get the MachinEndpoint parameter passed to both the WCF client object and the decorator?

Upvotes: 2

Views: 1248

Answers (1)

nemesv
nemesv

Reputation: 139758

The problem is caused by your parameter usage: p.TypedAs<MachineEndpoint>() because there is currently a limitation in Autofac namely that the RegisterDecorator does not pass through parameters when creating the inner decorated object.

So when it tries to create inner with the (c, p) => FooService(c, p.TypedAs<MachineEndpoint>()) the (IEnumerable<Parameter> p) will be empty so you get the exception.

You can basically do two things:

You can try to reorganize your code in a way that it doesn't use p.TypedAs<MachineEndpoint>() anymore.

Or

Because your IFooService anyway a WCF service which does not have multiple implementations you can register your Decorator by "hand":

builder.Register((c, p) => FooService(c, p.TypedAs<MachineEndpoint>()))
       .UseWcfSafeRelease()
       .Named<IFooService>("simple foo service");
builder.Register<IFooService>((c, p) => 
     Wrap(c.ResolveNamed<IFooService>("simple foo service",
          TypedParameter.From(p.TypedAs<MachineEndpoint>())),
          p.TypedAs<MachineEndpoint>(),
          c.Resolve<CertificateLookupCache>()));

Upvotes: 2

Related Questions