Non-magic property injection in Unity[source]
xml
<glacius:metadata> | |
<title>Non-magic property injection in Unity</title> | |
<description>description</description> | |
<category>C#</category> | |
<category>Programming</category> | |
<category>Legacy blog posts</category> | |
</glacius:metadata> | |
<glacius:macro name="legacy blargh banner"> | |
<properties> | |
<originalUrl>https://tmont.com/blargh/2010/12/non-magic-property-injection-in-unity</originalUrl> | |
<originalDate>2010-12-04T00:13:52.000Z</originalDate> | |
</properties> | |
</glacius:macro> | |
<p> | |
<a href="http://unitycontainer.org/">Unity</a> provides a way to perform injection on | |
properties, meaning that when an object is resolved through its DI container, it will | |
inject values for pre-defined properties. For example: | |
</p> | |
<glacius:code lang="csharp"><![CDATA[public class Foo { | |
public string Bar { get; set; } | |
} | |
// somewhere else... | |
container.RegisterType<Foo>(new InjectionProperty("Bar", "oh hai!")); | |
var foo = container.Resolve<Foo>(); | |
Console.WriteLine(foo.Bar); // "oh hai!" | |
]]></glacius:code> | |
<p> | |
Pretty nifty, but it still reeks of .NET 2.0 non-lambda lameness. This sucks because you | |
can't do all the cool stuff you're accustomed to, like type-safety, refactoring and static | |
analysis. | |
</p> | |
<p> | |
So I fixed that. Unfortunately, Microsoft made the <code>InjectionProperty</code> class | |
impressively difficult to extend, so we use the composite pattern to achieve greatness. | |
</p> | |
<glacius:code lang="csharp"><![CDATA[public class NonMagicInjectionProperty<T> : NonMagicInjectionProperty<T, object> { | |
public NonMagicInjectionProperty(Expression<Func<T, object>> propertyAccessor) : base(propertyAccessor) { } | |
public NonMagicInjectionProperty(Expression<Func<T, object>> propertyAccessor, object propertyValue) : base(propertyAccessor, propertyValue) { } | |
} | |
public class NonMagicInjectionProperty<T, TReturn> : InjectionMember { | |
private const string ErrorMessage = "Expected lambda expression like: foo => foo.Bar, where Bar is the name of the property to be injected"; | |
private readonly InjectionProperty injectionProperty; | |
public NonMagicInjectionProperty(Expression<Func<T, TReturn>> propertyAccessor) { | |
injectionProperty = new InjectionProperty(GetPropertyNameFromExpression(propertyAccessor)); | |
} | |
public NonMagicInjectionProperty(Expression<Func<T, TReturn>> propertyAccessor, TReturn propertyValue) { | |
injectionProperty = new InjectionProperty(GetPropertyNameFromExpression(propertyAccessor), propertyValue); | |
} | |
private static string GetPropertyNameFromExpression(Expression<Func<T, TReturn>> expression) { | |
var parameterName = expression.Parameters[0].Name; | |
var memberExpression = expression.Body as MemberExpression; | |
if (memberExpression == null) { | |
throw new ArgumentException(ErrorMessage); | |
} | |
var leftSide = memberExpression.Expression as ParameterExpression; | |
if (leftSide == null || leftSide.Name != parameterName) { | |
throw new ArgumentException(ErrorMessage); | |
} | |
return memberExpression.Member.Name; | |
} | |
public override void AddPolicies(Type serviceType, Type implementationType, string name, IPolicyList policies) { | |
injectionProperty.AddPolicies(serviceType, implementationType, name, policies); | |
} | |
}]]></glacius:code> | |
<p>Now you can do stuff like this:</p> | |
<glacius:code lang="csharp"><![CDATA[public class Foo { | |
public string Bar { get; set; } | |
} | |
// somewhere else... | |
container.RegisterType<Foo>(new NonMagicInjectionProperty<Foo>(foo => foo.Bar, "oh hai!")); | |
// or for more type safety (but not super necessary, in my opinion) | |
container.RegisterType<Foo>(new NonMagicInjectionProperty<Foo, string>(foo => foo.Bar, "oh hai!")); | |
var foo = container.Resolve<Foo>(); | |
Console.WriteLine(foo.Bar); // "oh hai!"]]></glacius:code> | |
<p>It's a little more verbose, but much more awesome.</p> |