Lazing Around with Autofac2

.NET 4.0 includes two handy little classes, Lazy<T> and Lazy<T, TMetadata>.

Lazy<T> represents a value that is initialised the first time it is accessed.

// No counting done here
var lineCount = new Lazy<int>(() => CountLines("bigfile.txt"));

// Count happens when value is accessed
Console.Write(lineCount.Value);

// No counting here either - once the value is initialised it is stored
Console.Write(lineCount.Value);

Lazy<T, TMetadata> works exactly the same way as Lazy<T>, but adds a Metadata property that can be used to get information about the value before it is instantiated.

var filename = "bigfile.txt";

// The filename passed in the constructor is stored in the Metadata property
// of type string
var lineCount = new Lazy<int, string>(() => CountLines(filename), filename);

// Metadata (i.e. the filename) can be accessed without causing the value to be initialised
Console.Write(lineCount.Metadata);

Metadata becomes very useful when you have more than one lazy item, and need to choose between them before accessing and initialising the value.

Lazy Dependencies

Components in an IoC container can use the lazy types to express dependencies, and thus delay the construction of part of the object graph either for functional or performance reasons.

One of the very nice features of the Managed Extensibility Framework (MEF) in .NET 4.0 is the way lazy dependencies are handled. An ‘import’ (MEF terminology) of type Lazy<T> will be injected automatically so long as T is registered with the CompositionContainer.

In Autofac’s latest 2.1 beta,  an extension called LazyDependencyModule gives a similar result.

Autofac 2 still supports .NET 3.5. The only Autofac assembly that requires .NET 4.0 is Autofac.Integration.Mef.dll, while all others target .NET 3.5.

Simple Lazy<T> Example

Let’s imagine that in our particular ASP.NET application, creating repositories is fairly expensive. In some of the code paths through ContactsController we don’t use the injected contacts repository, and would like to avoid the cost of instantiating it.

Yes dear reader, you need to use your imagination here rather than a profiler… :)

To communicate this requirement to Autofac, you use Lazy<IContactRepository> as the type of the controller constructor parameter:

public class ContactsController : Controller
{
    Lazy<IContactRepository> _contacts;

    public ContactsController(Lazy<IContactRepository> contacts)
    {
        _contacts = contacts;
    }

    public ActionResult Show(int contactId)
    {
        ViewData.Model = _contacts.Value.Load(contactId);
        return View();
    }
}

When configuring your Autofac ContainerBuilder, add the LazyDependencyModule from Autofac.Integration.Mef.dll.

var builder = new ContainerBuilder();

builder.RegisterModule(new LazyDependencyModule());

// No special configuration for the controller...
builder.RegisterType<ContactsController>().Named<IController>("controller.contacts");

// ...or the repository.
builder.RegisterType<ContactRepository>().As<IContactRepository>();

And that’s it! Wherever Lazy<T> is resolved, Autofac will do the right thing.

Using Metadata

I’ve included downloadable code for a more complete example Lazy in Autofac 2 demonstrating metadata use.

The Log component accepts a list of appenders, each with metadata. Based on the logging request, the logger selects an appender (instantiating if necessary) and writes a message to it.

public class Log
{
    readonly IEnumerable<Lazy<ILogAppender, ILogAppenderMetadata>> _appenders;

    public Log(IEnumerable<Lazy<ILogAppender, ILogAppenderMetadata>> appenders)
    {
        _appenders = appenders;
    }

    public void Write(string destination, string message)
    {
        var appender = _appenders.First(a => a.Metadata.AppenderName == destination);
        appender.Value.Write(message);
    }
}

You can see in this example how Autofac 2′s automatic support for IEnumerable dependencies works just as well combined with laziness.

The metadata view ILogAppenderMetadata has a single property AppenderName. The value of this property will be retrieved from the implementing component’s extended properties.

builder.Register(c => new ScreenAppender())
    .As<ILogAppender>()
    .WithMetadata("AppenderName", "screen");

Binding of ILogAppenderMetadata to the underlying dictionary of extended properties is done using the same mechanism as MEF uses, and you can find more information on that via the MEF site.

Under the Hood

If you’re interested to see how easy Autofac 2 makes supporting these ‘context adapters’, check out the implementation handling Lazy<T, TMetadata>, weighing in at 52 lines including braces, using statements and whitespace :)

Downloads

First Autofac 2 Beta Available

What does beta mean? Here are the good things you can expect from Autofac 2.1.5:

  • Everything you need to build a working application is in there
  • All the tests that should pass, pass
  • If you find bugs, they’ll be fixed
  • Between now and the full release, API breakage will be taken seriously

You should also be aware that:

  • There will be some bugs
  • Documentation on the wiki doesn’t reflect the new version
  • There’s no matching AutofacContrib release yet
  • Upgrading an existing application will take a little time

Long-time Autofac users will find the new version a bit strange at first, but the most visible differences are name changes. There’s a summary of API changes on the wiki.

I hope you have fun test-driving the new version. You can get the binaries from the Autofac downloads page.

Autofac 2 Status Update

Revision: 398Autofac 2 Sketch
Author: nicholas.blumhardt
Date: 8:06:55 AM, Monday, 21 July 2008
Message:
Added 2.0-experimental branch for exploration of internal refactorings.

Yes, work on Autofac 2 began a year and a half ago! While work all but stopped while I did other things in Redmond, I’m pleased to say that a beta is finally in sight.

You might be curious just how close is ‘close’: there are six items in the issue tracker that I think need to be addressed:

  • 98 Support named registrations for open generics
  • 140 Cache constructor bindings in ReflectionActivator
  • 150 Human-readable display strings and [DebuggerDisplay]
  • 154 Update ConfigurationSettingsReader (XML Config Support)
  • 155 Update contrib and examples to 2.1
  • 160 Scan assemblies for modules

Most of these are pretty unexciting – minor features, niceties and dependent project updates.

Issue 98, however, has been in the system a while. One of the core improvements in Autofac 2 makes it easier and much more efficient to add support for dynamically-generated component registrations.

As an example, if IFoo is registered, Autofac provides or can be extended to provide:

  • Owned<IFoo> for explicit ownership/Iifetime control
  • Func<IFoo> for dynamically creating IFoo components
  • Lazy<IFoo> and Lazy<IFoo, IFooMetadata> for lazy instantiation

These can even be composed automatically to give Func<Owned<IFoo>> and so-on.

Under the hood, each extension is a handler that can look at a requested Service, query other available services, and generate a component on-the-fly that is able to fulfill the request.

The same underlying mechanism supports open generic types.

builder.RegisterGeneric(typeof(Bar<>)).As(typeof(IBar<>));

var container = builder.Build();

container.Resolve<IBar<int>>();

The handler checks to see if the requested Service is a closed instance of the open generic service type it is watching for (IBar<>). If it is, the handler creates a regular component by closing the generic component type (Bar<> becomes Bar<int>), and from that point on the container will used the closed type to provide that particular closed service.

Now, all is well-and-good if the service is requested by type, as in Resolve<IFoo>().

Autofac uses NamedService and TypedService to represent services requested by name and by type respectively. When a component is resolved by name, as in Resolve<IFoo>(“foo”), the dynamic registration handlers get an instance of NamedService { ServiceName = “foo” }.

NamedService, in Autofac 1.x and Autofac 2 today, doesn’t carry type information, just a string, so while Resolve<IFoo>(“foo”) succeeds, there’s no way to automatically support Resolve<Func<IFoo>>(“foo”) – the handler can’t see that the returned object should be of type Func<IFoo>.

And that is at the crux of issue 98. Adding type information to NamedService doesn’t seem like a big deal, but it does touch a lot of APIs and will break some existing apps in potentially confusing ways. It may be that issue 98 is put to the side this time around. Either way a decision has to be reached before Autofac 2 can be released as a beta.

Stay tuned, and have a safe holiday if you’re lucky enough to be taking one!

One blog to rule them all…

Welcome to my new blog, perhaps a little more permanent than my last two. If you’re looking for something to read while I work on new content for this site, why don’t you take a walk down memory lane with some of my earlier articles?

Implementing the Specification Pattern with Linq – a great data access pattern for domain-driven .NET applications. Follow up with some really interesting additions from Luke Marshall, Rinat Abdullin, Steven Burman and Fredrik Kalseth.

State Machines in Domain Models – a primer on the Stateless hierarchical state machine framework. The title is a bit vague – Stateless is useful wherever a problem can be modelled in terms of states and transitions. Follow up with a look at the new features in version 2.

Implementing Optional Exports with MEF Stable Composition – if you’re working with the Managed Extensibility Framework, you need to understand Stable Composition (and how to debug it!)

Where does the Container Belong? – in answer to what is becoming an age-old question of IoC container users. Leads in to a discussion on context dependencies, and the foundation for declarative context adapters like Autofac’s generated factories and MEF’s PartCreator.

And I hear you ask… Where is Autofac in all of this? Much of my recent hiatus from writing owes to work on version 2. You can follow progress on the mailing list, or watch a summary of changes evolve. For those not familiar with the project, the introductory article on CodeProject still applies.

Ciao!