2009-08-18 29 views
9

Tôi đang sử dụng Prism V2 với một DirectoryModuleCatalog và tôi cần các mô đun được khởi tạo theo thứ tự nhất định. Thứ tự mong muốn được xác định với một thuộc tính trên mỗi lần thực hiện IModule.Cách kiểm soát thứ tự khởi tạo mô-đun trong Prism

Điều này là do mỗi mô-đun được khởi tạo, họ thêm Chế độ xem của họ vào vùng TabControl và thứ tự của các tab cần phải được xác định và kiểm soát bởi tác giả mô-đun.

Thứ tự không ngụ ý sự phụ thuộc, mà chỉ là thứ tự mà chúng nên được khởi tạo. Nói cách khác: các mô-đun A, B và C có thể có mức độ ưu tiên là 1, 2 và 3 tương ứng. B không có sự phụ thuộc vào A - nó chỉ cần được tải vào vùng TabControl sau A. Để chúng tôi có thứ tự xác định và có thể điều khiển được của các tab. Ngoài ra, B có thể không tồn tại trong thời gian chạy; do đó, họ sẽ tải dưới dạng A, C vì mức độ ưu tiên sẽ xác định thứ tự (1, 3). Nếu tôi sử dụng ModuleDependency, thì module "C" sẽ không thể tải w/o tất cả các phụ thuộc của nó.

Tôi có thể quản lý lôgic về cách sắp xếp các mô-đun, nhưng tôi không thể tìm ra trong đó để đặt logic đã nói.

Trả lời

13

Tôi không thích ý tưởng sử dụng ModuleDependency vì điều này sẽ có nghĩa là mô-đun một sẽ không tải khi mô-đun b đã không có mặt, khi trên thực tế không có sự phụ thuộc.Thay vào đó tôi đã tạo một thuộc tính ưu tiên để trang trí các module:

/// <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; } 
} 

sau đó tôi được trang trí các module như thế này:

[Priority(200)] 
[Module(ModuleName = "MyModule")] 
public class MyModule : IModule 

Tôi tạo ra một hậu duệ mới của 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; 
    } 
} 

Cuối cùng, Tôi đã thay đổi bootstrapper để sử dụng danh mục mới này:

/// <summary>Where are the modules located</summary> 
    /// <returns></returns> 
    protected override IModuleCatalog GetModuleCatalog() 
    { 
     return new PrioritizedDirectoryModuleCatalog() { ModulePath = @".\Modules" }; 
    } 

Tôi không chắc chắn nếu những thứ với lắp ráp tải là cách tốt nhất để làm điều này, nhưng có vẻ như để làm việc ...

+0

+1 Tôi muốn biết cách làm điều này trong một thời gian. Cảm ơn bạn rât nhiêu. – Phil

+0

Giải pháp tuyệt vời, chính xác những gì tôi đang tìm kiếm! – Lance

0

Trong cuộc gọi AddModule() trong Bootstrapper, bạn có thể chỉ định phụ thuộc. Vì vậy, bạn có thể nói A phụ thuộc vào B phụ thuộc vào C, và điều đó sẽ xác định thứ tự tải.

http://msdn.microsoft.com/en-us/magazine/cc785479.aspx

+0

Tôi không gọi "AddModule()"; Tôi đang sử dụng DirectoryModuleCatalog mà tìm tất cả các IModules trong một con đường nhất định. –

3

Bạn có thể sử dụng thuộc tính ModuleDependency trên lớp mô-đun của bạn để cho bộ nạp rằng mô-đun của bạn phụ thuộc vào các module khác:

[ModuleDependency("SomeModule")] 
[ModuleDependency("SomeOtherModule")] 
public class MyModule : IModule 
{ 
} 
+0

Các mô-đun không phụ thuộc; chi tiết hơn được thêm vào câu hỏi –

+0

Sự phụ thuộc không phải là vật lý theo nghĩa MyModule sử dụng một cái gì đó từ SomeModule và SomeOtherModule, nhưng đúng hơn là việc tải MyModule phụ thuộc vào hai mô-đun kia đang được nạp. Prism không quan tâm đến loại phụ thuộc giữa các mô đun và thuộc tính ModuleDependency có thể được sử dụng để thực thi bất kỳ loại phụ thuộc nào. –

+0

trong ví dụ về A, B, C mà tôi vừa thêm vào - B có thể không tồn tại; do đó, họ sẽ tải như A, C vì thứ tự vẫn đúng (1, 3). Nếu tôi đã sử dụng ModuleDependency, thì mô-đun "C" sẽ không thể tải w/o tất cả phụ thuộc của nó. –

2

Bạn có thể thay thế mặc định IModuleInitializer cho một thể hiện của một lớp tùy chỉnh thay vì khởi tạo các mô-đun ngay sau khi chúng được tải, lưu trữ chúng trong một danh sách mô-đun. Khi tất cả các mô-đun đã được tải, bạn khởi tạo chúng theo bất kỳ thứ tự nào bạn muốn.

