分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow
也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!
本文主要探讨了一种基于ASP.NET MVC3 Razor的模块化(Plugin)/插件(plugin)式架构的实现方法。本文借鉴了《Compile your asp.net mvc Razor views into a seperate dll》作者提供的方法。敬请注意。其实ASP.NET MVC的模块化(Plugin)/插件(plugin)式架构讨论的很多,但基于Razor视图引擎的很少(如:MVC2插件架构例子都是基于WebForm的,MVCContribPortable Areas也是,还有这个Plugin架构)。要么就是非常复杂非常重量级的框架,例如Orchard CMS的模块化做的很好,可惜太重量级了,也没独立的模块可以剥离出来。所以我们追寻的是简单的基于ASP.NET MVC3 Razor的模块化(Plugin)/插件(plugin)式架构的实现方法。本文最后实现的项目结构如下图:(插件都放到~/Plugin目录下,按功能划分模块,每个模块都有M,V,C)
其中,业务模块(class library project)包含其所有的视图、控制器等,模型可以放在里面也可以单独放一个project。主web项目没有引用业务模块,业务模块会编译到主web项目的~/plugin目录下面(注意:不是bin目录),然后当web应用启动的时候自动加载plugin目录下面的模块。最后运行起来的效果如下图:
其中红色的区域都是plugin进去的,那个tab的标题plugin到母版页的主菜单,tab内容也来自plugin。下面说说如何实现这样的ASP.NET MVC插件式plugin架构(模块化架构)。
实现的难点在动态加载UI视图(*.cshtml, _layout.cshtml, _viewStart.cshtml)
废话少说,直入要害。基于ASP.NET MVC3 Razor的编译发生在两个层面:
- 控制器(Controller), 模型(Models),和其它所有的C#代码等有msbuild(或者VisualStudio)编译到bin目录下的程序集(assembly)
- 视图(*.aspx, *.cshtml)由ASP.NET在运行时动态编译。当一个Razor视图(*.cshtml)显示前,Razor视图引擎调用BuildManager把视图(*.cshtml)编译到动态程序集assembly,然后使用Activator.CreateInstance来实例化新编译出来的对象,最后显示出来。如果视图(*.cshtml)用到@model绑定model,那么还会自动加载bin或者GAC里面的Model。
所以如果我们要动态加载插件(plugin),用反射bin目录下的程序集(assembly)的方法很容易搞定上面的第一部分(C#代码的部分),但UI视图的部分(上面第二部分)(特别是*.cshtml, 母版_layout.cshtml, 基视图_viewStart.cshtml)就比较难搞定。而且每次报错都是一样的,那就是Controller找不到相应的视图View,基本不知所云而且根本不是要点:view ….or its master was not found or no view engine supports the searched locations. The following locations were searched: …,因此要搞定UI视图的部分(上面第二部分)(特别是*.cshtml, 母版_layout.cshtml, 基视图_viewStart.cshtml),就需要自己动手了,基本原理是:
- 重载RazorBuildProvider,用来动态编译视图
- 实现一个自定义VirtualPathProvider,从虚拟路径自定义判断读取资源(从插件中加载资源),如果要使用编译的视图就返回编译的VirtualFile
- 实现一个容器Dictionary保存已编译的视图和虚拟路径,例如path <~/views/team/index.cshtml> type <Area.Module2.Views.Team._Page_Views_Team_Index_cshtml>,或者path <~/views/_viewstart.cshtml> type <Area.Module1.Views._Page_Views__ViewStart_cshtml>
代码:自定义VirtualPathProvider,从虚拟路径自定义判断读取资源(从插件中加载资源),如果要使用编译的视图就返回编译的VirtualFile
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Reflection;
5: using System.Web.Caching;
6: using System.Web.Hosting;
7: using System.Web.WebPages;
8:
9: namespace Common.Framework
10: {
11: public class CompiledVirtualPathProvider: VirtualPathProvider
12: {
13: /// <summary>
14: /// Gets a value that indicates whether a file exists in the virtual file system.
15: /// </summary>
16: /// <returns>
17: /// true if the file exists in the virtual file system; otherwise, false.
18: /// </returns>
19: /// <param name="virtualPath">The path to the virtual file.</param>
20: public override bool FileExists(string virtualPath)
21: {
22: return
23: GetCompiledType(virtualPath) != null
24: || Previous.FileExists(virtualPath);
25: }
26:
27: public Type GetCompiledType(string virtualPath)
28: {
29: return ApplicationPartRegistry.Instance.GetCompiledType(virtualPath);
30: }
31:
32: /// <summary>
33: /// Gets a virtual file from the virtual file system.
34: /// </summary>
35: /// <returns>
36: /// A descendent of the <see cref="T:System.Web.Hosting.VirtualFile"/> class that represents a file in the virtual file system.
37: /// </returns>
38: /// <param name="virtualPath">The path to the virtual file.</param>
39: public override VirtualFile GetFile(string virtualPath)
40: {
41: if (Previous.FileExists(virtualPath))
42: {
43: return Previous.GetFile(virtualPath);
44: }
45: var compiledType = GetCompiledType(virtualPath);
46: if (compiledType != null)
47: {
48: return new CompiledVirtualFile(virtualPath, compiledType);
49: }
50: return null;
51: }
52:
53: public override System.Web.Caching.CacheDependency GetCacheDependency(string virtualPath, System.Collections.IEnumerable virtualPathDependencies, DateTime utcStart)
54: {
55: if (virtualPathDependencies == null)
56: return Previous.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
57:
58: return Previous.GetCacheDependency(virtualPath,
59: from vp in virtualPathDependencies.Cast<string>()
60: where GetCompiledType(vp) == null
61: select vp
62: , utcStart);
63: }
64:
65: }
66: }
代码:容器Dictionary保存已编译的视图和虚拟路径,例如path <~/views/team/index.cshtml> type <Area.Module2.Views.Team._Page_Views_Team_Index_cshtml>,路径注册以后,会从容器库全局搜索所有注册过的视图,也就是说即使你视图引用的_layout.cshtml和_viewStart.cshtml在其他的Class library project照样可以找到。
1: using System;
2: using System.Collections.Generic;
3: using System.Diagnostics;
4: using System.Linq;
5: using System.Reflection;
6: using System.Web;
7: using System.Web.WebPages;
8:
9: namespace Common.Framework
10: {
11: public class DictionaryBasedApplicationPartRegistry : IApplicationPartRegistry
12: {
13: private static readonly Type webPageType = typeof(WebPageRenderingBase);
14: private readonly Dictionary<string, Type> registeredPaths = new Dictionary<string, Type>();
15:
16: /// <summary>
17: ///
18: /// </summary>
19: /// <param name="virtualPath"></param>
20: /// <returns></returns>
21: public virtual Type GetCompiledType(string virtualPath)
22: {
23: if (virtualPath == null) throw new ArgumentNullException("virtualPath");
24:
25: //Debug.WriteLine(String.Format("---GetCompiledType : virtualPath <{0}>", virtualPath));
26:
27: if (virtualPath.StartsWith("/"))
28: virtualPath = VirtualPathUtility.ToAppRelative(virtualPath);
29: if (!virtualPath.StartsWith("~"))
30: virtualPath = !virtualPath.StartsWith("/") ? "~/" + virtualPath : "~" + virtualPath;
31: virtualPath = virtualPath.ToLower();
32: return registeredPaths.ContainsKey(virtualPath)
33: ? registeredPaths[virtualPath]
34: : null;
35: }
36:
37: public void Register(Assembly applicationPart)
38: {
39: ((IApplicationPartRegistry)this).Register(applicationPart, null);
40: }
41:
42: public virtual void Register(Assembly applicationPart, string rootVirtualPath)
43: {
44: //Debug.WriteLine(String.Format("---Register assembly <{0}>, path <{1}>", applicationPart.FullName, rootVirtualPath));
45:
46: foreach (var type in applicationPart.GetTypes().Where(type => type.IsSubclassOf(webPageType)))
47: {
48: //Debug.WriteLine(String.Format("-----Register type <{0}>, path <{1}>", type.FullName, rootVirtualPath));
49:
50: ((IApplicationPartRegistry)this).RegisterWebPage(type, rootVirtualPath);
51: }
52: }
53:
54: public void RegisterWebPage(Type type)
55: {
56: ((IApplicationPartRegistry)this).RegisterWebPage(type, string.Empty);
57: }
58:
59: public virtual void RegisterWebPage(Type type, string rootVirtualPath)
60: {
61: var attribute = type.GetCustomAttributes(typeof(PageVirtualPathAttribute), false).Cast<PageVirtualPathAttribute>().SingleOrDefault<PageVirtualPathAttribute>();
62: if (attribute != null)
63: {
64: var rootRelativeVirtualPath = GetRootRelativeVirtualPath(rootVirtualPath ?? "", attribute.VirtualPath);
65:
66: //Debug.WriteLine(String.Format("---Register path/type : path <{0}> type <{1}>", rootRelativeVirtualPath.ToLower(),
67: // type.FullName));
68: registeredPaths[rootRelativeVirtualPath.ToLower()] = type;
69: }
70: }
71:
72: static string GetRootRelativeVirtualPath(string rootVirtualPath, string pageVirtualPath)
73: {
74: string relativePath = pageVirtualPath;
75: if (relativePath.StartsWith("~/", StringComparison.Ordinal))
76: {
77: relativePath = relativePath.Substring(2);
78: }
79: if (!rootVirtualPath.EndsWith("/", StringComparison.OrdinalIgnoreCase))
80: {
81: rootVirtualPath = rootVirtualPath + "/";
82: }
83: relativePath = VirtualPathUtility.Combine(rootVirtualPath, relativePath);
84: if (!relativePath.StartsWith("~"))
85: {
86: return !relativePath.StartsWith("/") ? "~/" + relativePath : "~" + relativePath;
87: }
88: return relativePath;
89: }
90: }
91: }
下面的代码很关键,用PreApplicationStartMethod关键字(.NET 4.0开始支持)使得代码在Application_Start之前执行。
有关[assembly: PreApplicationStartMethod(typeof(SomeClassLib.Initializer), "Initialize")]详细信息请参考这个页面和这个页面。
1: using System.Web;
2: using System.Web.Compilation;
3: using System.Web.Hosting;
4: using Common.Framework;
5: using Common.PrecompiledViews;
6:
7: [assembly: PreApplicationStartMethod(typeof(PreApplicationStartCode), "Start")]
8:
9: namespace Common.Framework
10: {
11: public static class PreApplicationStartCode
12: {
13: private static bool _startWasCalled;
14:
15: public static void Start()
16: {
17: if (_startWasCalled)
18: {
19: return;
20: }
21: _startWasCalled = true;
22:
23: //Register virtual paths
24: HostingEnvironment.RegisterVirtualPathProvider(new CompiledVirtualPathProvider());
25:
26: //Load Plugin Folder,
27: PluginLoader.Initialize();
28: }
29: }
30: }
代码:PluginLoader,加载plugin目录里面的东东(assembly和module配置文件)
1: using System;
2: using System.Collections.Generic;
3: using System.IO;
4: using System.Linq;
5: using System.Reflection;
6: using System.Text;
7: using System.Threading;
8: using System.Web;
9: using System.Web.Compilation;
10: using System.Web.Hosting;
11: using Common.Framework;
12: using Common.PrecompiledViews;
13:
14: //[assembly: PreApplicationStartMethod(typeof(PluginLoader), "Initialize")]
15:
16: namespace Common.PrecompiledViews
17: {
18: public class PluginLoader
19: {
20: public static void Initialize(string folder = "~/Plugin")
21: {
22: LoadAssemblies(folder);
23: LoadConfig(folder);
24: }
25:
26: private static void LoadConfig(string folder, string defaultConfigName="*.config")
27: {
28: var directory = new DirectoryInfo(HostingEnvironment.MapPath(folder));
29: var configFiles = directory.GetFiles(defaultConfigName, SearchOption.AllDirectories).ToList();
30: if (configFiles.Count == 0) return;
31:
32: foreach (var configFile in configFiles.OrderBy(s => s.Name))
33: {
34: ModuleConfigContainer.Register(new ModuleConfiguration(configFile.FullName));
35: }
36: }
给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow