Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does Structuremap support Lazy out of the box?

Does structuremap allow you to do constructor injection in a lazy fashion? Meaning not creating the object which is injected until it is used?

like image 621
Ryan Anderson Avatar asked Jul 25 '11 05:07

Ryan Anderson


1 Answers

UPDATE: StructureMap v3 implements this out of the box, so this trick is no longer necessary.


StructureMap version 2 doesn't, but with a few tricks you can get it to do what I believe you are looking for. First of all, you can already wire up Lazy<T> instances manually like this:

container = new Container(x =>
{
    x.Scan(y =>
    {
        y.TheCallingAssembly();
        y.WithDefaultConventions();
    });

    x.For<Lazy<IFoo>>().Use(y => new Lazy<IFoo>(y.GetInstance<Foo>));
    x.For<Lazy<IBar>>().Use(y => new Lazy<IBar>(y.GetInstance<Bar>));
    x.For<Lazy<IBaz>>().Use(y => new Lazy<IBaz>(y.GetInstance<Baz>));
});

This works just fine, but you have to register each and every type individually. It would be nicer if you could take advantage of a more convention-based approach. Ideally, the following syntax would be nice.

x.For(typeof(Lazy<>)).Use(typeof(Lazy<>));

This syntax actually works... somewhat. Unfortunately, at runtime, StructureMap will attempt to find the "greediest" constructor for Lazy<T> and settle on public Lazy(Func<T> valueFactory, bool isThreadSafe). Since we didn't tell it what to do with the boolean isThreadSafe parameter, it will throw an exception when it tries to resolve `Lazy'.

The documentation for Lazy states that the "thread safety mode" of the default Lazy(Func<T> valueFactory) constructor is LazyThreadSafetyMode.ExecutionAndPublication, which just so happens to be what you get by passing true into the isThreadSafe parameter of the constructor above. So, if we could just tell StructureMap to pass true for isThreadSafe, we would get the same behavior as if we called the constructor we actually wanted to use in the first place (e.g. Lazy(Func<T> valueFactory)).

Simply registering x.For(typeof(bool)).Use(y => true) would be very reckless and dangerous since we would be telling StructureMap to go ahead and use the value true for any boolean anywhere. Instead, we need to tell StructureMap what value to use for just this one boolean parameter, which we can do like this.

x.For(typeof(Lazy<>)).Use(typeof(Lazy<>))
 .CtorDependency<bool>("isThreadSafe").Is(true);

StructureMap now knows to use the value of "true" for the isThreadSafe parameter when resolving Lazy<T>. We can now use Lazy<T> in constructor parameters, and get the behavior I believe you were looking for.

You can read about the Lazy class in more detail here.

like image 189
Mel Avatar answered Sep 21 '22 21:09

Mel