Làm thế nào để đạt được điều này:

1) Trong bootstrapper, ghi đè lên ConfigureContainer phương pháp để thay thế mặc định IModuleInitializer cho một trường hợp của lớp MyModuleInitializer, nhưng việc duy trì sự khởi tạo mặc định với một tên (ví dụ, defaultModuleInitializer):


protected override void ConfigureContainer() 
{ 
    base.ConfigureContainer(); 
    var defaultContainer = Container.Resolve<IModuleInitializer>(); 
    Container.RegisterInstance<IModuleInitializer>("defaultModuleInitializer", defaultContainer); 
    Container.RegisterType<IModuleInitializer, MyModuleInitializer>(new ContainerControlledLifetimeManager()); 
} 


2) Tạo MyModuleInitializer lớp mà thực hiện thủ tục mong muốn storea-tất cả-then-sort-and-initialize:


public class MyModuleInitializer : IModuleInitializer 
{ 
    bool initialModuleLoadCompleted = false; 
    IModuleInitializer defaultInitializer = null; 
    List<ModuleInfo> modules = new List<ModuleInfo>(); 

    public MyModuleInitializer(IUnityContainer container) 
    { 
     defaultInitializer = container.Resolve<IModuleInitializer>("defaultModuleInitializer"); 
    } 

    public void Initialize(ModuleInfo moduleInfo) 
    { 
     if(initialModuleLoadCompleted) { 
      //Module loaded on demand after application startup - use the default initializer 
      defaultInitializer.Initialize(moduleInfo); 
      return; 
     } 

     modules.Add(moduleInfo); 

     if(AllModulesLoaded()) { 
      SortModules(); 
      foreach(var module in modules) { 
       defaultInitializer.Initialize(module); 
      } 
      modules = null; 
      initialModuleLoadCompleted = true; 
     } 
    } 

    private bool AllModulesLoaded() 
    { 
     //Here you check whether all the startup modules have been loaded 
     //(perhaps by looking at the module catalog) and return true if so 
    } 

    private void SortModules() 
    { 
     //Here you sort the "modules" list however you want 
    } 
} 

Lưu ý rằng sau khi tất cả các module khởi động đã được kích hoạt, lớp này lại trở về chỉ đơn giản là cách gọi bộ khởi tạo mặc định. Thích nghi với lớp học một cách thích hợp nếu đây không phải là những gì bạn cần.

+0

Đây là một giải pháp khá tốt. Phần khó khăn duy nhất là biết khi nào "AllModulesLoaded". Kể từ khi tôi đang sử dụng DirectoryModuleCatalog, tôi không thực sự có một cách dễ dàng để biết điều đó. Cảm ơn câu trả lời; Tôi đã giải quyết vấn đề một cách hoàn toàn khác. –

+0

+1 Tôi thích cái này nhiều hơn câu trả lời được chấp nhận: ở đó tôi không thích ý tưởng rằng các mô-đun phải biết về thứ tự mà chúng được nạp, hãy nói một mình về tên của những mô-đun đó. – stijn

1

tôi giải quyết điều này bằng cách sử dụng thuộc tính ModuleDependency và nó làm việc như một nét duyên dáng

0

Đưa trở lại này lại từ cõi chết như tôi dường như đã tìm thấy một giải pháp khác nhau mà một số có thể hữu ích. Tôi đã thử nó và nó hoạt động nhưng tôi vẫn chưa cảm nhận được tất cả những ưu và khuyết điểm.

Tôi đã sử dụng DirectoryModuleCatalog để có danh sách tất cả các mô-đun của mình được đặt vào một thư mục duy nhất. Nhưng tôi nhận thấy rằng phần lớn tất cả các mô-đun "Xem" của tôi phụ thuộc vào các mô-đun "Dịch vụ" của tôi và đó là một mẫu khá phổ biến. Không có dịch vụ nào phụ thuộc vào chế độ xem. Vì vậy, điều đó khiến tôi suy nghĩ, nếu chúng ta chỉ đặt tất cả các mô-đun dịch vụ vào một thư mục và tất cả các mô-đun xem vào một thư mục khác và tạo hai danh mục khác nhau theo đúng thứ tự. Một số đào xung quanh và tôi tìm thấy điều này article đề cập đến một cái gì đó gọi là một AggregateModuleCatalog, và nó được sử dụng để nối với nhau một loạt các danh mục. Tôi tìm thấy mã nguồn cho lớp này here. Và dưới đây là cách tôi đã sử dụng nó:

class Bootstrapper : UnityBootstrapper 
{ 
    protected override System.Windows.DependencyObject CreateShell() {...} 
    protected override void InitializeShell() {...} 

    protected override IModuleCatalog CreateModuleCatalog() 
    { 
     return new AggregateModuleCatalog(); 
    } 

    protected override void ConfigureModuleCatalog() 
    { 
     ((AggregateModuleCatalog)ModuleCatalog).AddCatalog(new DirectoryModuleCatalog { ModulePath = "Modules.Services" }); 
     ((AggregateModuleCatalog)ModuleCatalog).AddCatalog(new DirectoryModuleCatalog { ModulePath = "Modules.Views" }); 
    } 
} 

Và AggregateModuleCatalog:

public class AggregateModuleCatalog : IModuleCatalog 
{ 
    private List<IModuleCatalog> catalogs = new List<IModuleCatalog>(); 

    /// <summary> 
    /// Initializes a new instance of the <see cref="AggregateModuleCatalog"/> class. 
    /// </summary> 
    public AggregateModuleCatalog() 
    { 
     this.catalogs.Add(new ModuleCatalog()); 
    } 

    /// <summary> 
    /// Gets the collection of catalogs. 
    /// </summary> 
    /// <value>A read-only collection of catalogs.</value> 
    public ReadOnlyCollection<IModuleCatalog> Catalogs 
    { 
     get 
     { 
      return this.catalogs.AsReadOnly(); 
     } 
    } 

    /// <summary> 
    /// Adds the catalog to the list of catalogs 
    /// </summary> 
    /// <param name="catalog">The catalog to add.</param> 
    public void AddCatalog(IModuleCatalog catalog) 
    { 
     if (catalog == null) 
     { 
      throw new ArgumentNullException("catalog"); 
     } 

     this.catalogs.Add(catalog); 
    } 

    /// <summary> 
    /// Gets all the <see cref="ModuleInfo"/> classes that are in the <see cref="ModuleCatalog"/>. 
    /// </summary> 
    /// <value></value> 
    public IEnumerable<ModuleInfo> Modules 
    { 
     get 
     { 
      return this.Catalogs.SelectMany(x => x.Modules); 
     } 
    } 

    /// <summary> 
    /// Return the list of <see cref="ModuleInfo"/>s that <paramref name="moduleInfo"/> depends on. 
    /// </summary> 
    /// <param name="moduleInfo">The <see cref="ModuleInfo"/> to get the</param> 
    /// <returns> 
    /// An enumeration of <see cref="ModuleInfo"/> that <paramref name="moduleInfo"/> depends on. 
    /// </returns> 
    public IEnumerable<ModuleInfo> GetDependentModules(ModuleInfo moduleInfo) 
    { 
     var catalog = this.catalogs.Single(x => x.Modules.Contains(moduleInfo)); 
     return catalog.GetDependentModules(moduleInfo); 
    } 

    /// <summary> 
    /// Returns the collection of <see cref="ModuleInfo"/>s that contain both the <see cref="ModuleInfo"/>s in 
    /// <paramref name="modules"/>, but also all the modules they depend on. 
    /// </summary> 
    /// <param name="modules">The modules to get the dependencies for.</param> 
    /// <returns> 
    /// A collection of <see cref="ModuleInfo"/> that contains both all <see cref="ModuleInfo"/>s in <paramref name="modules"/> 
    /// and also all the <see cref="ModuleInfo"/> they depend on. 
    /// </returns> 
    public IEnumerable<ModuleInfo> CompleteListWithDependencies(IEnumerable<ModuleInfo> modules) 
    { 
     var modulesGroupedByCatalog = modules.GroupBy<ModuleInfo, IModuleCatalog>(module => this.catalogs.Single(catalog => catalog.Modules.Contains(module))); 
     return modulesGroupedByCatalog.SelectMany(x => x.Key.CompleteListWithDependencies(x)); 
    } 

    /// <summary> 
    /// Initializes the catalog, which may load and validate the modules. 
    /// </summary> 
    public void Initialize() 
    { 
     foreach (var catalog in this.Catalogs) 
     { 
      catalog.Initialize(); 
     } 
    } 

    /// <summary> 
    /// Adds a <see cref="ModuleInfo"/> to the <see cref="ModuleCatalog"/>. 
    /// </summary> 
    /// <param name="moduleInfo">The <see cref="ModuleInfo"/> to add.</param> 
    public void AddModule(ModuleInfo moduleInfo) 
    { 
     this.catalogs[0].AddModule(moduleInfo); 
    } 
} 

Tôi cũng nên đề cập đến rằng bài báo khẳng định như sau:

Để chứng minh nhiều cách sử dụng ModuleCatalog , QuickStart sử dụng Unity triển khai AggregateModuleCatalog rằng có nguồn gốc từ IModuleCatalog. Lớp này không được sử dụng trong một ứng dụng giao hàng.

Tại sao tôi không chắc chắn. Rất thích nghe bất kỳ lời giải thích nào về lý do tại sao điều đó có thể xảy ra.

Các vấn đề liên quan