.Net插件框架的实现及分析(二)

时间:2010年09月25日作者:Winson查看次数:查阅:695评论次数:5

话接上回,这是 .Net插件框架的实现及分析(一)的续篇,让我们来继续分析下这个插件框架如何实现吧。

既然是插件,就必须得动态加载,只需将编译好的插件DLL文件放到指定的插件目录下就可以使用了,这样就有一个动态获取插件的过程,我们此例中为文章内容格式化插件,当然就不只一个格式化插件在同一时间里使用了,所以需先创建的一个集合来收集这些插件:

ProviderCollector.cs 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CoderBlog.Core
{
    /// <summary>
    /// 实现一个泛型插件 Provider 集合
    /// </summary>
    /// <typeparam name="T">集合类型.</typeparam>
    public class ProviderCollector<T>
    {
        private List<T> list;

        /// <summary>
        /// 初始化新的实体
        /// </summary>
        public ProviderCollector()
        {
            list = new List<T>(3);
        }

        /// <summary>
        /// 添加一个 Provider 到集合
        /// </summary>
        /// <param name="provider">需添加的 Provider.</param>
        public void AddProvider(T provider)
        {
            lock (this)
            {
                list.Add(provider);
            }
        }

        /// <summary>
        /// 从集合里移除一个 Provider
        /// </summary>
        /// <param name="provider">需移除的 Provider</param>
        public void RemoveProvider(T provider)
        {
            lock (this)
            {
                list.Remove(provider);
            }
        }

        /// <summary>
        /// 获取所有的 Providers (返回数组).
        /// </summary>
        public T[] AllProviders
        {
            get
            {
                lock (this)
                {
                    return list.ToArray();
                }
            }
        }

        /// <summary>
        /// 获取一个 Provider, 按类型名称搜索
        /// </summary>
        /// <param name="typeName">类型名称</param>
        /// <returns>所找到的 Provider</returns>
        public T GetProvider(string typeName)
        {
            lock (this)
            {
                for (int i = 0; i < list.Count; i++)
                {
                    if (list[i].GetType().FullName.Equals(typeName)) return list[i];
                }
                return default(T);
            }
        }
    }
}

然后还要创建一个管理插件 Provider 集合实例的类:
Collectors.cs 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CoderBlog.PluginFramework;

namespace CoderBlog.Core
{
    /// <summary>
    /// 包含了 Providers 集合的实例
    /// </summary>
    public static class Collectors
    {
        /// <summary>
        /// Contains the file names of the DLLs containing each provider (provider->file).
        /// </summary>
        public static Dictionary<string, string> FileNames;

        /// <summary>
        /// 使用中的格式化插件的 Provider 集合
        /// </summary>
        public static ProviderCollector<IFormatterProvider> FormatterProviderCollector;

        /// <summary>
        /// 禁用中的格式化插件的 Provider 集合
        /// </summary>
        public static ProviderCollector<IFormatterProvider> DisabledFormatterProviderCollector;

        /// <summary>
        /// 查找一个 provider.
        /// </summary>
        /// <param name="typeName">Provider 类型名称</param>
        /// <param name="enabled">指定此 Provider 是否开启</param>
        public static IProvider FindProvider(string typeName, out bool enabled)
        {
            enabled = true;
            IProvider prov = null;

            prov = FormatterProviderCollector.GetProvider(typeName);
            if (prov == null)
            {
                prov = DisabledFormatterProviderCollector.GetProvider(typeName);
                if (prov != null) enabled = false;
            }
            if (prov != null) return prov;

            return null;
        }

        /// <summary>
        /// 尝试卸载一个 provider.
        /// </summary>
        /// <param name="typeName">provider 名称</param>
        public static void TryUnload(string typeName)
        {
            bool enabled;
            IProvider prov = FindProvider(typeName, out enabled);

            FormatterProviderCollector.RemoveProvider(prov as IFormatterProvider);
            DisabledFormatterProviderCollector.RemoveProvider(prov as IFormatterProvider);
        }

        /// <summary>
        /// 尝试禁用一个 provider.
        /// </summary>
        /// <param name="typeName">provider 名称</param>
        public static void TryDisable(string typeName)
        {
            IProvider prov = null;

            prov = FormatterProviderCollector.GetProvider(typeName);
            if (prov != null)
            {
                DisabledFormatterProviderCollector.AddProvider((IFormatterProvider)prov);
                FormatterProviderCollector.RemoveProvider((IFormatterProvider)prov);
                return;
            }
        }

