ZiggyCreatures / FusionCache

FusionCache is an easy to use, fast and robust hybrid cache with advanced resiliency features.
MIT License
1.56k stars 84 forks source link

[QUESTION] ObjectDisposedException when using Eager Refresh + DI with scoped services #265

Closed gercobrandwijk closed 3 weeks ago

gercobrandwijk commented 3 weeks ago

Describe the question

After enabling Eager Refresh I started seeing ObjectDisposedException, because the services that are used in the factory are being disposed while the factory is still runinng, but the request that trigger the Eager Refresh already finished.

To Reproduce

Question

What would be the advised way to handle scenario's like this?

Versions

I've encountered this issue on:

jodydonetti commented 3 weeks ago

Hi @gercobrandwijk and thanks for using FusionCache!

In general when using eager refresh or soft/hard timeouts a factory can, as you observed, finish running after the original GetOrSet method call.

The main example is EF with ASP.NET, where the DbContext is usually scoped and automatically provided by the DI container.

The solution in this case is to use an IServiceScopeFactory and be explicit about the lifetime, disposing it manually inside the factory (normally with a using statement).

Hope this helps.

ps: one question, if I may: why are you not on v1.0+? Just out of curiosity.

jodydonetti commented 3 weeks ago

Check this issue also https://github.com/ZiggyCreatures/FusionCache/issues/119#issuecomment-2173530857

jodydonetti commented 3 weeks ago

I also opened an issue in the EF repo for potential alternative solutions, see here.

gercobrandwijk commented 3 weeks ago

The solution in this case is to use an IServiceScopeFactory and be explicit about the lifetime, disposing it manually inside the factory (normally with a using statement).

Thanks, makes a lot of sense obviously. Tried it out, and it works fine :)

Check this issue also #119 (comment)

Sorry that I missed that one, I even searched for it in the issue list, but was not able to find it for some reason.

I also opened an issue in the EF repo for potential alternative solutions, see here.

The possible solution that you are suggesting there is I think not working in all scenarios. Imagine a factory that look like this: async () => { await Task.Delay(5000); return await myService.GetAllItems(); } In this scenario, when the DbContext/Service wants to be disposed, no query is actually running yet (because it still has to start), so it will still dispose and then after 5 sec give the same Disposed exception. Therefor just managing the scope by yourself would simply be the best I think.

ps: one question, if I may: why are you not on v1.0+? Just out of curiosity.

We still have to update, no particular reason ;)

jodydonetti commented 3 weeks ago

Thanks, makes a lot of sense obviously. Tried it out, and it works fine :)

Cool, happy it helped!

The possible solution that you are suggesting there is I think not working in all scenarios. Imagine a factory that look like this: async () => { await Task.Delay(5000); return await myService.GetAllItems(); } In this scenario, when the DbContext/Service wants to be disposed, no query is actually running yet (because it still has to start), so it will still dispose and then after 5 sec give the same Disposed exception. Therefor just managing the scope by yourself would simply be the best I think.

Although this specific example is kinda exotic, I get your point and it makes sense: I would say the general rationale is that a deferred (automatic) dispose would still not handle queries that are not started yet when the scope ends. Probably a manual scope management is the best solution.

We still have to update, no particular reason ;)

Got it 🙂