Permalink
Browse files

Merge static AutoWireHelpers into Container partial extensions

  • Loading branch information...
1 parent e14a6d1 commit 5802bcd3a9dc9970ca3c58d86ba6ced60b73c9ca @mythz mythz committed Mar 17, 2013
@@ -1,145 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Linq.Expressions;
-using System.Reflection;
-using Funq;
-using ServiceStack.Configuration;
-using ServiceStack.Text;
-using ServiceStack.CacheAccess;
-using System.Threading;
-using System.Collections;
-
-namespace Funq
-{
- public static class AutoWireHelpers
- {
- private static Dictionary<Type, Action<object>[]> autoWireCache = new Dictionary<Type, Action<object>[]>();
-
- private static MethodInfo GetResolveMethod(Type typeWithResolveMethod, Type serviceType)
- {
- var methodInfo = typeWithResolveMethod.GetMethod("Resolve", new Type[0]);
- return methodInfo.MakeGenericMethod(new[] { serviceType });
- }
-
- public static ConstructorInfo GetConstructorWithMostParams(Type type)
- {
- return type.GetConstructors()
- .OrderByDescending(x => x.GetParameters().Length)
- .FirstOrDefault(ctor => !ctor.IsStatic);
- }
-
- /// <summary>
- /// Generates a function which creates and auto-wires <see cref="TService"/>.
- /// </summary>
- /// <typeparam name="TService"></typeparam>
- /// <param name="lambdaParam"></param>
- /// <returns></returns>
- public static Func<Container, TService> GenerateAutoWireFn<TService>()
- {
- var lambdaParam = Expression.Parameter(typeof(Container), "container");
- var propertyResolveFn = typeof(Container).GetMethod("TryResolve", new Type[0]);
- var memberBindings = typeof(TService).GetPublicProperties()
- .Where(x => x.CanWrite && !x.PropertyType.IsValueType && x.PropertyType != typeof(string))
- .Select(x =>
- Expression.Bind
- (
- x,
- ResolveTypeExpression(propertyResolveFn, x.PropertyType, lambdaParam)
- )
- ).ToArray();
-
- var ctorResolveFn = typeof(Container).GetMethod("Resolve", new Type[0]);
- return Expression.Lambda<Func<Container, TService>>
- (
- Expression.MemberInit
- (
- ConstrcutorExpression(ctorResolveFn, typeof(TService), lambdaParam),
- memberBindings
- ),
- lambdaParam
- ).Compile();
- }
-
- /// <summary>
- /// Auto-wires an existing instance of a specific type.
- /// The auto-wiring progress is also cached to be faster
- /// when calling next time with the same type.
- /// </summary>
- /// <param name="instance"></param>
- public static void AutoWire(Container container, object instance)
- {
- var instanceType = instance.GetType();
- var propertyResolveFn = typeof(Container).GetMethod("TryResolve", new Type[0]);
-
- Action<object>[] setters;
- if (!autoWireCache.TryGetValue(instanceType, out setters))
- {
- setters = instanceType.GetPublicProperties()
- .Where(x => x.CanWrite && !x.PropertyType.IsValueType && x.PropertyType != typeof(string))
- .Select(x => GenerateAutoWireFnForProperty(container, propertyResolveFn, x, instanceType))
- .ToArray();
-
- //Support for multiple threads is needed
- Dictionary<Type, Action<object>[]> snapshot, newCache;
- do
- {
- snapshot = autoWireCache;
- newCache = new Dictionary<Type, Action<object>[]>(autoWireCache);
- newCache[instanceType] = setters;
- } while (!ReferenceEquals(
- Interlocked.CompareExchange(ref autoWireCache, newCache, snapshot), snapshot));
- }
-
- foreach (var setter in setters)
- setter(instance);
- }
-
- private static Action<object> GenerateAutoWireFnForProperty(
- Container container, MethodInfo propertyResolveFn, PropertyInfo property, Type instanceType)
- {
- var instanceParam = Expression.Parameter(typeof(object), "instance");
- var containerParam = Expression.Constant(container);
-
- Func<object, object> getter = Expression.Lambda<Func<object, object>>(
- Expression.Call(
- Expression.Convert(instanceParam, instanceType),
- property.GetGetMethod()
- ),
- instanceParam
- ).Compile();
-
- Action<object> setter = Expression.Lambda<Action<object>>(
- Expression.Call(
- Expression.Convert(instanceParam, instanceType),
- property.GetSetMethod(),
- ResolveTypeExpression(propertyResolveFn, property.PropertyType, containerParam)
- ),
- instanceParam
- ).Compile();
-
- return obj => {
- if (getter(obj) == null) setter(obj);
- };
- }
-
- private static NewExpression ConstrcutorExpression(
- MethodInfo resolveMethodInfo, Type type, Expression lambdaParam)
- {
- var ctorWithMostParameters = GetConstructorWithMostParams(type);
-
- var constructorParameterInfos = ctorWithMostParameters.GetParameters();
- var regParams = constructorParameterInfos
- .Select(pi => ResolveTypeExpression(resolveMethodInfo, pi.ParameterType, lambdaParam));
-
- return Expression.New(ctorWithMostParameters, regParams.ToArray());
- }
-
- private static MethodCallExpression ResolveTypeExpression(
- MethodInfo resolveFn, Type resolveType, Expression containerParam)
- {
- var method = resolveFn.MakeGenericMethod(resolveType);
- return Expression.Call(containerParam, method);
- }
- }
-}
@@ -1,6 +1,11 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Threading;
using ServiceStack.Configuration;
-using ServiceStack.ServiceHost;
using System;
+using ServiceStack.Text;
namespace Funq
{
@@ -14,7 +19,7 @@ public partial class Container
/// <typeparam name="T"></typeparam>
public IRegistration<T> RegisterAutoWired<T>()
{
- var serviceFactory = AutoWireHelpers.GenerateAutoWireFn<T>();
+ var serviceFactory = GenerateAutoWireFn<T>();
return this.Register(serviceFactory);
}
@@ -25,7 +30,7 @@ public IRegistration<T> RegisterAutoWired<T>()
public IRegistration<TAs> RegisterAutoWiredAs<T, TAs>()
where T : TAs
{
- var serviceFactory = AutoWireHelpers.GenerateAutoWireFn<T>();
+ var serviceFactory = GenerateAutoWireFn<T>();
Func<Container, TAs> fn = c => serviceFactory(c);
return this.Register(fn);
}
@@ -47,8 +52,138 @@ public IRegistration<T> RegisterAutoWired<T>()
/// <param name="instance"></param>
public void AutoWire(object instance)
{
- AutoWireHelpers.AutoWire(this, instance);
+ AutoWire(this, instance);
}
- }
+
+
+ private Dictionary<Type, Action<object>[]> autoWireCache = new Dictionary<Type, Action<object>[]>();
+
+ private static MethodInfo GetResolveMethod(Type typeWithResolveMethod, Type serviceType)
+ {
+ var methodInfo = typeWithResolveMethod.GetMethod("Resolve", new Type[0]);
+ return methodInfo.MakeGenericMethod(new[] { serviceType });
+ }
+
+ public static ConstructorInfo GetConstructorWithMostParams(Type type)
+ {
+ return type.GetConstructors()
+ .OrderByDescending(x => x.GetParameters().Length)
+ .FirstOrDefault(ctor => !ctor.IsStatic);
+ }
+
+ /// <summary>
+ /// Generates a function which creates and auto-wires <see cref="TService"/>.
+ /// </summary>
+ /// <typeparam name="TService"></typeparam>
+ /// <param name="lambdaParam"></param>
+ /// <returns></returns>
+ public static Func<Container, TService> GenerateAutoWireFn<TService>()
+ {
+ var lambdaParam = Expression.Parameter(typeof(Container), "container");
+ var propertyResolveFn = typeof(Container).GetMethod("TryResolve", new Type[0]);
+ var memberBindings = typeof(TService).GetPublicProperties()
+ .Where(x => x.CanWrite && !x.PropertyType.IsValueType && x.PropertyType != typeof(string))
+ .Select(x =>
+ Expression.Bind
+ (
+ x,
+ ResolveTypeExpression(propertyResolveFn, x.PropertyType, lambdaParam)
+ )
+ ).ToArray();
+
+ var ctorResolveFn = typeof(Container).GetMethod("Resolve", new Type[0]);
+ return Expression.Lambda<Func<Container, TService>>
+ (
+ Expression.MemberInit
+ (
+ ConstrcutorExpression(ctorResolveFn, typeof(TService), lambdaParam),
+ memberBindings
+ ),
+ lambdaParam
+ ).Compile();
+ }
+
+ /// <summary>
+ /// Auto-wires an existing instance of a specific type.
+ /// The auto-wiring progress is also cached to be faster
+ /// when calling next time with the same type.
+ /// </summary>
+ /// <param name="instance"></param>
+ public void AutoWire(Container container, object instance)
+ {
+ var instanceType = instance.GetType();
+ var propertyResolveFn = typeof(Container).GetMethod("TryResolve", new Type[0]);
+
+ Action<object>[] setters;
+ if (!autoWireCache.TryGetValue(instanceType, out setters))
+ {
+ setters = instanceType.GetPublicProperties()
+ .Where(x => x.CanWrite && !x.PropertyType.IsValueType && x.PropertyType != typeof(string))
+ .Select(x => GenerateAutoWireFnForProperty(container, propertyResolveFn, x, instanceType))
+ .ToArray();
+
+ //Support for multiple threads is needed
+ Dictionary<Type, Action<object>[]> snapshot, newCache;
+ do
+ {
+ snapshot = autoWireCache;
+ newCache = new Dictionary<Type, Action<object>[]>(autoWireCache);
+ newCache[instanceType] = setters;
+ } while (!ReferenceEquals(
+ Interlocked.CompareExchange(ref autoWireCache, newCache, snapshot), snapshot));
+ }
+
+ foreach (var setter in setters)
+ setter(instance);
+ }
+
+ private static Action<object> GenerateAutoWireFnForProperty(
+ Container container, MethodInfo propertyResolveFn, PropertyInfo property, Type instanceType)
+ {
+ var instanceParam = Expression.Parameter(typeof(object), "instance");
+ var containerParam = Expression.Constant(container);
+
+ Func<object, object> getter = Expression.Lambda<Func<object, object>>(
+ Expression.Call(
+ Expression.Convert(instanceParam, instanceType),
+ property.GetGetMethod()
+ ),
+ instanceParam
+ ).Compile();
+
+ Action<object> setter = Expression.Lambda<Action<object>>(
+ Expression.Call(
+ Expression.Convert(instanceParam, instanceType),
+ property.GetSetMethod(),
+ ResolveTypeExpression(propertyResolveFn, property.PropertyType, containerParam)
+ ),
+ instanceParam
+ ).Compile();
+
+ return obj =>
+ {
+ if (getter(obj) == null) setter(obj);
+ };
+ }
+
+ private static NewExpression ConstrcutorExpression(
+ MethodInfo resolveMethodInfo, Type type, Expression lambdaParam)
+ {
+ var ctorWithMostParameters = GetConstructorWithMostParams(type);
+
+ var constructorParameterInfos = ctorWithMostParameters.GetParameters();
+ var regParams = constructorParameterInfos
+ .Select(pi => ResolveTypeExpression(resolveMethodInfo, pi.ParameterType, lambdaParam));
+
+ return Expression.New(ctorWithMostParameters, regParams.ToArray());
+ }
+
+ private static MethodCallExpression ResolveTypeExpression(
+ MethodInfo resolveFn, Type resolveType, Expression containerParam)
+ {
+ var method = resolveFn.MakeGenericMethod(resolveType);
+ return Expression.Call(containerParam, method);
+ }
+ }
}
@@ -225,7 +225,6 @@
<Compile Include="Html\ViewDataInfo.cs" />
<Compile Include="Properties\Resources.Designer.cs" />
<Compile Include="Html\ITemplatePage.cs" />
- <Compile Include="Funq\AutoWireHelpers.cs" />
<Compile Include="ServiceHost\FileExtensions.cs" />
<Compile Include="ServiceHost\HttpFile.cs" />
<Compile Include="ServiceHost\HttpRequestAuthentication.cs" />
@@ -403,4 +402,4 @@
</ProjectReference>
</ItemGroup>
<ItemGroup />
-</Project>
+</Project>
@@ -0,0 +1,34 @@
+using Funq;
+using NUnit.Framework;
+
+namespace ServiceStack.Common.Tests
+{
+ [TestFixture]
+ public class FunqTests
+ {
+ interface IBar { }
+ class Bar : IBar { }
+ class TestFoo { public IBar Bar { get; set; } }
+
+ [Test]
+ public void Test1()
+ {
+ var container = new Container();
+ var m = new TestFoo();
+ container.Register<IBar>(new Bar());
+ Assert.NotNull(container.Resolve<IBar>(), "Resolve");
+ container.AutoWire(m);
+ Assert.NotNull(m.Bar, "Autowire");
+ }
+
+ [Test]
+ public void Test2()
+ {
+ var container = new Container();
+ var m = new TestFoo();
+ container.AutoWire(m);
+ Assert.Throws<ResolutionException>(() => container.Resolve<IBar>());
+ Assert.IsNull(m.Bar); // FAILS HERE
+ }
+ }
+}
@@ -130,6 +130,7 @@
<Compile Include="Configuration\AppSettingsTests.cs" />
<Compile Include="EndpointHandlerBaseTests.cs" />
<Compile Include="FluentValidation\ErrorCodeTests.cs" />
+ <Compile Include="FunqTests.cs" />
<Compile Include="MappingTests.cs" />
<Compile Include="MessagingTests.cs" />
<Compile Include="Messaging\RedisMqServerTests.cs" />

0 comments on commit 5802bcd

Please sign in to comment.