        /// <summary>
        /// 尝试开启一个 provider.
        /// </summary>
        /// <param name="typeName">provider 名称</param>
        public static void TryEnable(string typeName)
        {
            IProvider prov = null;

            prov = DisabledFormatterProviderCollector.GetProvider(typeName);
            if (prov != null)
            {
                FormatterProviderCollector.AddProvider((IFormatterProvider)prov);
                DisabledFormatterProviderCollector.RemoveProvider((IFormatterProvider)prov);
                return;
            }
        }

        /// <summary>
        /// 获取所有 providers, 包含启用和禁用的
        /// </summary>
        /// <returns>providers 的名称.</returns>
        public static string[] GetAllProviders()
        {
            List<string> result = new List<string>(20);

            foreach (IProvider prov in FormatterProviderCollector.AllProviders) result.Add(prov.GetType().FullName);
            foreach (IProvider prov in DisabledFormatterProviderCollector.AllProviders) result.Add(prov.GetType().FullName);

            return result.ToArray();
        }
    }
}
 

接下来就可以开始正式编写加载插件 Provider 的代码了,创建一个 ProviderLoader 类:

ProviderLoader.cs 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using CoderBlog.PluginFramework;
using System.Reflection;

namespace CoderBlog.Core
{
    /// <summary>
    /// 加载插件的 Provider
    /// </summary>
    public static class ProviderLoader
    {
        /// <summary>
        /// 列出所有插件的程序集
        /// </summary>
        /// <returns></returns>
        public static string[] ListPluginAssemblies()
        {
            string pluginPath = System.Web.HttpRuntime.AppDomainAppPath + "plugins";
            string[] files = Directory.GetFiles(pluginPath, "*.dll");
            string[] result = new string[files.Length];
            for (int i = 0; i < files.Length; i++) result[i] = Path.GetFileName(files[i]);
            return result;

        }

        /// <summary>
        /// 加载插件
        /// </summary>
        public static void Load()
        {
            string[] pluginAssemblies = ListPluginAssemblies();

            //分别创建2个对象以存放开始和禁用的插件
            List<IFormatterProvider> forms = new List<IFormatterProvider>();
            List<IFormatterProvider> dForms = new List<IFormatterProvider>();

            for (int i = 0; i < pluginAssemblies.Length; i++)
            {
                IFormatterProvider[] f;
                LoadFrom(pluginAssemblies[i], out f);
                forms.AddRange(f);

            }

            for (int i = 0; i < forms.Count; i++)
            {
                //加载并初始化插件
                Initialize<IFormatterProvider>(forms[i],
                    Collectors.FormatterProviderCollector,
                    Collectors.DisabledFormatterProviderCollector);
            }
        }

        /// <summary>
        /// 初始化插件 Provider
        /// </summary>
        /// <typeparam name="T">当前插件接口类型</typeparam>
        /// <param name="instance">插件实体</param>
        /// <param name="collectorEnabled">开启中的插件集合</param>
        /// <param name="collectorDisabled">禁用中的插件集合</param>
        private static void Initialize<T>(T instance, ProviderCollector<T> collectorEnabled,
            ProviderCollector<T> collectorDisabled) where T : class, IProvider
        {
            if (collectorEnabled.GetProvider(instance.GetType().FullName) != null ||
                collectorDisabled.GetProvider(instance.GetType().FullName) != null)
            {
                //如果为空,则直接返回
                return;
            }

            bool enabled = !IsDisabled(instance.GetType().FullName);
            try
            {
                if (enabled)
                {
                    //只对启用的插件进行初始化操作,并设置插件所需的配置文件,
                    //此方法最终由具体的插件类去实现
                    //以下只为演示说明,所以直接指定了配置文件路径和名称了,
                    //正常情况下应该再写一个 Provider 去动态获取配置文件具体路径的
                    instance.Init(Host.Instance, "~/plugins/config.ini");
                }
            }
            catch
            {
                // 禁用插件 Provider
                enabled = false;
                //保存插件状态,本例为将状态保存到文本文件中
                //具体实现在此也不写出来了,否则就要连带着另一个配置类 provider,太多了,呵
                //SaveStatus(instance.GetType().FullName, false);
                throw; // 再次抛出异常,因为不是正常执行中
            }
            if (enabled) collectorEnabled.AddProvider(instance);
            else collectorDisabled.AddProvider(instance);

        }

