-
-
Notifications
You must be signed in to change notification settings - Fork 838
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding decorator moves OnActivated event invocation #1361
Comments
Well that's... weird. Admittedly, it's been a while since I've looked at the events and ordering, I'd have guessed that you'd get
Like, I'll agree that changing the order seems like a bug. I haven't had a chance to run the code myself, but the repro looks straightforward and I trust you. ;) I don't have any ideas on easy fixes off the top of my head. It'll be something to dig into. If you have the time and inclination, we'd take a PR with a fix. Probably need to add some tests to make sure it doesn't get regressed later. |
To be completely honest, I originally expected
To be frank, I couldn't even easily find the place where it gets fired (I originally debugged this on our own application, which is kinda complex, the diagnostic trace for this particular failing resolve operation is 2.5 Mb in a text file). We're currently working around this on our side by breaking the circular dependency (which is, honestly, a better option anyway, and I'm glad it got surfaced), but I thought I should put this out for somebody else who might hit the same wall. I try giving it a go later, though. |
The reason for OnActivated firing at the end of everything (without a decorator) is actually to preserve legacy behaviour (pre 5.0 I think) where all Activated events fired when all services in a dependency chain had been resolved, just before the resolve completes; this permits resolves that happen inside the Autofac/src/Autofac/Builder/RegistrationBuilder{TLimit,TActivatorData,TRegistrationStyle}.cs Lines 525 to 533 in 0c79d7b
The reason this changes when the decorator gets involved is because the decorator middleware creates a new Autofac/src/Autofac/Features/Decorators/DecoratorMiddleware.cs Lines 109 to 120 in 0c79d7b
It does look weird though, I grant you, but it's because of the decorator supporting circular dependencies inside itself. We could change |
Ah, right, some of that is coming back to me now. Why it's happening makes sense in this context, but I'll also agree that it's potentially unexpected. Hmmm. Not sure how to rectify those two things. Perhaps it's a matter of documentation more than code fix? I'm not sure we've actually got documentation on how decorators affect the lifetime event chain and I don't see any tests that enforce event ordering, just that they happened. Maybe this is the opportunity to designate what we believe the correct ordering to be in a complex resolve chain and enforce that? (Even if the answer is "it's functioning correctly right now?") |
Yes, this is exactly what we do now: we resolve something in
I think that'd be the desired behavior (if by "at the end" you mean "at the end of the whole stack, after outer service), but we should check what it breaks. It definitely doesn't break the outer service (more specifically, in our case it would fix it), but is there any decorator-related scenario where it will break the decorator? I cannot come up with one right now. |
I think you may be right about it being the desired behaviour generally, I'm trying to think whether someone could do things in OnActivated that could impact the decorator, but I don't believe so... I don't have time right now to try the change out @srogovtsev, do you want to try that OnActivated change and see if any tests fail? |
I'll try to, ahem, try this out later this week. |
In short, it doesn't work this way: we don't have The simple workaround would be to just resolve I'll keep digging. |
You could create a new request context from the operation for each activated handler? |
Like this? ctxt.Operation.CurrentOperationEnding += (sender, evArgs) =>
{
var surrogateContext = new DefaultResolveRequestContext(evArgs.ResolveOperation,
evArgs.ResolveOperation.InitiatingRequest, evArgs.ResolveOperation.CurrentScope,
evArgs.ResolveOperation.DiagnosticSource);
var args = new ActivatedEventArgs<TLimit>(surrogateContext, ctxt.Service, ctxt.Registration, ctxt.Parameters, newInstance);
handler(args);
}; Still doesn't help, same error ("This resolve operation has already ended"). This kinda makes sense, because in Autofac/src/Autofac/Core/Resolving/ResolveOperation.cs Lines 265 to 275 in 0c79d7b
we first "end" the operation, and then fire the It works, though, if I switch the assignment and invocation (i.e., first invoke the events, and then "end" the operation), but I need to think on the consequences. Also, all the remaining tests pass but one:
Will keep digging. |
…esAutowired when decorator present
In fact, there's a much more visible bug arising from the same logic (we just don't use private class OuterService
{
public OuterService(IInnerService service)
{
}
}
private interface IInnerService
{
}
private class InnerService : IInnerService
{
public InnerService(InnermostService service)
{
}
}
private class InnerServiceDecorator : IInnerService
{
public InnerServiceDecorator(IInnerService toDecorate)
{
}
}
private class InnermostService
{
public OuterService Outer { get; set; }
}
var builder = new ContainerBuilder();
builder.RegisterType<OuterService>()
.SingleInstance();
builder.RegisterType<InnerService>()
.As<IInnerService>();
builder.RegisterType<InnermostService>()
.SingleInstance()
.PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies);
builder.RegisterDecorator<InnerServiceDecorator, IInnerService>();
using var container = builder.Build();
container.Resolve<OuterService>(); Fails with
which is exactly how we stumbled upon this in the first place (we just don't use Considering that |
Given the following service/container setup:
I observe the following output on resolving
OuterService
:Note that activation event fires last (which allows us, for example, resolve circular dependency, were
Inner
to depend onOuter
).Now we add a decorator (everything else stays the same):
The output is:
Now the activation event fires after decorator, not the last in the pipeline (which in our case brings back circular dependency).
Observed on Autofac 6.2 and 6.5.
The text was updated successfully, but these errors were encountered: