PageRenderTime 46ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Caliburn.MEF/ComponentPart.cs

http://caliburn.codeplex.com
C# | 295 lines | 132 code | 30 blank | 133 comment | 17 complexity | fef6d49b2eb499440673d6dc846d1cfc MD5 | raw file
Possible License(s): MIT, BSD-3-Clause
  1. namespace Caliburn.MEF
  2. {
  3. using System;
  4. using System.Collections.Generic;
  5. using System.ComponentModel.Composition;
  6. using System.ComponentModel.Composition.Hosting;
  7. using System.ComponentModel.Composition.Primitives;
  8. using System.ComponentModel.Composition.ReflectionModel;
  9. using System.Linq;
  10. using System.Reflection;
  11. using Core.InversionOfControl;
  12. /// <summary>
  13. /// A <see cref="ComposablePart"/> used to configure MEF with Caliburn's required services.
  14. /// </summary>
  15. public class ComponentPart : ComposablePart
  16. {
  17. readonly ComponentRegistrationBase registration;
  18. readonly List<ImportDefinition> imports = new List<ImportDefinition>();
  19. ExportDefinition[] exports;
  20. readonly Dictionary<ImportDefinition, Export> satisfiedImports =
  21. new Dictionary<ImportDefinition, Export>();
  22. object cachedInstance;
  23. readonly ConstructorInfo greedyConstructor;
  24. /// <summary>
  25. /// Initializes a new instance of the <see cref="ComponentPart"/> class.
  26. /// </summary>
  27. /// <param name="registration">The registration.</param>
  28. public ComponentPart(ComponentRegistrationBase registration)
  29. {
  30. this.registration = registration;
  31. var implementation = GetImplementation(registration);
  32. var instanceReg = registration as Instance;
  33. if (instanceReg != null)
  34. {
  35. cachedInstance = instanceReg.Implementation;
  36. }
  37. else
  38. {
  39. greedyConstructor = implementation
  40. .SelectEligibleConstructor();
  41. ConfigureImportDefinitions();
  42. }
  43. ConfigureExportDefinitions(implementation, registration.Service);
  44. }
  45. private void ConfigureExportDefinitions(Type implementationType, Type contractType)
  46. {
  47. var lazyMember = new LazyMemberInfo(implementationType);
  48. var contractName = !registration.HasName()
  49. ? AttributedModelServices.GetContractName(registration.Service)
  50. : registration.Name;
  51. var metadata = new Lazy<IDictionary<string, object>>(() =>
  52. {
  53. var md = new Dictionary<string, object>();
  54. md.Add(CompositionConstants.ExportTypeIdentityMetadataName,
  55. AttributedModelServices.GetTypeIdentity(contractType));
  56. return md;
  57. });
  58. exports = new[] { ReflectionModelServices.CreateExportDefinition(lazyMember, contractName, metadata, null) };
  59. }
  60. private void ConfigureImportDefinitions()
  61. {
  62. foreach (var param in greedyConstructor.GetParameters())
  63. {
  64. var cardinality = GetCardinality(param.ParameterType);
  65. var importType = cardinality == ImportCardinality.ZeroOrMore
  66. ? GetCollectionContractType(param.ParameterType)
  67. : param.ParameterType;
  68. imports.Add(
  69. ReflectionModelServices.CreateImportDefinition(
  70. new Lazy<ParameterInfo>(() => param),
  71. AttributedModelServices.GetContractName(importType),
  72. AttributedModelServices.GetTypeIdentity(importType),
  73. Enumerable.Empty<KeyValuePair<string, Type>>(),
  74. cardinality,
  75. CreationPolicy.Any,
  76. null
  77. )
  78. );
  79. }
  80. }
  81. private static Type GetCollectionContractType(Type collectionType)
  82. {
  83. return collectionType.GetElementType();
  84. }
  85. private static ImportCardinality GetCardinality(Type targetType)
  86. {
  87. if (targetType.IsArray)
  88. return ImportCardinality.ZeroOrMore;
  89. return ImportCardinality.ExactlyOne;
  90. }
  91. /// <summary>
  92. /// Gets the exported object described by the specified definition.
  93. /// </summary>
  94. /// <param name="definition">One of the <see cref="T:System.ComponentModel.Composition.Primitives.ExportDefinition"/> objects from the
  95. /// <see cref="P:System.ComponentModel.Composition.Primitives.ComposablePart.ExportDefinitions"/> property describing the exported object
  96. /// to return.</param>
  97. /// <returns>
  98. /// The exported <see cref="T:System.Object"/> described by <paramref name="definition"/>.
  99. /// </returns>
  100. /// <exception cref="T:System.ArgumentNullException">
  101. /// <paramref name="definition"/> is <see langword="null"/>.
  102. /// </exception>
  103. /// <exception cref="T:System.ArgumentException">
  104. /// <paramref name="definition"/> did not originate from the <see cref="P:System.ComponentModel.Composition.Primitives.ComposablePart.ExportDefinitions"/>
  105. /// property on the <see cref="T:System.ComponentModel.Composition.Primitives.ComposablePart"/>.
  106. /// </exception>
  107. /// <exception cref="T:System.InvalidOperationException">
  108. /// One or more pre-requisite imports, indicated by <see cref="P:System.ComponentModel.Composition.Primitives.ImportDefinition.IsPrerequisite"/>,
  109. /// have not been set.
  110. /// </exception>
  111. /// <exception cref="T:System.ObjectDisposedException">
  112. /// The <see cref="T:System.ComponentModel.Composition.Primitives.ComposablePart"/> has been disposed of.
  113. /// </exception>
  114. /// <exception cref="T:System.ComponentModel.Composition.Primitives.ComposablePartException">
  115. /// An error occurred getting the exported object described by the <see cref="T:System.ComponentModel.Composition.Primitives.ExportDefinition"/>.
  116. /// </exception>
  117. public override object GetExportedValue(ExportDefinition definition)
  118. {
  119. if (registration is PerRequest)
  120. return CreateInstance(definition);
  121. return cachedInstance ?? (cachedInstance = CreateInstance(definition));
  122. }
  123. /// <summary>
  124. /// Creates the instance.
  125. /// </summary>
  126. /// <param name="definition">The definition.</param>
  127. /// <returns></returns>
  128. private object CreateInstance(ExportDefinition definition)
  129. {
  130. var args = new List<object>();
  131. foreach (var parameterInfo in greedyConstructor.GetParameters())
  132. {
  133. var arg = (from export in satisfiedImports.Values
  134. where export.Definition.ContractName ==
  135. AttributedModelServices.GetContractName(parameterInfo.ParameterType)
  136. select export).FirstOrDefault();
  137. args.Add(arg.Value);
  138. }
  139. var instance = args.Count > 0
  140. ? Activator.CreateInstance(GetImplementation(registration), args.ToArray())
  141. : Activator.CreateInstance(GetImplementation(registration));
  142. IoC.Get<CompositionContainer>().SatisfyImportsOnce(instance);
  143. return instance;
  144. }
  145. /// <summary>
  146. /// Sets the import described by the specified definition with the specified exports.
  147. /// </summary>
  148. /// <param name="definition">One of the <see cref="T:System.ComponentModel.Composition.Primitives.ImportDefinition"/> objects from the
  149. /// <see cref="P:System.ComponentModel.Composition.Primitives.ComposablePart.ImportDefinitions"/> property describing the import to be set.</param>
  150. /// <param name="exports">An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:System.ComponentModel.Composition.Primitives.Export"/> objects of which
  151. /// to set the import described by <paramref name="definition"/>.</param>
  152. /// <exception cref="T:System.ArgumentNullException">
  153. /// <paramref name="definition"/> is <see langword="null"/>.
  154. /// <para>
  155. /// -or-
  156. /// </para>
  157. /// <paramref name="exports"/> is <see langword="null"/>.
  158. /// </exception>
  159. /// <exception cref="T:System.ArgumentException">
  160. /// <paramref name="definition"/> did not originate from the <see cref="P:System.ComponentModel.Composition.Primitives.ComposablePart.ImportDefinitions"/>
  161. /// property on the <see cref="T:System.ComponentModel.Composition.Primitives.ComposablePart"/>.
  162. /// <para>
  163. /// -or-
  164. /// </para>
  165. /// <paramref name="exports"/> contains an element that is <see langword="null"/>.
  166. /// <para>
  167. /// -or-
  168. /// </para>
  169. /// <paramref name="exports"/> is empty and <see cref="P:System.ComponentModel.Composition.Primitives.ImportDefinition.Cardinality"/> is
  170. /// <see cref="F:System.ComponentModel.Composition.Primitives.ImportCardinality.ExactlyOne"/>.
  171. /// <para>
  172. /// -or-
  173. /// </para>
  174. /// <paramref name="exports"/> contains more than one element and
  175. /// <see cref="P:System.ComponentModel.Composition.Primitives.ImportDefinition.Cardinality"/> is <see cref="F:System.ComponentModel.Composition.Primitives.ImportCardinality.ZeroOrOne"/> or
  176. /// <see cref="F:System.ComponentModel.Composition.Primitives.ImportCardinality.ExactlyOne"/>.
  177. /// </exception>
  178. /// <exception cref="T:System.InvalidOperationException">
  179. /// <see cref="M:System.ComponentModel.Composition.Primitives.ComposablePart.OnComposed"/> has been previously called and
  180. /// <see cref="P:System.ComponentModel.Composition.Primitives.ImportDefinition.IsRecomposable"/> is <see langword="false"/>.
  181. /// </exception>
  182. /// <exception cref="T:System.ObjectDisposedException">
  183. /// The <see cref="T:System.ComponentModel.Composition.Primitives.ComposablePart"/> has been disposed of.
  184. /// </exception>
  185. /// <exception cref="T:System.ComponentModel.Composition.Primitives.ComposablePartException">
  186. /// An error occurred setting the import described by the <see cref="T:System.ComponentModel.Composition.Primitives.ImportDefinition"/>.
  187. /// </exception>
  188. public override void SetImport(ImportDefinition definition, IEnumerable<Export> exports)
  189. {
  190. if (definition == null) throw new ArgumentNullException("definition");
  191. if (exports == null) throw new ArgumentNullException("exports");
  192. satisfiedImports[definition] = exports.FirstOrDefault();
  193. }
  194. /// <summary>
  195. /// Gets the export definitions that describe the exported objects provided by the part.
  196. /// </summary>
  197. /// <value>
  198. /// An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:System.ComponentModel.Composition.Primitives.ExportDefinition"/> objects describing
  199. /// the exported objects provided by the <see cref="T:System.ComponentModel.Composition.Primitives.ComposablePart"/>.
  200. /// </value>
  201. /// <exception cref="T:System.ObjectDisposedException">
  202. /// The <see cref="T:System.ComponentModel.Composition.Primitives.ComposablePart"/> has been disposed of.
  203. /// </exception>
  204. /// <remarks>
  205. /// <para>
  206. /// <note type="inheritinfo">
  207. /// If the <see cref="T:System.ComponentModel.Composition.Primitives.ComposablePart"/> was created from a
  208. /// <see cref="T:System.ComponentModel.Composition.Primitives.ComposablePartDefinition"/>, this property should return the result of
  209. /// <see cref="P:System.ComponentModel.Composition.Primitives.ComposablePartDefinition.ExportDefinitions"/>.
  210. /// </note>
  211. /// </para>
  212. /// <para>
  213. /// <note type="inheritinfo">
  214. /// Overriders of this property should never return <see langword="null"/>.
  215. /// If the <see cref="T:System.ComponentModel.Composition.Primitives.ComposablePart"/> does not have exports, return an empty
  216. /// <see cref="T:System.Collections.Generic.IEnumerable`1"/> instead.
  217. /// </note>
  218. /// </para>
  219. /// </remarks>
  220. public override IEnumerable<ExportDefinition> ExportDefinitions
  221. {
  222. get { return exports; }
  223. }
  224. /// <summary>
  225. /// Gets the import definitions that describe the imports required by the part.
  226. /// </summary>
  227. /// <value>
  228. /// An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:System.ComponentModel.Composition.Primitives.ImportDefinition"/> objects describing
  229. /// the imports required by the <see cref="T:System.ComponentModel.Composition.Primitives.ComposablePart"/>.
  230. /// </value>
  231. /// <exception cref="T:System.ObjectDisposedException">
  232. /// The <see cref="T:System.ComponentModel.Composition.Primitives.ComposablePart"/> has been disposed of.
  233. /// </exception>
  234. /// <remarks>
  235. /// <para>
  236. /// <note type="inheritinfo">
  237. /// If the <see cref="T:System.ComponentModel.Composition.Primitives.ComposablePart"/> was created from a
  238. /// <see cref="T:System.ComponentModel.Composition.Primitives.ComposablePartDefinition"/>, this property should return the result of
  239. /// <see cref="P:System.ComponentModel.Composition.Primitives.ComposablePartDefinition.ImportDefinitions"/>.
  240. /// </note>
  241. /// </para>
  242. /// <para>
  243. /// <note type="inheritinfo">
  244. /// Overrides of this property should never return <see langword="null"/>.
  245. /// If the <see cref="T:System.ComponentModel.Composition.Primitives.ComposablePart"/> does not have imports, return an empty
  246. /// <see cref="T:System.Collections.Generic.IEnumerable`1"/> instead.
  247. /// </note>
  248. /// </para>
  249. /// </remarks>
  250. public override IEnumerable<ImportDefinition> ImportDefinitions
  251. {
  252. get { return imports; }
  253. }
  254. private static Type GetImplementation(IComponentRegistration registration)
  255. {
  256. var singleton = registration as Singleton;
  257. if (singleton != null) return singleton.Implementation;
  258. var perRequest = registration as PerRequest;
  259. if (perRequest != null) return perRequest.Implementation;
  260. var instance = registration as Instance;
  261. if (instance != null && instance.Implementation != null) return instance.Implementation.GetType();
  262. throw new NotSupportedException();
  263. }
  264. }
  265. }