Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#5.0

TypedFactoryFacility in Castle.Windsor

5.00/5 (2 votes)
23 Feb 2015CPOL3 min read 17.3K   170  
How to use TypedFactoryFacility using Castle.Windsor

Introduction

This article demonstrates a complete example of TypedFactoryFacility.

Background

With TypedFactoryFacility, we specify an interface and don't provide any implementation. By using AsFactory extension method we're telling Windsor to provide its own implementation that adheres to the default convention for typed factories.

Using the code

C#
static void Main(string[] args)
{
      ResolveDependencies();
      _factorySelector = _container.Resolve<IFactorySelector>();

      var sqlStorage = _factorySelector.GetFactory(Storage.SqlDatabaseStorage);
      sqlStorage.ImportProduct(new Product());

      var azureTableStorage = _factorySelector.GetFactory(Storage.AzureTableStorage);
      azureTableStorage.ImportProduct(new Product());

      Console.ReadLine();
  }

  public static void ResolveDependencies()
  {
       _container = new WindsorContainer();
       _container.Install(FromAssembly.This());
   }

The factory :

C#
public interface IProductImporter
{
    void ImportProduct(Product product);
}

The concrete factories:

C#
public class SqlDatabaseImporter : IProductImporter
{
     public void ImportProduct(Product product)
     {
         Console.WriteLine();
         Console.ForegroundColor = ConsoleColor.Green;
         Console.WriteLine("Factory --> Import to Sql Database");
     }
}

public class TableStorageImporter : IProductImporter
{
     public void ImportProduct(Product product)
     {
         Console.WriteLine();
         Console.ForegroundColor = ConsoleColor.Blue;
         Console.WriteLine("Factory --> Import to Azure Blob Storage");
     }
}

So far, there is nothing impressive in the code. This is just a dummy interfaces, with two different implementations: SqlDatabaseImporter and TableStorageImporter.

Theory says that Factory Method, most of the time, needs a switch block. This is a way we usually identify factories. So we need something like that in place:

C#
public class FactorySelector : IFactorySelector
{
      private readonly IImporterFactory _factory;

      public FactorySelector(IImporterFactory factory)
      {
          _factory = factory;
      }

      public IProductImporter GetFactory(string storage)
      {
          IProductImporter importer;
          switch (storage)
          {
              case Storage.AzureTableStorage:
                  importer = _factory.GetBlobStorageImporter();
                  break;
              default:
                  importer = _factory.GetSqlDataBaseImporter();
                  break;
          }
          return importer;
      }
  }

Again, there is nothing special with this code. Like we mentioned before, factories usually require a switch selector.

Below are the steps we need , in order to implement TypedFactoryFacility:

1) We will need an interface. However, because of this feature, we won't need to provide the implementation, the framework will provide for us: So below is the interface :

C#
public interface IImporterFactory : IDisposable
{
     IProductImporter GetBlobStorageImporter();

     IProductImporter GetSqlDataBaseImporter();

     void Destroy(IProductImporter factory);
 }

2) We need to inform the framework :

C#
public class WindsorInstaller : IWindsorInstaller
{
       public void Install(IWindsorContainer container, IConfigurationStore store)
       {
            container.AddFacility<TypedFactoryFacility>();

            //Register Factories
            container.Register(
                Component.For<IProductImporter>().Named("BlobStorageImporter").ImplementedBy<TableStorageImporter>().LifestyleTransient());

            container.Register(
                Component.For<IProductImporter>().Named("SqlDataBaseImporter").ImplementedBy<SqlDatabaseImporter>().LifestyleTransient());

            container.Register(Component.For<IFactorySelector>().ImplementedBy<FactorySelector>().LifestyleTransient());

            container.Register(Component.For<IImporterFactory>().AsFactory());
        }
  }

We need to observe three things that just happened:

1) We need to add this facility.

C#
container.AddFacility<TypedFactoryFacility>();

2) We need to inform the framework to provide an implementation for us :

C#
container.Register(Component.For<IImporterFactory>().AsFactory());

3)We need to name our concrete factories:

C#
container.Register(
                Component.For<IProductImporter>().Named("BlobStorageImporter").ImplementedBy<TableStorageImporter>().LifestyleTransient());

            container.Register(
                Component.For<IProductImporter>().Named("SqlDataBaseImporter").ImplementedBy<SqlDatabaseImporter>().LifestyleTransient());

The question here, is why did we need to provide names for our factories? The answer is in the interface below:

C#
public interface IImporterFactory : IDisposable
 {
      IProductImporter "color: rgba(0, 0, 255, 1)">BlobStorageImporter();

      IProductImporter ^__strong style="color:blue;">GetSqlDataBaseImporter();

      void Destroy(IProductImporter factory);
  }

Get+BlobStorageImporter()

Get+SqlDatabaseImporter()

The second one has to match exactly the name we provided in the code where we registered the component. If you change it, and you start playing with it, you will notice that it won't work. It has to match the name we have provided. Also, if you forget to provide name, once you register the components,it will still not work.

Another interesting feature is the following :

C#
void Destroy(IProductImporter factory);

With the above code, we delegate the responsibility to the container, to release/dispose the object.Dispose is a releasing method as well, just a very special one. It tries to release all objects passed to them as parameters.

Now that we know how to use it, lets try tho think the following:

How can we redesign the following class, without using TypedFactoryFacility. So, let's go back to the code:

C#
public class FactorySelector : IFactorySelector
{
     private readonly IImporterFactory _factory;

     public FactorySelector(IImporterFactory factory)
     {
         _factory = factory;
     }

     public IProductImporter GetFactory(string storage)
     {
         IProductImporter importer;
         switch (storage)
         {
             case Storage.AzureTableStorage:
                 importer = _factory.GetBlobStorageImporter();
                 break;
             default:
                 importer = _factory.GetSqlDataBaseImporter();
                 break;
         }
         return importer;
     }
}

The first thought it to provide concrete implementations, so the above would become :

C#
public IProductImporter GetFactory(string storage)
{
       IProductImporter importer;
       switch (storage)
       {
             case Storage.AzureTableStorage:
                 importer = new SqlDatabaseImporter();
                 break;
             default:
                 importer = new TableStorageImporter();
                 break;
       }
       return importer;
}

Doable, but not practical. After all, we want to code against interfaces. How could we still use an Interface without poluting the above code with the concrete objects?

A solution would be to use Service Locator. We would pass IWIndsorContainer to the class and we would also change the interface in SqlDatabaseImporter and TableStorageImporter, with something equivalent to :

C#
public IProductImporter GetFactory(string storage)
{
       IProductImporter importer;
       switch (storage)
       {
             case Storage.AzureTableStorage:
                 importer = _container.Resolve<ISqlDataBaseImporter>();
                 break;
             default:
                 importer = _container.Resolve<ITableStorageImporter>();
                 break;
       }
       return importer;
}

Doable, but still not very good idea, as it changes the design to something that is not really a Factory and most important it introduces Service Locator. Service Locators are good candidates for memory leaks and should be avoided.

I believe its clear how helpful it is for us to use TypedFactoryFacility.

Happy Coding!!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)