Reputation: 926
What is the proper way to handle DI for dependencies that need to be created/disposed everytime they are used? That is to say: dependencies that should only ever be used in the context of a using statement...
public void Foo()
{
IUnityContainer myContainer = GetContainer();
using (IDataStore store = myContainer.Resolve<IDataStore>())
{
//Do work...
}
}
My Specific Problem
My app's DbContext
implements an IDataStore
interface. Because DbContext is not thread safe, and my business classes need to be, my business classes need to create a new IDataStore
instance any time they need to interact with the database.
I've tried 3 different approaches, but they all have problems:
IDataStore
whenever it is needed. As an added bonus, this IUnityContainer can be used in static methods where DI is usually not possible.IDataStore
whenever it is needed.With Approach 1, everything works. But this is apparently an anti-pattern. There are a lot of explicit .Resolve<IDataStore>()
calls happening in the business code. It also means that every business object is stuck using the same IUnityContainer, although right now that doesn't seem to be an issue.
With Approach 2 All of my business classes need to pass around the same IUnityContainer whenever they create instances of other business classes. It is also apparently considered a bad practice to DI you IoC container.
With Approach 3, I cannot create new instances of IDataStore from within the class. If I use using
blocks, this means that I can only use the IDataStore once (because it will get disposed). If I don't use using
blocks, the IDataStore instance will likly be created on one thread then used on another (causing threading issues for the DbContext). Not only that, but now each of my business classes now need to have a IDataStore passed to them in their constructors (or have a property set). This makes it even MORE likely that threading issues will occur, because many business classes instantiate other business objects and must do so using their own IDataStore.
To me, it seems that approach 1 is the best of these three, it allows me to create a new instance of IDataStore every time I resolve it. But if this is a bad practice, I want to know why. Is there a better approach to do this?
Update
I'm thinking now that I may need a IDataStoreFactory interface that is dependency injected instead of the IDataStore itself. This would mean that the concrete Factory class would have to instantiate a specific implementation of IDataStore...
public Interface IDataStoreFactory
{
IDataStore CreateNew();
}
public class MyDbContextFactory: IDataStoreFactory
{
public IDataStore CreateNew()
{
return new MyDbContext();
}
}
And then in my other class...
public class SomeClass
{
private IDataStoreFactory factory;
public SomeClass(IDataStoreFactory factory)
{
this.factory = factory;
}
public void Foo()
{
using (IDataStore store = factory.CreateNew())
{
//Do work...
}
}
}
Is this a valid approach to solve this problem, or is it also a bad practice? I haven't really used DI (or factories) before, so I want to make sure that I'm not doing something that is going to bite me down the road.
Upvotes: 3
Views: 135
Reputation: 233377
Dependency Injection is essentially a set of patterns related to the Dependency Inversion Principle. As Robert C. Martin explains in APPP, chapter 11: "clients [...] own the abstract interfaces". What this means is that an interface is defined by what the client needs, not by what any particular implementation provides.
Specifically, it means that interfaces ought never to derive from IDisposable
, because clients never need their dependencies to be disposable; that concern is exclusively related to a concrete implementation.
The point here is that IDataStore
should not derive from IDisposable
. Instead, it should only expose methods that the client needs. Let's call such a method Bar
.
Any client should be able to use any implementation of IDataStore
:
var baz = this.store.Bar(qux);
where this.store
is an instance of IDataStore
.
How do you deal with objects that need disposal, then?
You use a Decoraptor. Specifically, you create an implementation of IDataStore
that takes care of the lifetime management of your database context:
public class DataStoraptor : IDataStore
{
public IBaz Bar(IQuz qux)
{
using (var ctx = MyDbContext())
{
return ctx.Bar(qux);
}
}
}
This simplified example assumes that IDataStore
only defines a single member called Bar
, but I'm sure you can extrapolate from the example.
This example has the drawback that it creates a new MyDbContext
for every method call. This is safe, but perhaps not the most efficient use of resources. If, for example, you have several clients using IDataStore
in the same thread, you may want to be able to reuse a single instance of MyDbContext
in that thread. In that case, you can use a variation of Decoraptor that takes an Abstract Factory as a dependency. You can then implement that Abstract Factory in such a way that it returns instances that are scoped to a particular thread (or other sort of scope).
That variation is much more complicated, though, so make sure you need it before adding that complexity. Measure performance, and stick to the simpler implementation shown above as long as performance is good enough.
Upvotes: 3