PageRenderTime 45ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/src/LinFu.IoC/Configuration/Loaders/FactoryAttributeLoader.cs

http://github.com/philiplaureano/LinFu
C# | 216 lines | 126 code | 29 blank | 61 comment | 15 complexity | 8aaeb8cb8e343a9705c55b09b96120d0 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using LinFu.IoC.Configuration.Injectors;
  6. using LinFu.IoC.Factories;
  7. using LinFu.IoC.Interfaces;
  8. namespace LinFu.IoC.Configuration
  9. {
  10. /// <summary>
  11. /// A class that injects custom <see cref="IFactory" /> and <see cref="IFactory{T}" />
  12. /// instances into an <see cref="IServiceContainer" /> instance.
  13. /// </summary>
  14. public class FactoryAttributeLoader : ITypeLoader
  15. {
  16. /// <summary>
  17. /// Loads an <see cref="IFactory" /> and <see cref="IFactory{T}" /> instance
  18. /// into a <see cref="IServiceContainer" /> instance using the given
  19. /// <paramref name="sourceType" />.
  20. /// </summary>
  21. /// <param name="sourceType">The input type from which one or more factories will be created.</param>
  22. /// <returns>A set of <see cref="Action{T}" /> instances. This cannot be null.</returns>
  23. public IEnumerable<Action<IServiceContainer>> Load(Type sourceType)
  24. {
  25. if (sourceType == null)
  26. throw new ArgumentNullException("sourceType");
  27. // Extract the factory attributes from the current type
  28. var attributes = sourceType.GetCustomAttributes(typeof(FactoryAttribute), false);
  29. var attributeList = attributes.Cast<FactoryAttribute>()
  30. .Where(f => f != null).ToList();
  31. // The target type must have at least one
  32. // factory attribute
  33. if (attributeList.Count == 0)
  34. return new Action<IServiceContainer>[0];
  35. // Make sure the factory is created only once
  36. Func<IFactoryRequest, object> getFactoryInstance = request =>
  37. {
  38. var container = request.Container;
  39. return container.AutoCreateInternal(sourceType);
  40. };
  41. return GetResults(sourceType, attributeList, getFactoryInstance);
  42. }
  43. /// <summary>
  44. /// Determines whether or not the current <paramref name="sourceType" />
  45. /// can be loaded.
  46. /// </summary>
  47. /// <param name="sourceType">The source type currently being loaded.</param>
  48. /// <returns>Returns <c>true</c> if the type is a class type; otherwise, it returns <c>false</c>.</returns>
  49. public bool CanLoad(Type sourceType)
  50. {
  51. try
  52. {
  53. return sourceType.IsClass;
  54. }
  55. catch (TypeInitializationException)
  56. {
  57. // Ignore the error
  58. return false;
  59. }
  60. catch (FileNotFoundException)
  61. {
  62. // Ignore the error
  63. return false;
  64. }
  65. }
  66. /// <summary>
  67. /// Instantiates the <see cref="IFactory" /> instances associated with the <paramref name="sourceType" /> and
  68. /// adds those factories to the target container upon initialization.
  69. /// </summary>
  70. /// <param name="sourceType">The <see cref="System.Type" /> currently being inspected.</param>
  71. /// <param name="attributeList">
  72. /// The list of <see cref="FactoryAttribute" /> instances currently declared on on the source
  73. /// type.
  74. /// </param>
  75. /// <param name="getFactoryInstance">The functor that will be responsible for generating the factory instance.</param>
  76. /// <returns>A list of actions that will add the factories to the target container.</returns>
  77. private static IEnumerable<Action<IServiceContainer>> GetResults(Type sourceType,
  78. IEnumerable<FactoryAttribute> attributeList,
  79. Func<IFactoryRequest, object>
  80. getFactoryInstance)
  81. {
  82. var results = new List<Action<IServiceContainer>>();
  83. // The factory instance must implement either
  84. // IFactory or IFactory<T>
  85. var factoryInterfaces = from t in sourceType.GetInterfaces()
  86. where t.IsGenericType &&
  87. t.GetGenericTypeDefinition() == typeof(IFactory<>)
  88. select t;
  89. if (!typeof(IFactory).IsAssignableFrom(sourceType) && factoryInterfaces.Count() == 0)
  90. {
  91. var message =
  92. string.Format(
  93. "The factory type '{0}' must implement either the IFactory interface or the IFactory<T> interface.",
  94. sourceType.AssemblyQualifiedName);
  95. throw new ArgumentException(message, "sourceType");
  96. }
  97. var implementedInterfaces = new HashSet<Type>(factoryInterfaces);
  98. Func<Type, Func<IFactoryRequest, object>, IFactory> createFactory =
  99. (currentServiceType, getFactory) =>
  100. {
  101. // Determine if the factory implements
  102. // the generic IFactory<T> instance
  103. // and use that instance if possible
  104. Func<IFactoryRequest, IFactory> getStronglyTypedFactory =
  105. request =>
  106. {
  107. var result = getFactory(request);
  108. // If the object is IFactory then we can just return it.
  109. if (result is IFactory)
  110. return (IFactory) result;
  111. // Check to see if the object is IFactory<T>, if so we need to adapt it to
  112. // IFactory.
  113. var genericType = typeof(IFactory<>).MakeGenericType(currentServiceType);
  114. if (!genericType.IsInstanceOfType(result)) return null;
  115. // Adapt IFactory<T> to IFactory.
  116. var adapterType = typeof(FactoryAdapter<>).MakeGenericType(currentServiceType);
  117. var adapter = (IFactory) Activator.CreateInstance(adapterType, result);
  118. return adapter;
  119. };
  120. return GetFactory(currentServiceType, getStronglyTypedFactory, implementedInterfaces);
  121. };
  122. // Build the list of services that this factory can implement
  123. var servicesToImplement = from f in attributeList
  124. let serviceName = f.ServiceName
  125. let serviceType = f.ServiceType
  126. let argumentTypes = f.ArgumentTypes ?? new Type[0]
  127. let factory = createFactory(serviceType, getFactoryInstance)
  128. where factory != null
  129. select new
  130. {
  131. ServiceName = serviceName,
  132. ServiceType = serviceType,
  133. ArgumentTypes = argumentTypes,
  134. FactoryInstance = factory
  135. };
  136. foreach (var currentService in servicesToImplement)
  137. {
  138. var serviceName = currentService.ServiceName;
  139. var serviceType = currentService.ServiceType;
  140. var argumentTypes = currentService.ArgumentTypes;
  141. var factory = currentService.FactoryInstance;
  142. // HACK: Unnamed custom factories should be able to
  143. // intercept every request for the given service type
  144. if (serviceName == null)
  145. {
  146. var injector = new CustomFactoryInjector(serviceType, factory);
  147. results.Add(container => container.PreProcessors.Add(injector));
  148. }
  149. // Add each service to the container on initialization
  150. results.Add(container => container.AddFactory(serviceName, serviceType, argumentTypes, factory));
  151. }
  152. return results;
  153. }
  154. /// <summary>
  155. /// Instantiates the given factory using the <paramref name="getStronglyTypedFactory">factory functor.</paramref>
  156. /// </summary>
  157. /// <param name="currentServiceType">The service type that will be created by the factory.</param>
  158. /// <param name="getStronglyTypedFactory">The functor that will be responsible for creating the factory itself.</param>
  159. /// <param name="implementedInterfaces">
  160. /// The list of <see cref="IFactory{T}" /> interfaces that are implemented by the
  161. /// source type.
  162. /// </param>
  163. /// <returns>A valid factory instance.</returns>
  164. private static IFactory GetFactory(Type currentServiceType,
  165. Func<IFactoryRequest, IFactory> getStronglyTypedFactory,
  166. ICollection<Type> implementedInterfaces)
  167. {
  168. var genericType = typeof(IFactory<>).MakeGenericType(currentServiceType);
  169. // Lazy-instantiate the factories so that they can be injected by the container
  170. IFactory lazyFactory = new LazyFactory(getStronglyTypedFactory);
  171. IFactory result;
  172. if (implementedInterfaces.Contains(genericType))
  173. {
  174. // Convert the IFactory<T> instance down to an IFactory
  175. // instance so that it can be used by the target container
  176. var lazyFactoryType = typeof(LazyFactory<>).MakeGenericType(currentServiceType);
  177. var adapterType = typeof(FactoryAdapter<>).MakeGenericType(currentServiceType);
  178. lazyFactory = (IFactory) Activator.CreateInstance(lazyFactoryType, getStronglyTypedFactory);
  179. result = (IFactory) Activator.CreateInstance(adapterType, lazyFactory);
  180. return result;
  181. }
  182. // Otherwise, use the untyped IFactory instance instead
  183. result = lazyFactory;
  184. return result;
  185. }
  186. }
  187. }