在了解插件化编程之前,要先想清楚MVC项目的运行原理及CLR查找和加载程序集的方式
1).MVC项目的运行原理
2).CLR查找和加载程序集的方式
通过上述文章,可以了解到,程序运行初CRL会将所有的可以加载的程序集进行加载,之后将扫描程序集内所有的Controller并添加到RouteTable中,到此我们的插件程序集内的控制器将可以被实例化, 但还无法找到我们控制器对应的视图
1.先将框架的插件资源包拆解,分别复制到“2”中的程序集目录及“3”中的Razor视图页目录:
//作用:项目启动最先执行当前函数的“Initialize”方法
[assembly: PreApplicationStartMethod(typeof(BKYL.Web.Main.PreApplicationInit), "Initialize")]
namespace BKYL.Web.Main
{
public class PreApplicationInit
{
static PreApplicationInit()
{
PluginFolder = new DirectoryInfo(HostingEnvironment.MapPath("~/Plugins/Plugins"));
ShadowCopyFolder = new DirectoryInfo(HostingEnvironment.MapPath("~/Plugins/PluginsDlls"));
ViewsFolder = HostingEnvironment.MapPath("~/Plugins/PluginsViews");
ContentFolder = HostingEnvironment.MapPath("~/Content");
}
/// <summary>
/// 插件所在目录信息
/// </summary>
private static readonly DirectoryInfo PluginFolder;
/// <summary>
/// 程序运行时指定的dll目录
/// </summary>
private static readonly DirectoryInfo ShadowCopyFolder;
/// <summary>
/// 视图所在文件夹
/// </summary>
private static readonly string ViewsFolder;
/// <summary>
/// content
/// </summary>
private static readonly string ContentFolder;
public static void Initialize()
{
try
{
Directory.CreateDirectory(ShadowCopyFolder.FullName);
//清空插件dll运行目录中的文件
//foreach (var f in ShadowCopyFolder.GetFiles("*.dll", SearchOption.AllDirectories))
//{
// f.Delete();
//}
foreach (var item in PluginFolder.GetDirectories())
{
foreach (var item1 in item.GetDirectories())
{
CopyDirectory(item1.FullName, Path.Combine(ShadowCopyFolder.FullName, item1.Name), true);
}
}
foreach (var plug in PluginFolder.GetFiles("*.dll", SearchOption.AllDirectories).Where(i => i.Directory.Parent.Name == "Plugins"))
{
var path = Path.Combine(ShadowCopyFolder.FullName, plug.Name);
if (File.Exists(path))
{
var time = File.GetLastWriteTime(path);
if (plug.LastWriteTime != time)
{
File.Copy(plug.FullName, path, true);
}
}
else
{
File.Copy(plug.FullName, path, true);
}
}
var assemblys = ShadowCopyFolder.GetFiles("*.dll", SearchOption.AllDirectories).Select(x => AssemblyName.GetAssemblyName(x.FullName)).Select(x => Assembly.Load(x.FullName));
foreach (var a in assemblys)
{
var dllName = a.ManifestModule.Name.Replace(".dll", "");
if (PluginRegistryCache.PluginRegistryDic == null)
{
PluginRegistryCache.Load();
}
if (!PluginRegistryCache.PluginRegistryDic.ContainsKey(dllName.ToLower()))
{
continue;
}
var startUpClassName = PluginRegistryCache.PluginRegistryDic[dllName.ToLower()];
var fileNames = a.GetManifestResourceNames();
// var ms= a.GetModules();
string pluginsName = ViewsFolder + "/" + dllName;
string contentName = ContentFolder + "/" + dllName;
//创建plugin文件夹
if (!Directory.Exists(pluginsName))
{
Directory.CreateDirectory(pluginsName);
}
//创建content文件夹
if (!Directory.Exists(contentName))
{
Directory.CreateDirectory(contentName);
}
for (int i = 0; i < fileNames.Length; i++)
{
var fileStream = a.GetManifestResourceStream(fileNames[i]);
if (fileNames[i].Contains(".css") || fileNames[i].Contains(".js") ||
fileNames[i].Contains(".png") || fileNames[i].Contains(".jpg") ||
fileNames[i].Contains(".gif"))
{
var f = fileNames[i].Replace(dllName + ".", "");
FileCopy(f, contentName, fileStream);
}
if (fileNames[i].Contains(".cshtml"))
{
var f = fileNames[i].Replace(dllName + ".", "");
FileCopy(f, pluginsName, fileStream);
}
}
//加载dll启动方法
if (!string.IsNullOrWhiteSpace(startUpClassName))
{
IStartUp start = (IStartUp)a.CreateInstance(dllName + "." + startUpClassName);
if (start != null)
{
start.Load();
}
}
BuildManager.AddReferencedAssembly(a);
}
}
catch (Exception ex)
{
Log.WriteLine("插件程序文件复制出现异常," + ex.Message);
throw;
}
}
//拷贝文件
private static bool CopyDirectory(string SourcePath, string DestinationPath, bool overwriteexisting)
{
bool ret = false;
try
{
SourcePath = SourcePath.EndsWith(@"\") ? SourcePath : SourcePath + @"\";
DestinationPath = DestinationPath.EndsWith(@"\") ? DestinationPath : DestinationPath + @"\";
if (Directory.Exists(SourcePath))
{
if (Directory.Exists(DestinationPath) == false)
Directory.CreateDirectory(DestinationPath);
foreach (string fls in Directory.GetFiles(SourcePath))
{
FileInfo flinfo = new FileInfo(fls);
flinfo.CopyTo(DestinationPath + flinfo.Name, overwriteexisting);
}
foreach (string drs in Directory.GetDirectories(SourcePath))
{
DirectoryInfo drinfo = new DirectoryInfo(drs);
if (CopyDirectory(drs, DestinationPath + drinfo.Name, overwriteexisting) == false)
ret = false;
}
}
ret = true;
}
catch (Exception ex)
{
ret = false;
}
return ret;
}
private static void FileCopy(string f, string contentName, Stream fileStream)
{
var file = f.Split('.');
for (int j = 1; j < file.Length - 2; j++)
{
contentName = contentName + "/" + file[j];
if (!Directory.Exists(contentName))
{
Directory.CreateDirectory(contentName);
}
}
var vPath = contentName + "/" + file[file.Length - 2] + "." + file[file.Length - 1];
if (File.Exists(vPath))
{
File.Delete(vPath);
}
FileStream fs = new FileStream(vPath, FileMode.OpenOrCreate);
fileStream.Position = 0;
fileStream.CopyTo(fs);
fileStream.Close();
fs.Close();
}
}
}
2.配置插件Dll文件位置:
<!--程序集绑定标签-->
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="Plugins/PluginsDlls/" />
</assemblyBinding >
3.自定义Razor视图引擎:
public class CustomRazor : RazorViewEngine
{
/// <summary>
/// 定义视图页所在地址。
/// </summary>
private string[] _viewLocationFormats = new[]
{
// "~/Views/Parts/{0}.cshtml",
"~/Plugins/PluginsViews/{pluginFolder}/{1}/{0}.cshtml",
"~/Plugins/PluginsViews/{pluginFolder}/Shared/{0}.cshtml",
"~/Views/{1}/{0}.cshtml",
"~/Views/Shared/{0}.cshtml",
};
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
try
{
Log.WriteLine("开始加载视图", "info");
string ns = controllerContext.Controller.GetType().Namespace;
string controller = controllerContext.Controller.GetType().Name.Replace("Controller", "");
//说明是插件中的控制器,View目录需要单独处理
var nameplace = ns.ToLower().Replace(".controllers", "");
Log.WriteLine(nameplace + "插件视图","info");
if (PluginRegistryCache.PluginRegistryDic.ContainsKey(nameplace))
{
Log.WriteLine(nameplace + "插件视图","info");
var pluginsFolder = ns.Replace(".Controllers", "");
ViewLocationFormats = ReplacePlaceholder(pluginsFolder);
}
}
catch (Exception e)
{
Log.WriteDebug(e.Message + "插件视图");
}
return base.FindView(controllerContext, viewName, masterName, useCache);
}
/// <summary>
/// 替换pluginFolder占位符
/// </summary>
/// <param name="folderName"></param>
private string[] ReplacePlaceholder(string folderName)
{
string[] tempArray = new string[_viewLocationFormats.Length];
if (_viewLocationFormats != null)
{
for (int i = 0; i < _viewLocationFormats.Length; i++)
{
tempArray[i] = _viewLocationFormats[i].Replace("{pluginFolder}", folderName);
}
}
return tempArray;
}
}
4.在“Global.asax”文件中替换视图引擎:
//加载自定义视图引擎
ViewEngines.Engines.Clear();
var myEngine = new CustomRazor();
ViewEngines.Engines.Add(myEngine);
网友评论