autofac / Autofac.Extras.DynamicProxy

Interceptor and decorator support for Autofac IoC via Castle DynamicProxy
MIT License
106 stars 33 forks source link

Delay creation of class interceptor proxies until activating #9

Closed samuelmueller closed 2 years ago

samuelmueller commented 8 years ago

Hi Guys

I've noticed that all ClassInterceptor Proxies getting created while Registration. In my case this is a Performance Issue because i'm intercepting every of my ViewModels. Now i have a noticable delay on the start up. I've seen that the InterfaceInterceptors are getting created in a Activating Handler. Is there a way to create the class proxies also in a Activating Handler or is there another way to improve the performance?

tillig commented 8 years ago

For interface interceptors, we have to intercept anything that's registered as that interface - it's not tied to a particular registration. For example, if you have...

builder.RegisterType<First>().As<IService>();
builder.RegisterType<Second>().As<IService>();

...and then you register an interface interceptor on IService, we won't actually know the whole "thing" that we're intercepting until resolution time.

On the other hand, when you register a class interceptor, we know what you're wrapping so we can create the proxy at the time.

It may be possible to move the class interception proxy creation thing to later, but it'd be trading startup performance for runtime performance. We can skip certain checks ("Has the class interceptor been created yet?") if we know it's getting set up at container build/startup time, which avoids some locking and overhead that would be there in runtime.

Before we commit to anything, we should probably do a test to try it out perf-wise - come up with a solution, do a before and after check to see what the impact of the changes might be.

Right now we're really struggling with the .NET Core RC2 conversion so this probably won't happen for a while. If you are super interested, I might say try it out and see what happens - maybe do some tests of your own to see if you can fix startup perf without affecting runtime perf. Otherwise, we'll have to come back to this, probably after .NET Core has settled out.

RogerKratz commented 3 years ago

Was about to add an issue/a request on the very same thing (=postpone creating the proxy until first resolve call) but noticed that this one already existed. Any news about this one?

tillig commented 3 years ago

Nope, but we're open to pull requests.

tillig commented 2 years ago

I spent a bit of time today monkeying around with this trying to get it to work. I think that we're somewhat locked in with the current design.

With an interface proxy, basically what's getting generated is a class - from scratch - that implements a given interface. It's fairly cacheable in that it only has one constructor parameter, and that's the thing it proxies. Basically it's this:

public class GeneratedClass : ITheInterfaceToProxy
{
  public GeneratedClass(ITheInterfaceToProxy realThing);
}

Since it's a very specific thing being proxied and it can be created with a one-liner, we can shove that down into the Activation event and swap one instance of the interface for another - swap the "real thing" with a generated proxy wrapped around the real thing.

The key there, though, is that the "real thing" was already resolved.

OK, so for a class proxy we're using the only inheritance-based proxy available, the standard class proxy without target. We need the inheritance-based one so you can cast the proxy back to the original type. It's basically:

public class GeneratedClass: RealThing
{
  public GeneratedClass(IParam someParameter):
    base(someParameter){}

  public override void SomeMethod()
  {
    // pre-intercept, then
    base.SomeMethod()
    // post-intercept
  }
}

Autofac then resolves the GeneratedClass which can cast down to RealThing and everyone is happy.

The problem is, to activate and resolve the GeneratedClass, we need to know what that type is up front. There's no functionality in Autofac at the moment to go, "Hey, before you try to resolve RealThing, I was just kidding about that - I need you to really resolve this other type. That's why you see the class interceptor actually has the type generated up front - so we know about it and can use that during resolution.

I could see the potential for something like a source generator to start coming into play here, like to generate the proxy classes at compile time rather than at runtime, but that's a bit outside my capabilities at the moment and I kind of expect Castle or other proxy projects to kind of fill that gap.

It's not-technically-impossible, I think, to figure out some way to add to the resolve pipeline some way to swap out the implementation type, but then we'd also have to think about the perf hit tradeoffs, like:

In the end, while I think the idea is good, I don't think we can really do it right now.

I'm still open to a PR if someone has a "Eureka!" moment about it, but I'm going to close the issue since there's no plan at the moment to take this on.