每个公司为加快项目的开发效率,都会使用一些成熟的框架或组件,然后在此基础之上进行开发。YbSoftwareFactory也可复用这些框架和组件,通过二次开发来开发一个代码生成插件,把大部分和框架有关的公共代码和组件封装打包压缩到项目文件中,而把和具体业务管理有关的需要变化的代码使用T4模板来生成。然后使用该代码生成插件来快速生成一个适合自身开发习惯的可立即运行的解决方案来提高开发进度,然后在此基础之上进行后续代码的二次开发。
曾经的某公司用户在使用了YbSoftwareFactory来进行首个项目的辅助开发,原本计划28天的项目编码任务仅仅只用了3天,这3天的工作量也就是进行个性化的界面呈现调整和统计报表的制作,因为代码生成插件所生成的解决方案和代码已经集成了身份认证、权限管理、验证规则、日志输出等公共功能以及实体的增、删、查、改和导出个性化功能,工作效率的提高程度由此可见。
前一篇文章已经介绍了怎样实现 IPlugInGroupRepository 接口来完成插件组的开发,本章则介绍插件二次开发的最后一项任务,即插件组内各个插件怎样实现 IPlugInRepository 接口来完成插件的二次开发。
IPlugInRepository的实现类主要的职责就是完成代码的生成。 首先来看看IPlugInRepository的接口定义:

using System.Collections.Generic;
using System.Linq;
using System.Text;
using Yb.PlugIn.Base;
namespace Yb.PlugIn.Base
{
public interface IPlugInRepository
{
/// <summary>
/// 生成进度通知有关的事件
/// </summary>
event EventHandler<NotifyChangedEventArgs> NotifyChanged;
/// // <summary>
/// // 代码生成插件组,包含相关的一组插件,每个插件可生成不同类型的代码
/// // </summary>
// IPlugInGroupRepository GroupRepository { get; set; }
/// <summary>
/// 获取模板信息
/// </summary>
/// <returns></returns>
IEnumerable<TemplateInfo> GetTemplateInfos();
/// <param name="arg"> /// <summary>
/// 生成代码前的操作,如创建目录,解压文件等
/// </summary>
/// <param name="arg"> 1、解压后的路径 </param></param>
void BeforeBuild( object arg);
/// <summary>
/// 生成代码
/// </summary>
/// <param name="arg"> 数据源 </param>
/// <param name="templateInfos"> The template infos. </param>
/// <returns></returns>
IEnumerable<TemplateInfo> BuildCodes( object arg, IEnumerable<TemplateInfo> templateInfos);
/// <summary>
/// 输出代码,如写文件或者显示等
/// </summary>
/// <param name="outputMode"> 输出模式,如输出文件则会依次执行 BeforeBuild,BuildCodes,AfterBuild </param>
/// <param name="beforeBuildArg"> 开始生成代码前事件 </param>
/// <param name="argument"> 数据源 </param>
/// <param name="templateInfos"> The template infos. </param>
/// <param name="afterBuildArg"> 开始生成代码后参数 </param>
/// <returns></returns>
IEnumerable<TemplateInfo> BuildCodes( object beforeBuildArg, object argument, IEnumerable<TemplateInfo> templateInfos, object afterBuildArg);
/// <summary>
/// 代码生成后的任务,如运行数据库初始化脚本等
/// </summary>
/// <param name="arg"> 1、连接字符串 </param>
void AfterBuild( object arg);
}
}
下面将通过一个Demo来介绍接口下各个方法和属性的含义。
一、元数据定义

Description = " 生成Castel数据访问层,DevExpress界面层代码的解决方案 ",
DisplayOrder = 1,
ImageUri = " pack://application:,,,/Yb.PlugIn.CastleDevExpress.WebForm;component/Images/WebFormSolution.png ",
LargeImageUri = " pack://application:,,,/Yb.PlugIn.CastleDevExpress.WebForm;component/Images/WebFormSolution.png ",
CodeOutputMode = CodeOutputMode.File)]
[Export(StaticResources.PlugInGroupKey, typeof(IPlugInRepository))]
public class CastleDxWebSolutionPlugInRepository : CastleDxWebPlugInRepositoryBase
Name:将在Ribbion控件中显示的本插件的名称
DisplayOrder:显示顺序
ImageUri和LargeImageUri:将显示的个性化图标的URI路径,可参考上篇文章。
CodeOutputMode:代码生成模式,File和Preview可选,File模式将生成代码文件,通常用于完整的解决方案的生成。Preview将显示预览的代码窗口,通常用于实体层、数据访问层、界面层等某一类的代码生成,生成的代码可预览、编辑并输出到文件。
二、BeforeBuild方法
本方法主要在代码生成前执行,主要可用于解压项目文件到指定路径,如你无这方面的需求可不用重写,传入的 arg 参数为文件路径。以下是某个具体的实现代码:

