Typed factory should not track or dispose the objects it creates
See original GitHub issueIf an object is created by Castle Windsor calling the typed factory’s resolve/create method, then I agree that Castle Windsor should be the one to release/dispose.
If an object is created by me calling the typed factory’s resolve/create method, then I should be the one to release/dispose.
In other words, the returned object’s lifespan should not be tied to the factory but to whatever caused the call to the factory. That could be me manually (in which case I manually manage the lifespan), or it could be Castle Windsor resolving a component (in which case the lifespan should be tied to the component that needs the object, not to the factory itself). In neither case should the typed factory itself be tracking the objects it creates.
If I use a factory to construct an IDisposable
, I am the one responsible to put it in a using
statement (if it’s a local) or call Dispose
(if it’s a field). It’s the expected pattern in .NET with any IDisposable
-returning creation method.
In my case, that is kind of the whole point of injecting a Func<DbContext>
- so that I have absolute control of creation and disposal of every DbContext. I need that control.
The only reason I’m not newing up the DbContext myself in the using
block is that the DbContext has a dependency, but I need the same semantics:
using (var db = new MyDbContext(thingsToInject))
{
var query1 = await db.Thing1();
var query2 = await db.Thing2();
...
await db.Command();
}
Should have the same disposal semantics, but doesn’t:
using (var db = dbContextFactory.Invoke()) // Use Castle Windsor to handle DI
{
var query1 = await db.Thing1();
// Problem happens when Castle Windsor (rightfully) disposes dbContextFactory because the viewmodel is closed
// db gets disposed too early! I need it to last until the end of the using block! (duh?)
var query2 = await db.Thing2();
...
await db.Command();
}
I need at least an option to disable tracking objects instantiated by me from typed factories, but I also think it’s a more preferable idiom to not track them and that tracking object returned from manual calls should be disabled completely or at least disabled by default.
Issue Analytics
- State:
- Created 7 years ago
- Comments:15 (14 by maintainers)
Top GitHub Comments
I don’t think that
Func<T>
forces the container to do anything. What I do think is that semantically,Func<T>
represents the ability to obtain multipleT
s and also control when in time theT
s are created. IfFunc<T>
is not used for multiple instances or for controlling the lifestyle, you should just useT
because it is semantically more appropriate.Now that is a very good point which I didn’t catch earlier, which does seriously challenge my position. You are right about this. What an oversight. I actually do need to release DbContexts, so I’m forced to use interface factories or wait for the delegate factory to be disposed and hope that isn’t too long.
This seems pretty much concluded as a discussion, but it was a good read. I use interface-based typed factories for the very reason that was ultimately mentioned
It also means that if my DbContext was a singleton instead of transient, it could potentially be kept around rather than new’d and disposed each time.
One other thing typed factories offer is the ability to accept parameters as part of the
factory.create(..)
method and have them flow through to the eventually created component. I don’t use it often but it’s been handy when required.Lifetime then isn’t managed by IDisposable, but that’s OK because the factory interface is your code and it’s not hidden - it’s actually part of the contract to get a new widget constructed (Windor’s automatic implementation of the interface is what makes this all very elegant IMHO).