Dependency Injection and Factories
What is a decent way to handle conditional sub-factories with a DI approach. The scenario is the loader object that gets injected in开发者_如何学JAVAto an entry is dependent on the settings of that entry. Originally I was injecting the IoC container into the factory and using that to resolve based on naming conventions. However, I'd really like to keep the factory clean of the container.
The factory is injected into a class that will load all the settings from a database, and then use the factory to create a set of entries. The settings determine which loader will be used inside that given entry.
Edit: Changing code to better highlight the actual question. The issue is that multiple database managers must be supported simultaneously, if this were not the case then it would be simple. The database manager type is determined by the entry settings stored for a particular entry.
public class Entry : IEntry
{
private ISomething loader;
public Entry(ISomething something)
{
this.loader = something;
}
}
public class EntryFactory : IEntryFactory
{
IEntry BuildEntry(IEntrySetting setting)
{
//Use setting object to determine which database manager will be used
}
}
public class EntryManager
{
public EntryManager(IEntryFactory entryFactory)
{
var entrySettings = this.settings.Load();
foreach(var setting in entrySettings)
{
this.entries.Add(entryFactory.BuildEntry(setting));
}
}
}
I had considered having the sub-factories register with a primary factory and resolve them that way, but I don't know if there is a better approach.
What I typically do is create a wrapper for my DI container... Something like IDependencyResolver... And inject it into my factories. Then, you can have an implementation like StructureMapDependencyResolver that does the lifting. I like this better than injecting the container itself because this keeps me free to change DI containers (almost) instantly. At least my factories don't have to change.
public interface IDependencyResolver
{
T Resolve<T>();
}
public class UnityDependencyResolver : IDependencyResolver
{
private readonly IUnityContainer _container;
public UnityDependencyResolver(IUnityContainer container)
{
_container = container;
}
public T Resolve<T>()
{
return _container.Resolve<T>();
}
}
This approach is so flexible that you could implement your own Dependency Resolvers and inject them manually.
public class ManualDependencyResolver : IDependencyResolver
{
public T Resolve<T>()
{
if (typeof(T)==typeof(ITransactionRepository))
{
return new CheckTransactionRespostory(new DataContext());
}
throw new Exception("No dependencies were found for the given type.");
}
}
It depends on what your DI framework allows and you didn't specify it. Using Autofac delegate-based registration, I came to the following solutions. Note that ILoaderFactory
and IEntryFactory
have been replaced by simple Func<>
factories in both cases.
Solution 1, using two factories:
public class EntryManager
{
public EntryManager(Func<ILoader, IEntry> entryFactory, Func<Settings, ILoader> loaderFactory)
{
var entrySettings = this.settings.Load();
foreach(var setting in entrySettings)
{
this.entries.Add(entryFactory(loaderFactory(setting)));
}
}
}
private static ILoader SelectLoader(IEntrySetting settings)
{
// your custom loader selection logic
}
var builder = new ContainerBuilder();
builder.RegisterType<EntryManager>();
builder.RegisterType<Entry>().As<IEntry>();
builder.Register((c, p) => SelectLoader(p.TypedAs<IEntrySetting>()));
IContainer container = builder.Build();
container.Resolve<EntryManager>();
Solution 2, using only one factory:
public class EntryManager
{
public EntryManager(Func<IEntrySetting, IEntry> entryFactory)
{
var entrySettings = this.settings.Load();
foreach(var setting in entrySettings)
{
this.entries.Add(entryFactory(setting));
}
}
}
private static ILoader SelectLoader(IEntrySetting settings)
{
// your custom loader selection logic
}
var builder = new ContainerBuilder();
builder.RegisterType<EntryManager>();
builder.Register((c, p) => new Entry(SelectLoader(p.TypedAs<IEntrySetting>()))).As<IEntry>();
IContainer container = builder.Build();
container.Resolve<EntryManager>();
精彩评论