OnActivating casts to concrete type causing InvalidCastException

117 views
Skip to first unread message

Kaleb Pederson

unread,
Apr 24, 2013, 7:43:16 PM4/24/13
to aut...@googlegroups.com
Using Autofac-2.6.

I'm trying to decorate an object in the following fashion:

public interface ISample {}
public class Sample : ISample {}
public class ProxiedSample: ISample {/*with constructor*/}

builder.RegisterType<Sample>().As<ISample>()
    .OnActivating(e => e.ReplaceInstance(new ProxiedSample(e.Instance)));

That results in an exception though:

System.InvalidCastException : Unable to cast object of type 'ProxiedSample' to type 'Sample'.

Is there a variant of the registration above that will allow me to proxy it based on the interface type instead of the concrete type?

Note: I know there's the RegisterDecorator method but I have some other things going on in this case that make it less than ideal, especially given its key requirements.

Thanks.

--Kaleb

Kaleb Pederson

unread,
Apr 25, 2013, 11:34:54 AM4/25/13
to aut...@googlegroups.com
I just tested and the behavior occurs in both autofac 2.6 and 3.0. I also created a complete unit test and posted to Stack Overflow:


Thanks.

--Kaleb

Travis Illig

unread,
Apr 25, 2013, 1:06:08 PM4/25/13
to aut...@googlegroups.com
Tricky one. I think it's line 342 of the RegistrationBuilder<T, A, R> that's the culprit, the one where the ActivatingEventArgs gets created:

public IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> OnActivating(Action<IActivatingEventArgs<TLimit>> handler)
{
    if (handler == null) throw new ArgumentNullException("handler");
    RegistrationData.ActivatingHandlers.Add((s, e) =>
    {
        var args = new ActivatingEventArgs<TLimit>(e.Context, e.Component, e.Parameters, (TLimit)e.Instance);
        handler(args);
        e.Instance = args.Instance;
    });
    return this;
}

The generic is needed to make sure you get some nice strong typing, but it does throw a wrench in works like this because of the cast.

I'm not sure what the right way to fix that is. Maybe internally just don't cast it? Hypothetically it would mean someone could try replacing the instance with an incompatible type, like replacing a string with an int and that wouldn't be good, either. It doesn't look like we have the service type being resolved at the time of activation, and since components can be exposed as many different service types, we can't just arbitrarily choose one. I suppose we could make sure it's compatible with ALL the service types, but that'd cause pain with your proxy type - change the registration with another .As<T> and you'd have to implement a new interface on the proxy type.

I suppose in the meantime you could use the DynamicProxy library. That works pretty well for this stuff.
-T

Kaleb Pederson

unread,
Apr 29, 2013, 11:24:31 AM4/29/13
to aut...@googlegroups.com
I spent a little bit of time working through it this weekend, details inline below.

On Thu, Apr 25, 2013 at 10:06 AM, Travis Illig <travis...@gmail.com> wrote:
Tricky one. I think it's line 342 of the RegistrationBuilder<T, A, R> that's the culprit, the one where the ActivatingEventArgs gets created:

The above is fine as the event args are outbound so we still have the concrete type available. However, the ReplaceInstance method does a cast to originally specified concrete type, which is where things were blowing up.


public IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> OnActivating(Action<IActivatingEventArgs<TLimit>> handler)
{
    if (handler == null) throw new ArgumentNullException("handler");
    RegistrationData.ActivatingHandlers.Add((s, e) =>
    {
        var args = new ActivatingEventArgs<TLimit>(e.Context, e.Component, e.Parameters, (TLimit)e.Instance);
        handler(args);
        e.Instance = args.Instance;
    });
    return this;
}


Since e.Instance was being assigned args.Instance regardless of whether ReplaceInstance was called, I tried using reflection to set the instance but then it blows up during resolution as apparently it's the concrete type that's used in the cast after doing the resolution.

I did find an easy work around though:

[Test] public void OnActivating_ReplaceInstance_can_be_used_to_replace_instances_of_interface_types() { var builder = new ContainerBuilder(); builder.RegisterType<Sample>().AsSelf(); builder.Register<ISample>(c => c.Resolve<Sample>()) .OnActivating(x => x.ReplaceInstance(new ProxiedSample(x.Instance))) .SingleInstance(); var container = builder.Build(); var sample = container.Resolve<ISample>(); Assert.That(sample, Is.InstanceOf<ProxiedSample>()); }

Thanks for taking a look at it.

--Kaleb

 
On Thursday, April 25, 2013 8:34:54 AM UTC-7, Kaleb Pederson wrote:
I just tested and the behavior occurs in both autofac 2.6 and 3.0. I also created a complete unit test and posted to Stack Overflow:


Thanks.

--Kaleb


On Wed, Apr 24, 2013 at 4:43 PM, Kaleb Pederson <kaleb.p...@gmail.com> wrote:
Using Autofac-2.6.

I'm trying to decorate an object in the following fashion:

public interface ISample {}
public class Sample : ISample {}
public class ProxiedSample: ISample {/*with constructor*/}

builder.RegisterType<Sample>().As<ISample>()
    .OnActivating(e => e.ReplaceInstance(new ProxiedSample(e.Instance)));

That results in an exception though:

System.InvalidCastException : Unable to cast object of type 'ProxiedSample' to type 'Sample'.

Is there a variant of the registration above that will allow me to proxy it based on the interface type instead of the concrete type?

Note: I know there's the RegisterDecorator method but I have some other things going on in this case that make it less than ideal, especially given its key requirements.

Thanks.

--Kaleb

--
You received this message because you are subscribed to the Google Groups "Autofac" group.
To unsubscribe from this group and stop receiving emails from it, send an email to autofac+u...@googlegroups.com.
To post to this group, send email to aut...@googlegroups.com.
Visit this group at http://groups.google.com/group/autofac?hl=en-US.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Kaleb Pederson

unread,
Apr 29, 2013, 11:36:45 AM4/29/13
to aut...@googlegroups.com
I just updated the wiki with the workaround:


The page was tagged as Autofac-2 but I know at least the OnActivating portions also work for v3. I'm not using autofac v3 yet, but if someone wants to confirm and/or update the tag I suspect some future reader would be grateful.

--Kaleb

Alex Meyer-Gleaves

unread,
Apr 30, 2013, 7:57:35 AM4/30/13
to aut...@googlegroups.com

Thanks Kaleb. That looks like a very simple workaround. I have updated the tag on the page to Autofac3.

Reply all
Reply to author
Forward
0 new messages