PageRenderTime 52ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/V4/PrismLibrary/Silverlight/Prism.MefExtensions/Modularity/MefXapModuleTypeLoader.cs

#
C# | 308 lines | 223 code | 37 blank | 48 comment | 20 complexity | 1794b3337aa6f86122cde9be476421f3 MD5 | raw file
  1. //===================================================================================
  2. // Microsoft patterns & practices
  3. // Composite Application Guidance for Windows Presentation Foundation and Silverlight
  4. //===================================================================================
  5. // Copyright (c) Microsoft Corporation. All rights reserved.
  6. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
  7. // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
  8. // LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  9. // FITNESS FOR A PARTICULAR PURPOSE.
  10. //===================================================================================
  11. // The example companies, organizations, products, domain names,
  12. // e-mail addresses, logos, people, places, and events depicted
  13. // herein are fictitious. No association with any real company,
  14. // organization, product, domain name, email address, logo, person,
  15. // places, or events is intended or should be inferred.
  16. //===================================================================================
  17. using System;
  18. using System.Collections.Generic;
  19. using System.ComponentModel;
  20. using System.ComponentModel.Composition;
  21. using System.ComponentModel.Composition.Hosting;
  22. using System.Net;
  23. using System.Windows;
  24. using Microsoft.Practices.Prism.Modularity;
  25. namespace Microsoft.Practices.Prism.MefExtensions.Modularity
  26. {
  27. /// <summary>
  28. /// Provides a XAP type loader using the MEF DeploymentCatalog.
  29. /// </summary>
  30. [Export]
  31. public class MefXapModuleTypeLoader : IModuleTypeLoader
  32. {
  33. private Dictionary<Uri, List<ModuleInfo>> downloadingModules = new Dictionary<Uri, List<ModuleInfo>>();
  34. private HashSet<Uri> downloadedUris = new HashSet<Uri>();
  35. private DownloadedPartCatalogCollection downloadedPartCatalogs;
  36. /// <summary>
  37. /// Initializes a new instance of the <see cref="MefXapModuleTypeLoader"/> class.
  38. /// </summary>
  39. /// <param name="downloadedPartCatalogs">The downloaded part catalog collection.</param>
  40. [ImportingConstructor]
  41. public MefXapModuleTypeLoader(DownloadedPartCatalogCollection downloadedPartCatalogs)
  42. {
  43. if (downloadedPartCatalogs == null)
  44. {
  45. throw new ArgumentNullException("downloadedPartCatalogs");
  46. }
  47. this.downloadedPartCatalogs = downloadedPartCatalogs;
  48. }
  49. /// <summary>
  50. /// Raised repeatedly to provide progress as modules are loaded in the background.
  51. /// </summary>
  52. public virtual event EventHandler<ModuleDownloadProgressChangedEventArgs> ModuleDownloadProgressChanged;
  53. private void RaiseModuleDownloadProgressChanged(ModuleInfo moduleInfo, long bytesReceived, long totalBytesToReceive)
  54. {
  55. this.RaiseModuleDownloadProgressChanged(new ModuleDownloadProgressChangedEventArgs(moduleInfo, bytesReceived, totalBytesToReceive));
  56. }
  57. private void RaiseModuleDownloadProgressChanged(ModuleDownloadProgressChangedEventArgs e)
  58. {
  59. if (this.ModuleDownloadProgressChanged != null)
  60. {
  61. this.ModuleDownloadProgressChanged(this, e);
  62. }
  63. }
  64. /// <summary>
  65. /// Raised when a module is loaded or fails to load.
  66. /// </summary>
  67. public virtual event EventHandler<LoadModuleCompletedEventArgs> LoadModuleCompleted;
  68. private void RaiseLoadModuleCompleted(ModuleInfo moduleInfo, Exception error)
  69. {
  70. this.RaiseLoadModuleCompleted(new LoadModuleCompletedEventArgs(moduleInfo, error));
  71. }
  72. private void RaiseLoadModuleCompleted(LoadModuleCompletedEventArgs e)
  73. {
  74. if (this.LoadModuleCompleted != null)
  75. {
  76. this.LoadModuleCompleted(this, e);
  77. }
  78. }
  79. /// <summary>
  80. /// Evaluates the <see cref="ModuleInfo.Ref"/> property to see if the current typeloader will be able to retrieve the <paramref name="moduleInfo"/>.
  81. /// Returns true if the <see cref="ModuleInfo.Ref"/> property is a URI, because this indicates that the file is a downloadable file.
  82. /// </summary>
  83. /// <param name="moduleInfo">Module that should have it's type loaded.</param>
  84. /// <returns>
  85. /// <see langword="true"/> if the current typeloader is able to retrieve the module, otherwise <see langword="false"/>.
  86. /// </returns>
  87. public virtual bool CanLoadModuleType(ModuleInfo moduleInfo)
  88. {
  89. if (moduleInfo == null)
  90. {
  91. throw new ArgumentNullException("moduleInfo");
  92. }
  93. if (!string.IsNullOrEmpty(moduleInfo.Ref))
  94. {
  95. Uri uriRef;
  96. return Uri.TryCreate(moduleInfo.Ref, UriKind.RelativeOrAbsolute, out uriRef);
  97. }
  98. return false;
  99. }
  100. /// <summary>
  101. /// Retrieves the <paramref name="moduleInfo"/>.
  102. /// </summary>
  103. /// <param name="moduleInfo">Module that should have it's type loaded.</param>
  104. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exceptions are included in the resulting message and are handled by subscribers of the LoadModuleCompleted event.")]
  105. public virtual void LoadModuleType(ModuleInfo moduleInfo)
  106. {
  107. if (moduleInfo == null)
  108. {
  109. throw new System.ArgumentNullException("moduleInfo");
  110. }
  111. try
  112. {
  113. Uri uri = new Uri(moduleInfo.Ref, UriKind.RelativeOrAbsolute);
  114. DownloadModuleFromUri(moduleInfo, uri);
  115. }
  116. catch (Exception ex)
  117. {
  118. this.RaiseLoadModuleCompleted(moduleInfo, ex);
  119. }
  120. }
  121. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "The deplyment catalog is added to the container and disposed when the container is disposed.")]
  122. private void DownloadModuleFromUri(ModuleInfo moduleInfo, Uri uri)
  123. {
  124. DeploymentCatalog deploymentCatalog = new DeploymentCatalog(uri);
  125. try
  126. {
  127. // If this module has already been downloaded, fire the completed event.
  128. if (this.IsSuccessfullyDownloaded(deploymentCatalog.Uri))
  129. {
  130. this.RaiseLoadModuleCompleted(moduleInfo, null);
  131. }
  132. else
  133. {
  134. bool needToStartDownload = !this.IsDownloading(uri);
  135. // I record downloading for the moduleInfo even if I don't need to start a new download
  136. this.RecordDownloading(uri, moduleInfo);
  137. if (needToStartDownload)
  138. {
  139. deploymentCatalog.DownloadProgressChanged += this.DeploymentCatalog_DownloadProgressChanged;
  140. deploymentCatalog.DownloadCompleted += this.DeploymentCatalog_DownloadCompleted;
  141. deploymentCatalog.DownloadAsync();
  142. }
  143. }
  144. }
  145. catch (Exception)
  146. {
  147. // if there is an exception between creating the deployment catalog and calling DownloadAsync,
  148. // the deployment catalog needs to be disposed.
  149. // otherwise, it is added to the compositioncontainer which should handle this.
  150. deploymentCatalog.Dispose();
  151. throw;
  152. }
  153. }
  154. private void DeploymentCatalog_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
  155. {
  156. DeploymentCatalog deploymentCatalog = (DeploymentCatalog)sender;
  157. // I ensure the download progress changed is raised is on the UI thread.
  158. if (!Deployment.Current.Dispatcher.CheckAccess())
  159. {
  160. Deployment.Current.Dispatcher.BeginInvoke(new Action<DeploymentCatalog, DownloadProgressChangedEventArgs>(this.HandleDownloadProgressChanged), deploymentCatalog, e);
  161. }
  162. else
  163. {
  164. this.HandleDownloadProgressChanged(deploymentCatalog, e);
  165. }
  166. }
  167. private void HandleDownloadProgressChanged(DeploymentCatalog deploymentCatalog, DownloadProgressChangedEventArgs e)
  168. {
  169. List<ModuleInfo> moduleInfos = this.GetDownloadingModules(deploymentCatalog.Uri);
  170. foreach (ModuleInfo moduleInfo in moduleInfos)
  171. {
  172. this.RaiseModuleDownloadProgressChanged(moduleInfo, e.BytesReceived, e.TotalBytesToReceive);
  173. }
  174. }
  175. private void DeploymentCatalog_DownloadCompleted(object sender, AsyncCompletedEventArgs e)
  176. {
  177. DeploymentCatalog deploymentCatalog = (DeploymentCatalog)sender;
  178. deploymentCatalog.DownloadProgressChanged -= DeploymentCatalog_DownloadProgressChanged;
  179. deploymentCatalog.DownloadCompleted -= DeploymentCatalog_DownloadCompleted;
  180. // I ensure the download completed is on the UI thread so that types can be loaded into the application domain.
  181. if (!Deployment.Current.Dispatcher.CheckAccess())
  182. {
  183. Deployment.Current.Dispatcher.BeginInvoke(new Action<DeploymentCatalog, AsyncCompletedEventArgs>(this.HandleDownloadCompleted), deploymentCatalog, e);
  184. }
  185. else
  186. {
  187. this.HandleDownloadCompleted(deploymentCatalog, e);
  188. }
  189. }
  190. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exceptions are included in the resulting message and are handled by subscribers of the LoadModuleCompleted event.")]
  191. private void HandleDownloadCompleted(DeploymentCatalog deploymentCatalog, AsyncCompletedEventArgs e)
  192. {
  193. List<ModuleInfo> moduleInfos = this.GetDownloadingModules(deploymentCatalog.Uri);
  194. Exception error = e.Error;
  195. if (error == null)
  196. {
  197. try
  198. {
  199. this.RecordDownloadComplete(deploymentCatalog.Uri);
  200. foreach (ModuleInfo moduleInfo in moduleInfos)
  201. {
  202. this.downloadedPartCatalogs.Add(moduleInfo, deploymentCatalog);
  203. }
  204. this.RecordDownloadSuccess(deploymentCatalog.Uri);
  205. }
  206. catch (Exception ex)
  207. {
  208. error = ex;
  209. }
  210. }
  211. foreach (ModuleInfo moduleInfo in moduleInfos)
  212. {
  213. this.RaiseLoadModuleCompleted(moduleInfo, error);
  214. }
  215. }
  216. private bool IsDownloading(Uri uri)
  217. {
  218. lock (this.downloadingModules)
  219. {
  220. return this.downloadingModules.ContainsKey(uri);
  221. }
  222. }
  223. private void RecordDownloading(Uri uri, ModuleInfo moduleInfo)
  224. {
  225. lock (this.downloadingModules)
  226. {
  227. List<ModuleInfo> moduleInfos;
  228. if (!this.downloadingModules.TryGetValue(uri, out moduleInfos))
  229. {
  230. moduleInfos = new List<ModuleInfo>();
  231. this.downloadingModules.Add(uri, moduleInfos);
  232. }
  233. if (!moduleInfos.Contains(moduleInfo))
  234. {
  235. moduleInfos.Add(moduleInfo);
  236. }
  237. }
  238. }
  239. private List<ModuleInfo> GetDownloadingModules(Uri uri)
  240. {
  241. lock (this.downloadingModules)
  242. {
  243. return new List<ModuleInfo>(this.downloadingModules[uri]);
  244. }
  245. }
  246. private void RecordDownloadComplete(Uri uri)
  247. {
  248. lock (this.downloadingModules)
  249. {
  250. if (!this.downloadingModules.ContainsKey(uri))
  251. {
  252. this.downloadingModules.Remove(uri);
  253. }
  254. }
  255. }
  256. private bool IsSuccessfullyDownloaded(Uri uri)
  257. {
  258. lock (this.downloadedUris)
  259. {
  260. return this.downloadedUris.Contains(uri);
  261. }
  262. }
  263. private void RecordDownloadSuccess(Uri uri)
  264. {
  265. lock (this.downloadedUris)
  266. {
  267. this.downloadedUris.Add(uri);
  268. }
  269. }
  270. }
  271. }