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

http://github.com/philiplaureano/LinFu · C# · 140 lines · 86 code · 14 blank · 40 comment · 9 complexity · 6a362929dae60586bebe7961312588e4 MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Reflection;
  6. using LinFu.IoC.Configuration.Interfaces;
  7. using LinFu.IoC.Factories;
  8. using LinFu.IoC.Interfaces;
  9. namespace LinFu.IoC.Configuration.Loaders
  10. {
  11. /// <summary>
  12. /// A loader class that scans a type for <see cref="ImplementsAttribute" />
  13. /// attribute declarations and creates a factory for each corresponding
  14. /// attribute instance.
  15. /// </summary>
  16. /// <seealso cref="IFactory" />
  17. public class ImplementsAttributeLoader : ITypeLoader
  18. {
  19. /// <summary>
  20. /// Converts a given <see cref="System.Type" /> into
  21. /// a set of <see cref="Action{IServiceContainer}" /> instances so that
  22. /// the <see cref="IContainer" /> instance can be loaded
  23. /// with the given factories.
  24. /// </summary>
  25. /// <param name="sourceType">The input type from which one or more factories will be created.</param>
  26. /// <returns>A set of <see cref="Action{IServiceContainer}" /> instances. This cannot be null.</returns>
  27. public IEnumerable<Action<IServiceContainer>> Load(Type sourceType)
  28. {
  29. // Extract the Implements attribute from the source type
  30. ICustomAttributeProvider provider = sourceType;
  31. var attributes = provider.GetCustomAttributes(typeof(ImplementsAttribute), false);
  32. var attributeList = attributes.Cast<ImplementsAttribute>().ToList();
  33. var results = new List<Action<IServiceContainer>>();
  34. IFactory singletonFactory = null;
  35. foreach (var attribute in attributeList)
  36. {
  37. var serviceName = attribute.ServiceName;
  38. var serviceType = attribute.ServiceType;
  39. var lifeCycle = attribute.LifecycleType;
  40. var currentFactory = CreateFactory(serviceType, sourceType, lifeCycle);
  41. if (currentFactory == null)
  42. continue;
  43. // If this type is implemented as a factory singleton,
  44. // it only needs to be implemented once
  45. if (lifeCycle == LifecycleType.Singleton)
  46. {
  47. if (singletonFactory == null)
  48. singletonFactory = currentFactory;
  49. else
  50. currentFactory = singletonFactory;
  51. }
  52. results.Add(container =>
  53. container.AddFactory(serviceName, serviceType, currentFactory));
  54. }
  55. return results;
  56. }
  57. /// <summary>
  58. /// Determines whether or not the current <paramref name="sourceType" />
  59. /// can be loaded.
  60. /// </summary>
  61. /// <param name="sourceType">The source type currently being loaded.</param>
  62. /// <returns>Returns <c>true</c> if the type is a class type; otherwise, it returns <c>false</c>.</returns>
  63. public bool CanLoad(Type sourceType)
  64. {
  65. try
  66. {
  67. return sourceType.IsClass;
  68. }
  69. catch (TypeInitializationException)
  70. {
  71. // Ignore the error
  72. return false;
  73. }
  74. catch (FileNotFoundException)
  75. {
  76. // Ignore the error
  77. return false;
  78. }
  79. }
  80. /// <summary>
  81. /// Creates a factory instance that can create instaces of the given
  82. /// <paramref name="serviceType" /> using the <paramref name="implementingType" />
  83. /// as the implementation.
  84. /// </summary>
  85. /// <param name="serviceType">The service being implemented.</param>
  86. /// <param name="implementingType">The actual type that will implement the service.</param>
  87. /// <param name="lifecycle">The <see cref="LifecycleType" /> that determines the lifetime of each instance being created.</param>
  88. /// <returns>A valid <see cref="IFactory" /> instance.</returns>
  89. private static IFactory CreateFactory(Type serviceType, Type implementingType, LifecycleType lifecycle)
  90. {
  91. // HACK: Use a lazy factory since the actualy IFactoryBuilder instance won't
  92. // be available until runtime
  93. Func<IFactoryRequest, object> factoryMethod =
  94. request =>
  95. {
  96. var currentContainer = request.Container;
  97. var arguments = request.Arguments;
  98. var builder = currentContainer.GetService<IFactoryBuilder>();
  99. // HACK: If the service type is a type definition and
  100. // the implementing type is a type definition,
  101. // assume that the service type has the same number of
  102. // generic arguments as the implementing type
  103. var actualServiceType = serviceType;
  104. var actualImplementingType = implementingType;
  105. if (serviceType.IsGenericTypeDefinition && implementingType.IsGenericTypeDefinition &&
  106. serviceType.GetGenericArguments().Count() == implementingType.GetGenericArguments().Count())
  107. {
  108. var typeArguments = request.ServiceType.GetGenericArguments();
  109. actualServiceType = serviceType.MakeGenericType(typeArguments);
  110. actualImplementingType = implementingType.MakeGenericType(typeArguments);
  111. }
  112. var actualFactory = builder.CreateFactory(actualServiceType, actualImplementingType,
  113. lifecycle);
  114. var factoryRequest = new FactoryRequest
  115. {
  116. ServiceType = serviceType,
  117. ServiceName = request.ServiceName,
  118. Arguments = arguments,
  119. Container = currentContainer
  120. };
  121. return actualFactory.CreateInstance(factoryRequest);
  122. };
  123. return new FunctorFactory(factoryMethod);
  124. }
  125. }
  126. }