Autofac.Extras.DynamicProxy: PropertiesAutowired not working when EnableInterfaceInterceptors and AllowCircularDependencies

I use Autofac DynamicProxy2 do AOP logging. If I register like this:

builder.RegisterAssemblyTypes(assemblys.ToArray()).Where(t => t.Name.EndsWith("ServiceImpl"))
                .AsImplementedInterfaces()
                .InstancePerLifetimeScope()
                .PropertiesAutowired()
                .EnableInterfaceInterceptors()
                .InterceptedBy(typeof(LogInterceptor));
            ;

then ValuesServiceImpl instance can be auto wire to property, like this:

public class ValuesController : ApiController
    {
        public IValuesService ValuesService { get; set; }
    }

But if I use PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies)

builder.RegisterAssemblyTypes(assemblys.ToArray()).Where(t => t.Name.EndsWith("ServiceImpl"))
                    .AsImplementedInterfaces()
                    .InstancePerLifetimeScope()
                    .PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies)
                    .EnableInterfaceInterceptors()
                    .InterceptedBy(typeof(LogInterceptor));

then PropertiesAutowired not working, ValuesService property is null.

In my project I must allow Circular Dependencies, and I prefer use EnableInterfaceInterceptors instead of EnableClassInterceptors

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 18 (17 by maintainers)

Commits related to this issue

Most upvoted comments

My two cents:

My experience of writing extensible systems has shown me that having a specific order value assigned to an event handler is an eventual recipe for disaster (even if it fixes the immediate problem). When two extension writers both set the same order value, you’re back to square one. Or there’s a competing race to have the highest/lowest order value. It becomes something of a nightmare.

The most effective event-handling solution I’ve found in such systems is to have a generous number of clearly defined events that are strongly ordered, with event handlers attached to each that are not ordered. Autofac already has a number of such events (Preparing, Activating, Activated, etc), but they could be extended/cleaned up.

An Alternative

An alternate approach/improvement (in my opinion) to the event handling model when using ‘declarative’ event ordering (like we appear to have in the fluent set-up, and @alsami points out is pretty far-reaching already) might be to use a middleware-style event pipeline model for event handling.

Treating each event like a request to be processed allows a given handler to consider the before/after the ‘next’ event handler, with the last stage of the pipeline being the Autofac Core behaviour. You can see an implementation of this in AutoStep, a project I’m working on right now.

In such a model, the three Autofac event ‘types’ would be Preparation, Activation and Release.

An event handler would look like:

void EventHandler(ActivationEventArgs args, Action<ActivationEventArgs> next) 
{
         // Before Activation
        next(args);
        // After Activation
}

A stage in the event pipeline simply doesn’t call ‘next’ if it doesn’t want the default behaviour (or other handlers) to execute.

Sounds like this overlaps a lot with autofac/Autofac#860, which is also about EnableInterfaceInterceptors() and event ordering.

I’ve noodled on this for the last day or so since you made some proposals. In an ideal world I think the fluent builder syntax should just “do the right thing.” This skips over a lot of important details to be sure, but the idea is that the order technically shouldn’t matter.

I’m afraid of changing EnableInterfaceInterceptors to be void for a few reasons:

  • It’d be breaking big time, and while I get that some things do need to be breaking changes, it’d be really nice to avoid it.
  • It will change the fluent semantics. If it returns void, does that mean the InterceptedBy call would have to be before EnableInterfaceInterceptors? If so, that… reads really oddly, like Yoda writing the fluent grammar.
  • It seems like we could very easily run into one of these ordering issues again, and if the answer is “change the thing that needs to be last to void” then we’re going to have a lot of void methods.

Things that randomly come across my head which are probably not good ideas but might spur some inspiration or additional other ideas…

  • What if we had more events in the pipeline? A richer event chain, like pre-activating, activating, post-activating, pre-activated, activated, post-activated… or whatever. Maybe better names. Could we control things in the lifecycle ordering better with more events?
  • What if we could declare event handler order? Like action filters in ASP.NET MVC where you can set an order value and before they get run they get sorted by order. I don’t think that’s something that comes “out of the box” with events but maybe we could do something internally that makes it seem like it’s ordered?

Perhaps @alexmg , @alistairjevans , or @alsami have additional ideas?

Or maybe I’m too focused on it being an ordering issue?

Hi @tillig after investigating about this issue, I see that the reason is about when using .PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies) it will add an ActivatedEvent to inject properties to instance however the instance has been changed when handling ActivatingEvent raised by EnableInterfaceInterceptors so its property is null.

My approach is to change EnableInterfaceInterceptors to raise ActivatedEvent and make its Instance property to be settable(Currently it is read-only field https://github.com/autofac/Autofac/blob/develop/src/Autofac/Core/ActivatedEventArgs.cs#L74).

I aslo has one more concern regarding the issue autofac/Autofac#860. Currently I see the order of fluent API is very important. For example:


builder.RegisterAssemblyTypes(assemblys.ToArray()).Where(t => t.Name.EndsWith("ServiceImpl"))
                    .AsImplementedInterfaces()
                    .InstancePerLifetimeScope()
                    .PropertiesAutowired()   //1
                    .OnActivated(a => Console.WriteLine( a.GetType().ToString() ))   //2
                    .EnableInterfaceInterceptors()       //3
                    .InterceptedBy(typeof(LogInterceptor));

if we swap the order of //1 //2 //3 with each others it can result to different output because it affects the order of raising event. Somehow I see it doesn’t make sense so my additional suggestion is to make the method EnableInterfaceInterceptors return void instead of a fluent API so that we can ensure the interception will run as a last event.

What do you think about it ?