9.77 Typed Factory Facility - delegate-based factories

优质
小牛编辑
132浏览
2023-12-01

In some simple cases it may feel like an overkill to create additional interface when all you want is to resolve single component. For cases like this Windsor (version 2.5 or newer) lets you take dependency on a delegate rather than interface, and the delegate will callback to the container to provide the instance for you when you invoke it.

Using factories

There's nothing magical with delegate-based factories. You use them as regular delegates, while Windsor does all the heavy lifting.

public class UsingDelegate
{
   private Func<IFoo> factory;

   public void UsingDelegate(Func<IFoo> fooFactory)
   {
      factory = fooFactory;
   }

   public void SomeOperation()
   {
      var foo = factory();
      foo.Bar();
   }

}

In this case creating a UsingDelegate does not trigger creation of (potentially expensive to create - think NHibernate's SessionFactory) IFoo. IFoo will only be created when call to SomeOperation occurs, which may be a log time after UsingDelegate was created.

Registering factories (implicitly)

The act of exposing delegate as dependency is enough to instruct the facility that it should provide a factory. This is called implicit registration. It can't be just any delegate though. Similar rules as with interface-based factories apply:

  • delegate must have a non-void return type.
  • delegate can't have out arguments

:information_source: Lifestyle: The factories registered that way will have transient lifestyle.

Registering factories explicitly

When you register the factory implicitly you give up the ability to configure the factory. If you need some non-default configuration, you can register, and configure the factory explicitly.

container.Register(
     Component.For<Func<IFoo>>().AsFactory(c => c.SelectedWith("nonDefaultFactory"))
);

How dependencies are matched

Dependencies are matched a little differently for delegate-based factories.

First by name

For delegate:

public delegate IFoo FooFactory(IBar bar);

If component registered for service IFoo has dependency named bar it will be satisfied with the value passed to the delegate.

Then by type

If instead the component has dependency named mySuperBar for type IBar, it won't find it on the FooFactory delegate. It will then go and see if there's any dependency for type IBar regardless of it's name, and if it finds one, it will use that instead.

:information_source: Duplicated dependency types are not allowed for Func<> family of delegates: When using generic purpose delegate Func<> (any of them) it is not possible to have delegate with duplicated argument types. Since their names are just meaningless arg1, arg2 etc, it is not possible to discriminate between arguments in Func<IFoo, IFoo>. In this case use dedicated delegate type, or better yet - interface-based factory.}

Releasing components resolved via factory

It is important to remember that if the Release Policy of your container tracks a component you resolve, the factory will hold a reference to the component as well. This is to let Windsor release the components you resolve via the factory, when the factory itself gets released.

:warning: NoTrackingReleasePolicy will not release the factory: Since release policies that do not track components are not able to release them, it's important to use LifecycledComponentsReleasePolicy (which is the default) when using delegate-based factories to be able to properly release the components resolved via the factory.

See also