Dot-Net

Prism中如何控制模組初始化的順序

  • April 25, 2020

我將 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" };
   }

我不確定帶有程序集載入的東西是否是最好的做事方式,但它似乎工作……

引用自:https://stackoverflow.com/questions/1296642