How to control the order of module initialization in Prism

后端 未结 7 870
暖寄归人
暖寄归人 2020-12-28 21:34

I\'m using Prism V2 with a DirectoryModuleCatalog and I need the modules to be initialized in a certain order. The desired order is specified with an attribute on each IModu

7条回答
  •  攒了一身酷
    2020-12-28 22:00

    I didn't like the idea of using ModuleDependency because this would mean that module a would not load when module b was not present, when in fact there was no dependency. Instead I created a priority attribute to decorate the module:

    /// 
    /// Allows the order of module loading to be controlled.  Where dependencies
    /// allow, module loading order will be controlled by relative values of priority
    /// 
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public sealed class PriorityAttribute : Attribute
    {
        /// 
        /// Constructor
        /// 
        /// the priority to assign
        public PriorityAttribute(int priority)
        {
            this.Priority = priority;
        }
    
        /// 
        /// Gets or sets the priority of the module.
        /// 
        /// The priority of the module.
        public int Priority { get; private set; }
    }
    

    I then decorated the modules like this:

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

    I created a new descendent of DirectoryModuleCatalog:

    /// 
    /// ModuleCatalog that respects PriorityAttribute for sorting modules
    /// 
    [SecurityPermission(SecurityAction.InheritanceDemand), SecurityPermission(SecurityAction.LinkDemand)]
    public class PrioritizedDirectoryModuleCatalog : DirectoryModuleCatalog
    {
        /// 
        /// local class to load assemblies into different appdomain which is then discarded
        /// 
        private class ModulePriorityLoader : MarshalByRefObject
        {
            /// 
            /// Get the priorities
            /// 
            /// 
            /// 
            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom")]
            public Dictionary GetPriorities(IEnumerable 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();
                var assemblies = new Dictionary();
    
                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;
            }
        }
    
        /// 
        /// 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
        /// 
        /// modules to retrieve priorities for
        /// 
        private Dictionary GetModulePriorities(IEnumerable 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);
            }
        }
    
        /// 
        /// Sort modules according to dependencies and Priority
        /// 
        /// modules to sort
        /// sorted modules
        protected override IEnumerable Sort(IEnumerable modules)
        {
            Dictionary priorities = GetModulePriorities(modules);
            //call the base sort since it resolves dependencies, then re-sort 
            var result = new List(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;
        }
    }
    

    Finally, I changed the bootstrapper to use this new catalog:

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

    I'm not sure if the stuff with assembly loading is the best way to do things, but it seems to work...

提交回复
热议问题