Dot-Net
Prism中如何控制模組初始化的順序
我將 Prism V2 與 DirectoryModuleCatalog 一起使用,我需要按特定順序初始化模組。所需的順序由每個 IModule 實現上的屬性指定。
這樣一來,在初始化每個模組時,它們會將其視圖添加到 TabControl 區域中,並且選項卡的順序需要確定並由模組作者控制。
該順序並不意味著依賴關係,而只是它們應該被初始化的順序。換句話說:模組 A、B 和 C 的優先級可能分別為 1、2 和 3。B 不依賴於 A - 它只需要在 A之後載入到 TabControl 區域。這樣我們就有了一個確定性和可控的選項卡順序。此外,B 在執行時可能不存在;因此它們將載入為 A、C,因為優先級應確定順序 (1, 3)。如果我使用了 ModuleDependency,那麼模組“C”將無法載入它的所有依賴項。
我可以管理如何對模組進行排序的邏輯,但我不知道將所述邏輯放在哪裡。
我不喜歡使用 ModuleDependency 的想法,因為這意味著當模組 b 不存在時模組 a 不會載入,而實際上沒有依賴關係。相反,我創建了一個優先級屬性來裝飾模組:
/// <summary> /// Allows the order of module loading to be controlled. Where dependencies /// allow, module loading order will be controlled by relative values of priority /// </summary> [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public sealed class PriorityAttribute : Attribute { /// <summary> /// Constructor /// </summary> /// <param name="priority">the priority to assign</param> public PriorityAttribute(int priority) { this.Priority = priority; } /// <summary> /// Gets or sets the priority of the module. /// </summary> /// <value>The priority of the module.</value> public int Priority { get; private set; } }然後我像這樣裝飾模組:
[Priority(200)] [Module(ModuleName = "MyModule")] public class MyModule : IModule我創建了 DirectoryModuleCatalog 的新後代:
/// <summary> /// ModuleCatalog that respects PriorityAttribute for sorting modules /// </summary> [SecurityPermission(SecurityAction.InheritanceDemand), SecurityPermission(SecurityAction.LinkDemand)] public class PrioritizedDirectoryModuleCatalog : DirectoryModuleCatalog { /// <summary> /// local class to load assemblies into different appdomain which is then discarded /// </summary> private class ModulePriorityLoader : MarshalByRefObject { /// <summary> /// Get the priorities /// </summary> /// <param name="modules"></param> /// <returns></returns> [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom")] public Dictionary<string, int> GetPriorities(IEnumerable<ModuleInfo> modules) { //retrieve the priorities of each module, so that we can use them to override the //sorting - but only so far as we don't mess up the dependencies var priorities = new Dictionary<string, int>(); var assemblies = new Dictionary<string, Assembly>(); foreach (ModuleInfo module in modules) { if (!assemblies.ContainsKey(module.Ref)) { //LoadFrom should generally be avoided appently due to unexpected side effects, //but since we are doing all this in a separate AppDomain which is discarded //this needn't worry us assemblies.Add(module.Ref, Assembly.LoadFrom(module.Ref)); } Type type = assemblies[module.Ref].GetExportedTypes() .Where(t => t.AssemblyQualifiedName.Equals(module.ModuleType, StringComparison.Ordinal)) .First(); var priorityAttribute = CustomAttributeData.GetCustomAttributes(type).FirstOrDefault( cad => cad.Constructor.DeclaringType.FullName == typeof(PriorityAttribute).FullName); int priority; if (priorityAttribute != null) { priority = (int)priorityAttribute.ConstructorArguments[0].Value; } else { priority = 0; } priorities.Add(module.ModuleName, priority); } return priorities; } } /// <summary> /// Get the priorities that have been assigned to each module. If a module does not have a priority /// assigned (via the Priority attribute) then it is assigned a priority of 0 /// </summary> /// <param name="modules">modules to retrieve priorities for</param> /// <returns></returns> private Dictionary<string, int> GetModulePriorities(IEnumerable<ModuleInfo> modules) { AppDomain childDomain = BuildChildDomain(AppDomain.CurrentDomain); try { Type loaderType = typeof(ModulePriorityLoader); var loader = (ModulePriorityLoader) childDomain.CreateInstanceFrom(loaderType.Assembly.Location, loaderType.FullName).Unwrap(); return loader.GetPriorities(modules); } finally { AppDomain.Unload(childDomain); } } /// <summary> /// Sort modules according to dependencies and Priority /// </summary> /// <param name="modules">modules to sort</param> /// <returns>sorted modules</returns> protected override IEnumerable<ModuleInfo> Sort(IEnumerable<ModuleInfo> modules) { Dictionary<string, int> priorities = GetModulePriorities(modules); //call the base sort since it resolves dependencies, then re-sort var result = new List<ModuleInfo>(base.Sort(modules)); result.Sort((x, y) => { string xModuleName = x.ModuleName; string yModuleName = y.ModuleName; //if one depends on other then non-dependent must come first //otherwise base on priority if (x.DependsOn.Contains(yModuleName)) return 1; //x after y else if (y.DependsOn.Contains(xModuleName)) return -1; //y after x else return priorities[xModuleName].CompareTo(priorities[yModuleName]); }); return result; } }最後,我更改了引導程序以使用這個新目錄:
/// <summary>Where are the modules located</summary> /// <returns></returns> protected override IModuleCatalog GetModuleCatalog() { return new PrioritizedDirectoryModuleCatalog() { ModulePath = @".\Modules" }; }我不確定帶有程序集載入的東西是否是最好的做事方式,但它似乎工作……