/src/LinFu.IoC/Configuration/FactoryBuilder.cs

http://github.com/philiplaureano/LinFu · C# · 194 lines · 100 code · 32 blank · 62 comment · 10 complexity · 34098e626e753269aa41fef040c9a069 MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Reflection;
  4. using LinFu.IoC.Configuration.Interfaces;
  5. using LinFu.IoC.Factories;
  6. using LinFu.IoC.Interfaces;
  7. namespace LinFu.IoC.Configuration
  8. {
  9. /// <summary>
  10. /// The default implementation of the <see cref="IFactoryBuilder" /> class.
  11. /// </summary>
  12. internal class FactoryBuilder : IFactoryBuilder
  13. {
  14. private static readonly Dictionary<LifecycleType, Type> _factoryTypes = new Dictionary<LifecycleType, Type>();
  15. private static readonly IServiceContainer _dummyContainer = new ServiceContainer();
  16. /// <summary>
  17. /// Initializes the list of factory types.
  18. /// </summary>
  19. static FactoryBuilder()
  20. {
  21. _factoryTypes[LifecycleType.OncePerRequest] = typeof(OncePerRequestFactory<>);
  22. _factoryTypes[LifecycleType.OncePerThread] = typeof(OncePerThreadFactory<>);
  23. _factoryTypes[LifecycleType.Singleton] = typeof(SingletonFactory<>);
  24. }
  25. /// <summary>
  26. /// Creates a factory instance that can create instaces of the given
  27. /// <paramref name="serviceType" /> using the <paramref name="implementingType" />
  28. /// as the implementation.
  29. /// </summary>
  30. /// <param name="serviceType">The service being implemented.</param>
  31. /// <param name="implementingType">The actual type that will implement the service.</param>
  32. /// <param name="lifecycle">The <see cref="LifecycleType" /> that determines the lifetime of each instance being created.</param>
  33. /// <returns>A valid <see cref="IFactory" /> instance.</returns>
  34. public IFactory CreateFactory(Type serviceType, Type implementingType, LifecycleType lifecycle)
  35. {
  36. // Determine the factory type
  37. var factoryTypeDefinition = _factoryTypes[lifecycle];
  38. var actualType = GetActualType(serviceType, implementingType);
  39. if (!serviceType.ContainsGenericParameters && !actualType.ContainsGenericParameters)
  40. {
  41. var factoryType = factoryTypeDefinition.MakeGenericType(serviceType);
  42. return CreateFactory(serviceType, actualType, factoryType);
  43. }
  44. Func<IFactoryRequest, object> factoryMethod =
  45. request =>
  46. {
  47. var serviceName = request.ServiceName;
  48. var type = request.ServiceType;
  49. var currentContainer = request.Container;
  50. var arguments = request.Arguments;
  51. // Determine the implementing type
  52. var concreteType = GetActualType(type, implementingType);
  53. // The concrete type cannot be null
  54. if (concreteType == null)
  55. return null;
  56. // Generate the concrete factory instance
  57. // at runtime
  58. var factoryType = factoryTypeDefinition.MakeGenericType(type);
  59. var factory = CreateFactory(type, concreteType, factoryType);
  60. var factoryRequest = new FactoryRequest
  61. {
  62. ServiceType = serviceType,
  63. ServiceName = serviceName,
  64. Arguments = arguments,
  65. Container = currentContainer
  66. };
  67. return factory.CreateInstance(factoryRequest);
  68. };
  69. return new FunctorFactory(factoryMethod);
  70. }
  71. /// <summary>
  72. /// Creates a factory instance that can create instaces of the given
  73. /// <paramref name="serviceType" /> using the <paramref name="actualType" />
  74. /// as the implementation.
  75. /// </summary>
  76. /// <param name="serviceType">The service being implemented.</param>
  77. /// <param name="actualType">The actual type that will implement the service.</param>
  78. /// <param name="factoryType">The factory type that will instantiate the target service.</param>
  79. /// <returns>A valid <see cref="IFactory" /> instance.</returns>
  80. private IFactory CreateFactory(Type serviceType, Type actualType, Type factoryType)
  81. {
  82. // Create the factory itself
  83. var factoryMethod = CreateFactoryMethod(serviceType, actualType);
  84. var factoryInstance = factoryType.AutoCreateFrom(_dummyContainer, factoryMethod);
  85. var result = factoryInstance as IFactory;
  86. return result;
  87. }
  88. /// <summary>
  89. /// Determines the implementing concrete type from the given <paramref name="serviceType" />.
  90. /// </summary>
  91. /// <param name="serviceType">The service type.</param>
  92. /// <param name="implementingType">The concrete class that will implement the service type.</param>
  93. /// <returns>The actual implementing type.</returns>
  94. private static Type GetActualType(Type serviceType, Type implementingType)
  95. {
  96. if (!implementingType.ContainsGenericParameters)
  97. return implementingType;
  98. var actualType = implementingType;
  99. // The service type must be a generic type with
  100. // closed generic parameters
  101. if (!serviceType.IsGenericType || serviceType.ContainsGenericParameters)
  102. return implementingType;
  103. // Attempt to apply the generic parameters of the service type
  104. // to the implementing type
  105. var typeParameters = serviceType.GetGenericArguments();
  106. try
  107. {
  108. var concreteType = implementingType.MakeGenericType(typeParameters);
  109. // The concrete type must derive from the given service type
  110. if (serviceType.IsAssignableFrom(concreteType))
  111. actualType = concreteType;
  112. }
  113. catch
  114. {
  115. // Ignore the error
  116. }
  117. return actualType;
  118. }
  119. /// <summary>
  120. /// A <c>private</c> method that creates the factory method delegate
  121. /// for use with a particular factory class.
  122. /// </summary>
  123. /// <seealso cref="SingletonFactory{T}" />
  124. /// <seealso cref="OncePerRequestFactory{T}" />
  125. /// <seealso cref="OncePerThreadFactory{T}" />
  126. /// <param name="serviceType">The service type being instantiated.</param>
  127. /// <param name="implementingType">The type that will provide the implementation for the actual service.</param>
  128. /// <returns>A factory method delegate that can create the given service.</returns>
  129. private MulticastDelegate CreateFactoryMethod(Type serviceType, Type implementingType)
  130. {
  131. var flags = BindingFlags.NonPublic | BindingFlags.Static;
  132. var factoryMethodDefinition = typeof(FactoryBuilder).GetMethod("CreateFactoryMethodInternal", flags);
  133. var factoryMethod = factoryMethodDefinition.MakeGenericMethod(serviceType, implementingType);
  134. // Create the Func<IFactoryRequest, TService> factory delegate
  135. var result = factoryMethod.Invoke(null, new object[0]) as MulticastDelegate;
  136. return result;
  137. }
  138. /// <summary>
  139. /// A method that generates the actual lambda function that creates
  140. /// the new service instance.
  141. /// </summary>
  142. /// <typeparam name="TService">The service type being instantiated.</typeparam>
  143. /// <typeparam name="TImplementation">The type that will provide the implementation for the actual service.</typeparam>
  144. /// <returns>A strongly-typed factory method delegate that can create the given service.</returns>
  145. internal static Func<IFactoryRequest, TService> CreateFactoryMethodInternal<TService, TImplementation>()
  146. where TImplementation : TService
  147. {
  148. return request =>
  149. {
  150. var container = request.Container;
  151. var arguments = request.Arguments;
  152. var serviceContainer = container;
  153. // Attempt to autoresolve the constructor
  154. if (serviceContainer != null)
  155. return
  156. (TService) serviceContainer.AutoCreateInternal(typeof(TImplementation), arguments);
  157. // Otherwise, use the default constructor
  158. return (TService) Activator.CreateInstance(typeof(TImplementation), arguments);
  159. };
  160. }
  161. }
  162. }