Friday, December 26, 2008

Unity

I am trying to use the Composite Application Guidance (aka Prism or Composite WPF) for an application at work. It all sounded very interesting and easy to use, but when the rubber hit the road it turned out that I needed to be educated in some of the concepts it uses. For instance, I was not familiar with Dependency Injection. Prism uses Unity as the DI container and I thought I'd highlight some examples on how to use the Unity container:

Type Mapping - registered class:

container.RegisterType<IModule, ConcreteModule>();
IModule ModuleA = container.Resolve(<IModule>);


The first line is a type mapping. Anytime you need IModule, the container will return a ConcreteModule. the ModuleA object will have an instance of ConcreteModule.


Type Mapping - non-registered class:

The following class needs an instance of IModule:

Class ModuleA
{
ModuleA(IModule module)
{
...
}
}

Using Unity you can do the following:
container.RegisterType<IModule, ConcreteModule>();
ModuleA modA = container.Resolve<ModuleA>();


The first line is a type mapping. Anytime you need IModule, the container will return a ConcreteModule.
The second line requests a new instance of ModuleA. ModuleA is not registered with the container. But Unity will look at ModuleA's constructor and see that it needs a IModule. It will then inject an instance of ConcreteModule because of the 1st line's mapping.

Type Mapping - Singleton:
container.RegisterType<IModule, ConcreteModule>(new ContainerControlledLifetimeManager());
Every time you ask for an new instance, the same one is returned.

Named instances:
container.RegisterType<Database, SQLDatabase>("Sql");
container.RegisterType<
Database, OracleDatabase>("Oracle");
This will register two mappings of type Database and name these mappings for future use.

You can access the type by the name like so:
Database db = container.Resolve<Database>("Sql");
In this case, db will be a SQLDatabase.

Resolving all named instances
container.RegisterType<Database, SQLDatabase>("Sql");
container.RegisterType<
Database, OracleDatabase>("Oracle");
IEnumerable<
Database> databases = container.ResolveAll<Database>();
The object databases will contain two objects; one of type SQLDatabase and one of type OracleDatabase.

Adding instances to the container
Use container.RegisterInstance to add pre-created objects to the container. Unity assumes that these objects are to be kept as singleton.
container.RegisterInstance<Database>(new SQLDatabase());
container.RegisterInstance<Database>("Oracle", new OracleDatabase());


Adding dependencies to pre-existing objects with attributes

Assume the following class:

public class ModuleA: Module
{
[Dependency]
public IModuleInfo ModuleInfo {get; set; }
}

In this case, ModuleA has IModuleInfo as a dependency.

Below we're registering the IModuleInfo type with the container, instantiating a new ModuleA and asking Unity to add dependencies to the object:

container.RegisterType<IModuleInfo, TISModuleInfo>();
ModuleA modA = new ModuleA();
container.BuildUp(modA);


modA will then get an instance of TISModuleInfo in its ModuleInfo property.

Specifying dependencies through configuration (no attributes)

Assume the following class:
public class GenericDatabase: Database
{
private string connectionString;
public ILogger Logger { get; set; }
public GenericDatabase(string connectionString)
{
this.connectionString = connectionString;
}
container.RegisterType<ILogger, NullLogger>();
container.RegisterType<
Database, GenericDatabase>();
string connStrFromAppConfig = ConfigurationManager.ConnectionStrings["MyConnectionString"];
container.Configure<
InjectedMembers>().ConfigureInjectionFor<GenericDatabase>( new InjectionConstructor(connStrFromAppConfig), new InjectionProperty("Logger"));
Database db = container.Resolve<Database>();
container.RegisterInstance<Database>("Oracle", new OracleDatabase());


Here we're injecting dependencies through the Configure method of the container. Since the connection string is constructor parameter, we must use the InjectionConstructor class, and because Logger is just a property in GenericDatabase, we use the InjectionProperty class.

Nested containers

If the child container doesn't have what you're asking from it, it will look at the parent container.
To register child containers:
UnityContainer parent = new UnityContainer();
IUnityContainer child1 = parent.CreateChildContainer();
IUnityContainer child2 = parent.CreateChildContainer();

parent.RegisterType<IModule, GenericModule>(new ContainerControlledLifetimeManager());
child1.RegisterType<
IModule, ModuleA>(new ContainerControlledLifetimeManager());

IModule module1 = child1.Resolve<IModule>();
IModule module2 = child2.Resolve<IModule>();


In the code above, module1 will be of type ModuleA, and module2 will be of type GenericModule, since child2 looks at the parent container because it doesn't have the type IModule registered.

No comments: