Neil Barnwell
Neil Barnwell

Reputation: 42125

Castle Windsor: Set component dependencies on existing object

In MEF it's possible to set the dependencies for an existing object using something like:

container.SatisfyImportsOnce(instance);

Is it possible to do the same with Castle Windsor?

I'm using (read: learning) Caliburn.Micro, and trying to update the template project from MEF to Windsor, which is where I've come across the issue.

Upvotes: 2

Views: 1338

Answers (3)

BlackSpy
BlackSpy

Reputation: 5603

You can however code this functionality yourself. For example, here is an ASP.NET MVC FilterAttributeFilterProvider, used to inject proprties onto attribute action filters.

  public class AttributeFilterProvider : FilterAttributeFilterProvider
  {
    public AttributeFilterProvider(IKernel kernel)
    {
      _kernel = kernel;
    }

    private readonly IKernel _kernel;

    protected override IEnumerable<FilterAttribute> GetControllerAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
    {
      var attributes = base.GetControllerAttributes(controllerContext, actionDescriptor);
      BuildUpAttributeDependancies(attributes);
      return attributes;
    }

    protected override IEnumerable<FilterAttribute> GetActionAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
    {
      var attributes = base.GetActionAttributes(controllerContext, actionDescriptor);
      BuildUpAttributeDependancies(attributes);
      return attributes;
    }

    private void BuildUpAttributeDependancies(IEnumerable<FilterAttribute> attributes)
    {
      foreach (var attribute in attributes)
      {
        var propInfos = attribute.GetType().GetProperties().Where(x => x.GetValue(attribute) == null).AsEnumerable();
        foreach (var pi in propInfos)
        {
          if (_kernel.HasComponent(pi.PropertyType))
          {
            var service = _kernel.Resolve(pi.PropertyType);
            pi.SetValue(attribute, service);
          }
        }
      }
    }
  }

In the BuildUpAttributeDependancies method, we look for un-initialised (null) properties, and then check to see if the type has been registered with Castle Windsor. If it has, we set the property.

By replacing the default FilterAttributeFilterProvider with our custom one (above) in the global.asax file we can now use Castle Windsors DI features to inject any type onto any Action Filter in our MVC application. To complete this answer fully, here is an example of a global.asax application class with Castle Windsor setup for both Controller (at instantiation time) and ActionFilter (at usage time) dependancy injection:

public class MvcApplication : System.Web.HttpApplication
{
  private static IWindsorContainer _container;

  private static void BootstrapContainer()
  {
    _container = new WindsorContainer()
        .Install(FromAssembly.This());

    var controllerFactory = new ControllerFactory(_container.Kernel);
    ControllerBuilder.Current.SetControllerFactory(controllerFactory);
  }

  private static void BootstrapFilters()
  {
    var oldProvider = FilterProviders.Providers.Single(f => f is FilterAttributeFilterProvider);
    FilterProviders.Providers.Remove(oldProvider);

    var provider = new AttributeFilterProvider(_container.Kernel);
    FilterProviders.Providers.Add(provider);
  }

  protected void Application_Start()
  {
    AreaRegistration.RegisterAllAreas();

    WebApiConfig.Register(GlobalConfiguration.Configuration);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);

    BootstrapContainer();
    BootstrapFilters();
  }
}

Upvotes: 1

Jason Lodice
Jason Lodice

Reputation: 36

Sorry Neil, Windsor doesn't have that feature

Castle Windsor FAQ

Windsor will resolve a property dependency (which it considers an optional dependency) such as an ILogger property if there is a registered component for that service. But this only happens during component activation...when the component is constructed the first time, there is no way to pass Windsor an existing instance and inject components into properties.

Upvotes: 2

Rohan West
Rohan West

Reputation: 9298

With Castle Windsor you can register an existing instance with the container, is this the kind of thing you are looking for?

var instance = new Logger();
var container = new WindsorContainer();

container.Register(Component.For<ILogger>().Instance(instance))

where

public interface ILogger
{
  void LogException(Exception ex);
}

public class Logger : ILogger
{
  public void LogException(Exception ex)
  {
    // Log exception
  }
}

Upvotes: 2

Related Questions