I am looking to see how Ioc/Di can simplify wiring the following classes I use often.
Consider a library that has an abstract notion of an Entity and an interface for a data access object:
public abstract class EntityWithTypedId<TId> : IEntityWithTypedId<TId>{...}
public interface IDao<T, TId> where T : IEntityWithTypedId<TId>
For the dao, I have one implementation for NHibernate as well as a fake dao I find useful for testing:
// has an NHib implementation
public class Dao<T, TId> : ID开发者_JAVA技巧ao<T, TId> where T : EntityWithTypedId<TId> {...}
public class DaoFakeBase<T> : IDao<T, int>, IDisposable where T : IEntityWithTypedId<int> {...}
I currently do the following to define an Entity and Dao type for a given project:
/// <summary>
/// <see cref="IEntityWithTypedId{IdT}"/> with an int for an id
/// </summary>
[Serializable]
public abstract class Entity : EntityWithTypedId<int>
{
}
public class Dao<T> : Dao<T, int> where T : Entity
{
protected Dao(ISessionFactory sessionFactory) : base(sessionFactory) { }
}
Can I use a DI tool to define the Entity instead? Can someone show me a code sample of how to do it if so?
Can you also lay out how I might tell my test assembly to use DaoFakes and production to use NHib.Dao
I have been looking at Windsor, mostly because NHibernate contrib projects use it, but am also interested in MEF, AutoFac and Ninject, in that order. I realize that MEF is not an IoC container in the sense that Windsor is. From what I can see with Windsor I would use Installer classes, maybe an EntityInstaller and a DaoInstaller, although I might be missing a FActory type of object here too.
Cheers,
BerrylUPDATE @ KeithS
Are you saying to change something like:
class MyViewModel(IDao<MyClass, int> dao) {...}
becomes something like
class MyViewModel(Func<IDao<MyClass, int>, obj> getDaoFunc) {
_dao = getDaoFunc(this);
}
In your example...
class MyViewModel(IDao<MyClass, int> dao) {...}
...IDao
would get resolved at runtime based on a previous registration within your container. The syntax for a Prism/Unity implementation is below...
IUnityContainer.RegisterType<IDao..., DaoFakeBase...>();
The RegisterType
takes place within IModule.Initialize() of a given module as defined in the UnityBootstrapper
class.
protected override IModuleCatalog GetModuleCatalog()
{
ModuleCatalog catalog = new ModuleCatalog();
catalog.AddModule(typeof(project.Init));
return catalog;
}
You can also register a given type based on a lifetime manager; to behave as a Singleton...
IUnityContainer.RegisterType<IShellController, ShellController>(new ContainerControlledLifetimeManager());
...where the IShellController
resolved instance will remain the same returned instance throughout the lifetime of the IUnityContainer
.
UPDATE:
Using your code the registration would look like this...
public interface IDao<T, TId> where T : IEntityWithTypedId<TId>
{ }
public class Dao<T, TId> : IDao<T, TId> where T : EntityWithTypedId<TId>
{ }
public class TId
{ }
public abstract class EntityWithTypedId<TId> : IEntityWithTypedId<TId>
{ }
public interface IEntityWithTypedId<TId>
{ }
IUnityContainer.RegisterType<IEntityWithTypedId<TId>, EntityWithTypedId<TId>>();
IUnityContainer.RegisterType<IDao<IEntityWithTypedId<TId>, TId>, Dao<IEntityWithTypedId<TId>, TId>>();
IDao<IEntityWithTypedId<TId>, TId> dao = IUnityContainer.Resolve<IDao<IEntityWithTypedId<TId>, TId>>();
I would not use IoC to register the relationship between DAOs and their types (which is basically what you'd be doing). This will lead to you using the IoC container as a "service locator", a known anti-pattern where you pass the IoC container into objects that will use it to get the DAO they need.
I think the best way to simplify this from a consumption perspective would be to define a strategy pattern, using a factory class or method:
public Dao<T, TId> GetDaoFor<T, TId>(T objectInstance) where T:EntityWithTypedId<TId>
{
//Here, you could use a Dictionary, Linq with some reflection, etc.
}
This one method can be injected as a delegate into classes dependent upon DAOs. The difference is that classes that need a DAO are dependent on a method that can give it to them, which can be provided by the IoC container; they are NOT dependent on the container itself (which is the primary source of evil inherent in the "service locator" pattern). This reduces the number of things you'd have to change if you re-wrote how you got these DAOs.
EDIT: A bit off-topic, but I opened the door:
The service location pattern is generally to be avoided, because it results in code that relies on the service locator. For instance, the following is common in code where the IoC has been exposed at child levels:
private IDependency _dependency;
public IDependency MyDependency
{
get {
_dependency = _dependency ?? IoC.Resolve<IDependency>();
return _dependency;
}
}
While this seems like a nice pattern (dependencies are lazily initialized, consuming code doesn't need to know about dependencies of the child, and you always* get a reference), this code will ALWAYS require the IoC singleton to exist. You can change the IoC framework behind it, you can remove the third-party tool altogether and roll your own, but this class will always require something on which to statically call Resolve<IDependency>()
.
You also don't ALWAYS get a reference; you get a reference only if you properly registered IDependency with IoC. This produces two more weaknesses; 1) you don't know what the class will need without opening it up, and 2) if/when the call fails, it will fail deep in the bowels of the dependent class' inner workings. If you develop a new class, and plug it into IoC, it may pass integration, and even work in production for a while, until you start getting weird "object reference set to null" errors in really weird places in code, which are, trust me, a nightmare to debug.
Lastly, unit-testing service-locator-pattern code is more difficult, for the simple reason that you must mock the service locator as well as the dependency provided by the service locator. You can leave the production service locator in use, and simply register mocked classes as the dependencies, but that's not a unit test; the test relies on, and thus to some extent tests, that the integration of the class and its service locator works as expected. That's an integration test.
By contrast, dependency injection patterns free you from any dependency on how dependencies are resolved. The only requirement (in constructor-injection) is that they be around when the class is created. This has several advantages:
- If not using an IoC framework, you have to know what the class will need to instantiate it.
- If using an IoC framework, you get a runtime error when attempting to instantiate the dependent class, not sometime later when the object actually gets resolved.
- When testing a dependent class, you can more easily mock the dependency, because the dependency does not have to be fed in via the service locator.
- You can in most IoC frameworks still lazily initialize dependencies by providing a factory method instead of the actual dependency to the constructor. The above pattern then calls that delegate, which could come from anywhere, instead of a static named method which is satisfied by one and only one construct in the entire codebase.
精彩评论