        /// <summary>
        /// 动态加载自程序集的插件
        /// </summary>
        /// <param name="assembly">程序集名称</param>
        /// <param name="formatters">格式化插件数组,可同时加载多个</param>
        public static void LoadFrom(string assembly, out IFormatterProvider[] formatters)
        {
            Assembly asm = null;
            try
            {
                //asm = Assembly.LoadFile(assembly);
                // 使用此方法可让DLL不会被锁住,加载完后还可以被删除
                asm = Assembly.Load(LoadAssemblyFromProvider(Path.GetFileName(assembly)));
            }
            catch
            {
                formatters = new IFormatterProvider[0];
                return;
            }

            Type[] types = null;

            try
            {
                types = asm.GetTypes();
            }
            catch (ReflectionTypeLoadException)
            {
                formatters = new IFormatterProvider[0];
                return;
            }

            List<IFormatterProvider> frs = new List<IFormatterProvider>();

            Type[] interfaces;
            for (int i = 0; i < types.Length; i++)
            {
                // 避免加载到抽象类,它们不能被实例化
                if (types[i].IsAbstract) continue;

                interfaces = types[i].GetInterfaces();
                foreach (Type iface in interfaces)
                {
                    //这里也可以添加其他插件的 proivder,加多几个 if 判断即可
                    if (iface == typeof(IFormatterProvider))
                    {
                        IFormatterProvider tmpf = CreateInstance<IFormatterProvider>(asm, types[i]);
                        if (tmpf != null)
                        {
                            frs.Add(tmpf);
                            Collectors.FileNames[tmpf.GetType().FullName] = assembly;
                        }
                    }
                }
            }
            formatters = frs.ToArray();
        }

        /// <summary>
        /// 创建一个提供者的实例
        /// </summary>
        /// <typeparam name="T">提供者接口类型</typeparam>
        /// <param name="asm">包含指定类型的程序集</param>
        /// <param name="type">要创建的实例类型</param>
        /// <returns>创建的实例,或者 <c>null</c>.</returns>
        private static T CreateInstance<T>(Assembly asm, Type type) where T : class, IProvider
        {
            T instance;
            try
            {
                instance = asm.CreateInstance(type.ToString()) as T;
                return instance;
            }
            catch
            {
                throw;
            }
        }

        /// <summary>
        /// 检测插件是否被禁用
        /// </summary>
        /// <param name="typeName">类型名称</param>
        /// <returns></returns>
        public static bool IsDisabled(string typeName)
        {
            //具体逻辑请自行实现啦,可从配置类里获取插件状态
            return true;
        }

        /// <summary>
        /// 从程序集加载提供者
        /// </summary>
        /// <param name="assemblyName">程序集文件名</param>
        /// <returns></returns>
        private static byte[] LoadAssemblyFromProvider(string filename)
        {
            if (filename == null) throw new ArgumentNullException("filename");
            if (filename.Length == 0) throw new ArgumentException("filename");

            if (!File.Exists(GetPluginFullPath(filename))) return null;

            //此函数代码其实应该放到配置类实现,然后使用lock以保证线程安全,
            //在此只为了演示,所以先放到此片了
            //lock (this)
            //{
                try
                {
                    return File.ReadAllBytes(GetPluginFullPath(filename));
                }
                catch (IOException)
                {
                    return null;
                }
            //}
        }

        /// <summary>
        /// 根据文件名获取完整的插件路径
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        private static string GetPluginFullPath(string filename)
        {
            //具体请自行实现,同理,此函数也应该放到配置类
            return "";
        }
    }
}
 

OK,到此整个插件框架已基本完成了。下一篇将说说如何使用此框架来创建一个简单的插件 :biggrin:

标签:,,分类:.NET
5条评论
  1. only博客留言于:2010年10月07日13:49 回复

    代码的确加载很快~

  2. 久久IT博客留言于:2010年09月27日09:44 回复

    很久没来了,来看看,技术强人,佩服“`

    • Winson留言于:2010年09月27日09:50 回复

      谢谢支持 :cheerful:

  3. 德意留言于:2010年09月25日22:34 回复

    纯技术支持一下。

    • Winson留言于:2010年09月25日22:39 回复

      呵,谢谢支持 :)

发表评论

*

*

5 + 6 = ?

:alien: :angel: :angry: :blink: :blush: :cheerful: :cool: :cwy: :devil: :dizzy: :ermm: :face: :getlost: :biggrin: :happy: :heart: :kissing: :lol: :ninja: :pinch: :pouty: :sad: :shocked: :sick: :sideways: :silly: :sleeping: :smile: :tongue: :unsure: :w00t: :wink: :wassat: :wub: :whistle:

无觅相关文章插件,快速提升流量