-=//mawi.org//=-
 Wednesday, July 11, 2007

I introduced the factory facility of windsor (or actually castle microkernel) at the end of my previous project and I submitted some patches on it's programmatic use, and there is not so much documentation on it (well, not any on that part) so a little writeup was in order.

What is windsor? 

If you don't know what a container/IoC/DI is, and like to know concepts, check this out. If you don't know and want an example of windsor, check this out. A container is a little piece of frameworkish code that takes care of constructing your instances for you in a OO program, so you don't have to keep track of what goes where which simplifies alot.

 

You use it by first describing the classes you use and then asking for an instance of one. For example, you can tell it that you have a class D that uses an instance of class A and C. Class C in turn needs an instance of a class B. This is done in some init area of your code. Then in the program, you can say give me a D (resolve), and it will create A, create B and use it to create C and use these to create a D and return to you. Example classes:

 
public class A { }
public class B { }
public class C
{
   public C(B instanceOfB) { }
}
public class D
{
   public D(A instanceOfA, C instanceOfC) { }
}

 

Example use in test:

 

[Test]
public void MostBasicExample()
{
   IKernel container = new DefaultKernel();
   container.AddComponent("keyForClassA", typeof(A));
   container.AddComponent("keyForClassB", typeof(B));
   container.AddComponent("keyForClassC", typeof(C));
   container.AddComponent("keyForClassD", typeof(D));
 
   D anAInstance = 
      container.Resolve("keyForClassD", typeof(D)) as D;
 
   Assert.That(anAInstance, Is.TypeOf(typeof(D)));
}

 

Concepts/acronymns are "inversion of control" and "dependency injection", I like to refer to it as just a container, it's imprecise and contextual instead of sacrificing simplicity to get precise, but that's who I am. It's just oo, really. There are several such pieces of container code for .NET; castle windsor, spring.net and the objectbuilder framework from ms that is used in their application blocks, etc. I personally favor the castle project implementation because of it's ease of use and simplicity.

 

Use your own wrapper 

I feel the default container lacks some stuff, so I wrap it and add generics, methods that omit the key (sometimes I know that I will only have one implementation of a class in a system) and that look at metadata for attributes in an assembly and auto register. I make my wrapper a singleton. I slap on a custom attribute I call "ContainerComponent" on the example classes above and the test becomes:

 

[Test]
public void MostBasicExample()
{
   Container.Instance.AddFromAssemblyOf<A>();
   D anAInstance = Container.Instance.Resolve<D>();
 
   Assert.That(anAInstance, Is.TypeOf(typeof(D)));
}

(In fairness, there have been some recent commits that move toward this.)

 

The factory facility example 

Windsor can be extended via facilities, small pieces of code that add small pieces of functionality. The factory facility allows us to tell windsor to use a certain method (instance or static) on a class to create an instance of a class. I added some simple methods to add factories programmatically. Here are some example classes:

 
public interface IUserRepository
{
   void UpdateUser();
}
 
public class UserRepositoryImplementation : IUserRepository
{
   public void UpdateUser()
   {
 
   }
}
 
public class UserRepositoryFactory
{
   public UserRepositoryImplementation Create()
   {
      return new UserRepositoryImplementation();
   }
}

Factory support facility programmatically 

Here is an example test using microkernel and programmatically using the factory support facility (currently in trunk):

 

[Test]
public void FactoryResolveWithProposedFacilityPatch()
{
   IKernel kernel = new DefaultKernel();
   FactorySupportFacility facility = 
      new FactorySupportFacility();
   kernel.AddFacility("factory.support", facility);
 
   string userRepositoryKey = "userrepository";
   facility.AddFactory<IUserRepository, UserRepositoryFactory>
      (userRepositoryKey, "Create");
 
   IUserRepository userRepository = kernel.Resolve(
      userRepositoryKey,
      typeof(IUserRepository)) as IUserRepository;
 
   Assert.That(userRepository, 
      Is.TypeOf(typeof(UserRepositoryImplementation)));
}

 

I think that is messy, if we use a little container wrapper and say that we have don't need the key to differentiate

between implementations, it becomes:

 

[Test]
public void FactoryResolveWithContainer_NoKey()
{
   Container.Instance
      .AddFactory<IUserRepository, UserRepositoryFactory>
      ("Create");
 
   IUserRepository userRepository = 
      Container.Instance.Resolve<IUserRepository>();
 
   Assert.That(userRepository, 
      Is.TypeOf(typeof(UserRepositoryImplementation)));
}

 

Fuller example

Here are some example classes for a fuller example:

 

   public class FileStorageService
   {
 
   }
   public class AnnounceService
   {
      private FileStorageService _storage;
      private IUserRepository _userRepo;
 
      public IUserRepository UserRepo
      {
         get { return _userRepo; }
      }
 
 
      public AnnounceService( FileStorageService fileStorage,
         IUserRepository userRepo )
      {
         _storage = fileStorage;
         _userRepo = userRepo;
      }
 
   }

 

Example:

 

[Test]
public void FactoryResolveWithContainer_FullerExample()
{
   Container container = Container.Instance;
   container.AddFactory<IUserRepository, 
      UserRepositoryFactory>("Create");
   container.AddComponent<FileStorageService>();
   container.AddComponent<AnnounceService>();
 
   AnnounceService announceService = 
      container.Resolve<AnnounceService>();
 
   Assert.That(announceService.UserRepo, 
      Is.TypeOf(typeof(UserRepositoryImplementation)));
}

 

Parameters 

You can set parameters for the factory method at configuration time and at runtime. Will continue with that and post code later on, stay tuned.

.NET | Code
7/11/2007 5:35:41 PM (Romance Daylight Time, UTC+02:00)  #    Comments [1]  |  Trackback