Appearance
Lazy Resolution
Lamar has some built in functionality for "lazy" resolved dependencies, so that instead of your application service taking a direct dependency on IExpensiveToBuildService
that might not be necessary, you could instead have Lamar fulfil a dependency on Lazy<IExpensiveToBuildService>
or Func<IExpensiveToBuildService>
that could be used to retrieve that expensive service only when it is needed from whatever Container
originally created the parent object.
Do note that the Lazy<T>
and Func<T>
approaches respect the lifecycle of the underlying registration rather than automatically building a unique object instance.
Also note that Lazy<T>
or Func<T>
is your best (only) viable approach if you wish to have Lamar inject bi-directional relationships.
Lazy<T>
Assuming that Lamar either has an existing configuration for T
or can derive a way to build T
, you can just declare a dependency on Lazy<T>
like this sample:
cs
public class WidgetLazyUser
{
private readonly Lazy<IWidget> _widget;
public WidgetLazyUser(Lazy<IWidget> widget)
{
_widget = widget;
}
public IWidget Widget => _widget.Value;
}
[Fact]
public void lazy_resolution_in_action()
{
var container = new Container(_ => { _.For<IWidget>().Use<AWidget>(); });
container.GetInstance<WidgetLazyUser>()
.Widget.ShouldBeOfType<AWidget>();
}
cs
public class WidgetLazyUser
{
private readonly Lazy<IWidget> _widget;
public WidgetLazyUser(Lazy<IWidget> widget)
{
_widget = widget;
}
public IWidget Widget
{
get { return _widget.Value; }
}
}
[Fact]
public void lazy_resolution_in_action()
{
var container = new Container(_ =>
{
_.For<IWidget>().Use<AWidget>();
});
container.GetInstance<WidgetLazyUser>()
.Widget.ShouldBeOfType<AWidget>();
}
Func<T>
Likewise, you can also declare a dependency on Func<T>
with very similar mechanics:
cs
[Fact]
public void build_a_func_that_returns_a_singleton()
{
var container = new Container(x => { x.ForSingletonOf<IWidget>().Use<AWidget>(); });
var func = container.GetInstance<Func<IWidget>>();
var w1 = func();
var w2 = func();
var w3 = func();
w1.ShouldBeOfType<AWidget>();
w1.ShouldBeSameAs(w2);
w1.ShouldBeSameAs(w3);
w2.ShouldBeSameAs(w3);
}
cs
[Fact]
public void build_a_func_that_returns_a_singleton()
{
var container = new Container(x =>
{
x.ForSingletonOf<IWidget>().Use<ColorWidget>().Ctor<string>("color").Is("green");
});
var func = container.GetInstance<Func<IWidget>>();
var w1 = func();
var w2 = func();
var w3 = func();
w1.ShouldBeOfType<ColorWidget>().Color.ShouldBe("green");
w1.ShouldBeTheSameAs(w2);
w1.ShouldBeTheSameAs(w3);
w2.ShouldBeTheSameAs(w3);
}
This functionality predates the introduction of the Lazy type to .Net
Func<string, T>
Finally, you can also declare a dependency on Func<string, T>
that will allow you to lazily resolve a dependency of T
by name:
cs
[Fact]
public void build_a_func_by_string()
{
var container = new Container(x =>
{
x.For<IWidget>().Add<GreenWidget>().Named("green");
x.For<IWidget>().Add<BlueWidget>().Named("blue");
x.For<IWidget>().Add<RedWidget>().Named("red");
});
var func = container.GetInstance<Func<string, IWidget>>();
func("green").ShouldBeOfType<GreenWidget>();
func("blue").ShouldBeOfType<BlueWidget>();
func("red").ShouldBeOfType<RedWidget>();
}
cs
[Fact]
public void build_a_func_by_string()
{
var container = new Container(x =>
{
x.For<IWidget>().Add<ColorWidget>().Ctor<string>("color").Is("green").Named("green");
x.For<IWidget>().Add<ColorWidget>().Ctor<string>("color").Is("blue").Named("blue");
x.For<IWidget>().Add<ColorWidget>().Ctor<string>("color").Is("red").Named("red");
});
var func = container.GetInstance<Func<string, IWidget>>();
func("green").ShouldBeOfType<ColorWidget>().Color.ShouldBe("green");
}
Bi-relational Dependency Workaround
Lamar does not directly support bi-directional dependency relationships -- but will happily tell you in an exception when you accidentally manage to create one without cratering your AppDomain with a StackOverflowException
.
Either Func<T>
or Lazy<T>
can be used as a workaround for purposeful bi-directional dependencies between types. The following is an example of using this strategy:
cs
public class Thing1
{
private readonly Lazy<Thing2> _thing2;
public Thing1(Lazy<Thing2> thing2)
{
_thing2 = thing2;
}
public Thing2 Thing2 => _thing2.Value;
}
public class Thing2
{
public Thing2(Thing1 thing1)
{
Thing1 = thing1;
}
public Thing1 Thing1 { get; set; }
}
[Fact]
public void use_lazy_as_workaround_for_bi_directional_dependency()
{
var container = Container.For(_ =>
{
_.AddSingleton<Thing1>();
_.AddSingleton<Thing2>();
});
var thing1 = container.GetInstance<Thing1>();
var thing2 = container.GetInstance<Thing2>();
thing1.Thing2.ShouldBeSameAs(thing2);
thing2.Thing1.ShouldBeSameAs(thing1);
}
cs
[Singleton]
public class Thing1
{
private readonly Lazy<Thing2> _thing2;
public Thing1(Lazy<Thing2> thing2)
{
_thing2 = thing2;
}
public Thing2 Thing2
{
get { return _thing2.Value; }
}
}
[Singleton]
public class Thing2
{
public Thing1 Thing1 { get; set; }
public Thing2(Thing1 thing1)
{
Thing1 = thing1;
}
}
[Fact]
public void use_lazy_as_workaround_for_bi_directional_dependency()
{
var container = new Container();
var thing1 = container.GetInstance<Thing1>();
var thing2 = container.GetInstance<Thing2>();
thing1.Thing2.ShouldBeSameAs(thing2);
thing2.Thing1.ShouldBeSameAs(thing1);
}