Closed JohnBall007 closed 2 years ago
I'm working through https://stackoverflow.com/questions/32459670/resolving-instances-with-asp-net-core-di-from-within-configureservices which seems comprehensive. The comment: "warning if you need to resolve services in ConfigureServices and that service is a singleton it will be a different singleton to the one your Controllers use!" was one of my observations. Fixing that by creating static access just moved the problem to this one above. I think I just need to define a common container used by both the controllers and my infrastructure/memory-loaded database.
Last attempt to diagnose the problem today. All our tests run using the current code base and Lamar. Only use via the controllers and the system startup is under investigation. When we load the system via the first controller call (injecting IServiceProvider and using it to create our loading object or just injecting the loading provider directly), the system loads as usual, but we then get the runtime errors as above. The system used to work (before and after .net 6.0 migration) by doing a "manual load" including creating and loading a container as we do for testing.
@JohnBall007
1.) I might need a little more context here. Are you wanting to run the Lamar container diagnostics after the app is started? That's easy enough w/ Lamar.Diagnostics + Oakton as explained in the Lamar docs. But I'm not sure that's what you're asking here.
2.) Your code is erroneously disposing the main container somewhere in the async code most likely. A workaround is to set the Container.DisposalLock
property to latch that. I can't think of a super clean way to do that w/ the HostBuilder
mechanics though. You could always do that from an IHostedService
that takes in IContainer
. That might be worth a PR to add that into Lamar. Really just a temporary hack though to help you find the real culprit.
@jeremydmiller Thanks for your update.
1.) Our testing works perfectly, which looks like a normal structuremap/lamar bootstrap. Here we create a container, and then create a single object that bootstraps our system - basically a memory-based repository with access via singletons. I was looking for a seam with the .NET 6.0 startup process where I can access the container that is created as a final step.
I just want to replicate the test implementation. I don't think that was explained well, but does this help?
I've tried a few things to debug. Converting the singletons to static moved the problem to the creation with Func
To find a seam to bootstrap our system, I grabbed an IServiceProvider from a lazy controller call. Now presumably the Lamar.Container found there can't be disposed but using that I create the system, but the rest of the code will be disposed. Controller: System generator:
2.) I've eliminated the async calls for the moment to isolate the problem. We still have the failure and in reality if we find a seam or way to bootstrap within the .NET 6.0 startup, perhaps that can be removed from the design. I think we can do more to isolate the problem at this end (and that will give me time to read up on the PR process!)
I hope this is enough context to explain what we are doing, and perhaps you can see the obvious mistake we are making! Again, many thanks for your assistance!
End-of-day status: I have refactored some of the old code that I was suspicious of, but basically removing any of the failing Func
Ah, because the Func
On Tuesday, January 25, 2022, 05:08:22 AM CST, John Ball ***@***.***> wrote:
End-of-day status: I have refactored some of the old code that I was suspicious of, but basically removing any of the failing Func elements simply moves the problem to the next Func. Outside of the registry definition and the startup/program files, there are no references to Lamar in the code base. It's unclear why we have no other DI problems that complain about the lost container other than the Func. I'm checking with my team how to raise the PR correctly so we can try to workaround to isolate the problem.
— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.Message ID: @.***>
We've had some change with the startup process by putting it in a scoped wrapper, but the FuncContainer.DisposalLock
, I have done a fork on the Lamar branch and will create the pull request after adding a branch. Is that the right approach (I've not done that before and presume there is no actual code I need to touch!)
Well, we now have a workaround that is using a second container. That suggests that the problem is with the disposal of the Func
Nearly there @jeremydmiller . We now have a fix that is just an addition to the registry. Whoo hoo!
Problem summary: We track state using a singleton in our code-base. User-state can last a long time. But we access that state with an object graph from the controller that includes an injected Func
Is this expected behaviour (assuming my analysis is confirmed?) that the DI treats the Func<> as a transient injected object, so when the single use of the class completes, it is disposed, even though a class actively holds its object handle. Reason: Lamar caters to Func<>, but .NET CORE does not?
Here's our definition with one of the Func
@JohnBall007 I understand what's happening with the Func
And if you're in business, I'm closing this.
Thanks @jeremydmiller I appreciate your guidance on this. It must be challenging dealing with inexperienced people like me, but this final response has directed me to a solution. I was fixated on getting the Func to work rather than just solving the problem. We have a codebase with a few factories of this nature and finding a silver bullet (like this) was preferred to migration.
We migrated to Lamar from StructureMap a few months ago and were delighted by the time reduction in our regular integration tests from an hour to 6 minutes. We are now running Lamar 7.1.1 on .NET 6.0. There are two questions I have which I can't see in the sample documentation stemming from our need to load a DB first.
(1) To include Lamar, we added the extension method into program.cs
.UseLamar(new LamarProdRegistry())
to incorporate our existing registry file. It has been working fine and still drives tests without error, but it is unclear what the best way is to create an additional object for prod. We have a few singletons in the registry to configure and not respecting that seemed to violate the lifecycle (validly). Is there a way to run the validation for Lamar after the initiation? .NET 6.0 doesn't make the approach obvious.(2) Before our webapi starts, we want to load a custom DB into memory, and have added an async task. This seems a bad idea, as now we get constant errors about accessing a disposed object, especially with Func creation. Is there an approach recommended for Lamar users? A sample message is: "System.ObjectDisposedException
HResult=0x80131622
Message=Cannot access a disposed object.
Object name: 'This Container has been disposed'.
Source=Lamar
StackTrace:
at Lamar.IoC.Scope.assertNotDisposed()
at Lamar.IoC.Scope.GetInstance(Type serviceType)
at Lamar.IoC.Scope.GetInstance[T]()
"
We are back on this tomorrow. I hope this is a valid way to raise a question!