Non-magic property injection in Unity(redirected from blargh/2010/12/non-magic-property-injection-in-unity)
This article was originally published in my blog (affectionately referred to as blargh) on . The original blog no longer exists as I've migrated everything to this wiki.
The original URL of this post was at https://tmont.com/blargh/2010/12/non-magic-property-injection-in-unity. Hopefully that link redirects back to this page.
Unity 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:
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!"
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.
So I fixed that. Unfortunately, Microsoft made the InjectionProperty
class
impressively difficult to extend, so we use the composite pattern to achieve greatness.
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);
}
}
Now you can do stuff like this:
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!"
It's a little more verbose, but much more awesome.