/// 解压文件到指定路径
/// </summary>
/// <param name="arg"> 解压路径 </param>
public override void BeforeBuild( object arg)
{
if (arg == null)
{
throw new ArgumentNullException( " arg ", " 参数不能为空 ");
}
var path = arg.ToString();
if ( string.IsNullOrWhiteSpace(path))
{
throw new ArgumentNullException( " arg ", " 解压路径不能为空 ");
}
OnNotifyChanged( new NotifyChangedEventArgs(NotifyType.Infomation, " 正在解压文件,请稍候 "));
// 解压文件到指定路径
FileUtility.ExtractFileTo(ZipFileRelativePath,path);
OnNotifyChanged( new NotifyChangedEventArgs(NotifyType.Infomation, " 解压文件结束 "));
}
三、GetTemplateInfos 方法
本方法返回本插件代码生成所需的模板信息记录列表,这些模板对象将供 YbSoftwareFactory 处理,代码示例如下:

/// 获取本插件所需的模板信息
/// </summary>
/// <returns></returns>
public override IEnumerable<TemplateInfo> GetTemplateInfos()
{
var templateInfo = base.GetTemplateInfos();
return templateInfo.Where(c => c.BuildEnable && (c.Tag & StaticResources.SolutionPlugInTemplateTag) > 0);
}
四、BuildCodes 方法
本方法为代码生成的核心代码,其中arg参数为一个表信息记录列表,templateInfos为模板信息,生成后的代码将保存到模板信息的“ExportContent”字段并返回。

/// 生成代码
/// </summary>
/// <param name="arg"></param>
/// <param name="templateInfos"></param>
/// <returns></returns>
public override IEnumerable<TemplateInfo> BuildCodes( object arg, IEnumerable<TemplateInfo> templateInfos)
{
// 参数类型转换
var tableInfos = BaseInfoUtility.ObjectToTableInfoList(arg).Where(c=>c.IsSelected).ToList();
// 生成结果
var templateList = new List<TemplateInfo>();
_totalCount = tableInfos.Count() * (templateInfos.Count() - 3) + 3;
_currentIndex = 0;
foreach ( var templateInfo in templateInfos)
{
if(!templateInfo.BuildEnable) continue;
// 读模板信息
templateInfo.TemplateContent = FileUtility.ReadFile(templateInfo.TemplateRelativePath);
// 判断模板类型,是否每张表都需生成一个模板
if ((templateInfo.Tag & StaticResources.IsTableInfoListOfTemplateArg) == 0)
{
foreach ( var tableInfo in tableInfos)
{
// 复制模板,防止生成后下一个循环被覆盖
var currentTemplateInfo = (TemplateInfo)templateInfo.Clone();
// 生成代码
BuildTableInfoCode( " CurrentTable ", templateList, tableInfo, currentTemplateInfo);
}
}
else
{
// 生成 Web.Config 文件
if (templateInfo.ExportFileNameFormatString.Equals( " Web.config ",StringComparison.OrdinalIgnoreCase))
{
// 创建Web.Config代码
BuildWebConfigCode(templateList, templateInfo, tableInfos);
}
else
{
// 创建如项目文件等需要传入“表对象集合”为参数的模板代码
BuildTableInfosCode(templateList, templateInfo, tableInfos);
}
}
}
return templateList;
}
五、AfterBuild方法
AfterBuild方法主要在代码生成后进行一些后续处理操作,你可利用该方法进行一些任务,如安装数据库初始化脚本等,示例代码如下:

/// 安装和权限有关的数据库脚本
/// </summary>
/// <param name="arg"></param>
public override void AfterBuild( object arg)
{
if (arg == null || string.IsNullOrWhiteSpace(arg.ToString())) return;
var connStr = arg.ToString();
OnNotifyChanged( new NotifyChangedEventArgs(NotifyType.Infomation, " 正在安装数据库脚本 "));
try
{
// 执行创建表的脚本文件
ExecuteSqlServerDbScript(connStr);
// 进度通知
OnNotifyChanged( new NotifyChangedEventArgs(NotifyType.Infomation, " 数据库脚本处理完成 "));
}
catch (Exception er)
{
// 进度通知
OnNotifyChanged( new NotifyChangedEventArgs(NotifyType.Infomation, string.Format( " 数据库脚本安装失败,{0} ",er.Message)));
}
}
六、OnNotifyChanged方法
该方法主要进行事件通知,在界面上显示滚动条等。
附下载地址:
“Yb.PlugIn.CastleDevExpress.WebForm”插件源码下载
附“Yb.PlugIn.CastleDevExpress.WebForm”代码生成插件的Demo数据库下载