My favorites | Sign in
Project Home Downloads Wiki Issues Source
Search
for
AggregateService  
Dynamically generate aggregate services.
Autofac2
Updated Aug 6, 2010 by plillev...@gmail.com

Introduction

An aggregate service is useful when you need to treat a set of dependencies as one dependency. When a class depends on several constructor-injected services, or have several property-injected services, moving those services into a separate class yields a simpler API.

An example is super- and subclasses where the superclass have one or more constructor-injected dependencies. The subclasses must usually inherit these dependencies, even though they might only be useful to the superclass. With an aggregate service, the superclass constructor parameters can be collapsed into one parameter, reducing the repetitiveness in subclasses. Another important side effect is that subclasses are now insulated against changes in the superclass dependencies, introducing a new dependency in the superclass means only changing the aggregate service definition. This example is further elaborated here.

Aggregate services can be implemented by hand, e.g. by building a class with constructor-injected dependencies and exposing those as properties. Writing and maintaining aggregate service classes and accompanying tests can quickly get tedious though. The AggregateService extension to Autofac lets you generate aggregate services directly from interface definitions without having to write any implementation.

Under the cover, the AggregateService uses DynamicProxy2 from the Castle Project. Given an interface (the aggregate of services into one), a proxy is generated implementing the interface. The proxy will translate calls to properties and methods into Resolve calls to an Autofac context.

Required References

  • Autofac.dll
  • Castle.Core.dll
  • Castle.DynamicProxy2.dll
  • AutofacContrib.AggregateService.dll

Getting Started

Lets say we have a class with a number of constructor-injected dependencies that we store privately for later use:

    public class SomeController
    {
        private readonly IMyService _myService
        private readonly ISomeOtherService _someOtherService;
        ...

        public SomeController(IMyService myService, ISomeOtherService someOtherService, ...)
        {
             _myService = myService;
             _someOtherService = someOtherService;
             ...
        }
    }

To aggregate the dependencies we move those into a separate interface definition and takes a dependency to that interface instead.

    public interface IMyAggregateService
    {
        IMyService MyService { get; }
        ISomeOtherService SomeOtherService { get; }
        ...
    }

    public class SomeController
    {
        private readonly IMyAggregateService _services;

        public SomeController(IMyAggregateService services)
        {
            _services = services;
        }
    }

Finally, we register the aggregate service interface.

    using AutofacContrib.AggregateService;

    var builder = new ContainerBuilder();
    builder.RegisterAggregateService<IMyAggregateService>();
    builder.Register(...).As<IMyService>();
    builder.Register(...).As<IMyOtherService>();
    builder.Register<SomeController>();
    var container = builder.Build();

How services are resolved

Properties

Read-only properties mirror the behavior of regular constructor-injected dependencies. The type of each property will be resolved and cached in the aggregate service when the aggregate service instance is constructed.

Here is a functionally equivalent sample:

class MyAggregateServiceImpl: IMyAggregateService
{
    private IMyService _myService;

    public MyAggregateServiceImpl(IComponentContext context)
    {
        _myService = context.Resolve<IMyService>();
    }
 
    public IMyService MyService 
    { 
        get { return _myService; }      
    }
}

Methods

Methods will behave like factory delegates and will translate into a resolve call on each invocation. The method return type will be resolved, passing on any parameters to the resolve call.

A functionally equivalent sample of the method call:

class MyAggregateServiceImpl: IMyAggregateService
{
 
    ...
    public ISomeThirdService GetThirdService(string data)
    {
        var dataParam = new TypedParameter(typeof(string), data);
        return _context.Resolve<ISomeThirdService>(dataParam);
    }
}

Property setters and void methods

Property setters and methods without return types does not make sense in the aggregate service. Their presence in the aggregate service interface does not prevent proxy generation. Calling such methods though will throw an exception.

Performance considerations

Due to the fact that method calls in the aggregate service pass through a dynamic proxy there is some overhead on each method call. A performance study on Castle DynamicProxy2 vs other frameworks can be found here.


Sign in to add a comment
Powered by Google Project Hosting