Enterprise Library深入解析与灵活应用(5):创建一个简易版的批处理执行器,认识Enterprise Library典型的配置方式和对象创建方式

简介:

最近负责一个框架性项目的升级,主要是从.NET Framework 3.0建议到.NET .NET Framework 3.5,开发工具也从VS2005迁移到VS2008。但是最让我头疼的是,原来Team Foundation Server 2005不能正常工作,公司暂时还没有购买VSTS 2008的打算。基于TFS 2005的Team Build功能不能使用了,导致原本通过Team Build实现的功能需要手工来做,涉及到的包括:Source Code的编译、文档的生成、VS项目类型的模板的创建、脚本的合并、安装包的生成等等。由于绝大部分的功能分为两类:文件系统的管理(目录/文件的创建、移动、拷贝和删除)和可执行文件的执行,所以我本打算写一个bat文件搞定就可以了,在操作过程中觉得可扩展性太差了,于是花了半天的时间写了一个GUI的工具。

这个工具执行一组批处理,也可以看成是一个Sequential Workflow的执行器,我把它成为Batch Job Executor。在使用Batch Job Executor过程中,通过配置可以对批处理的每个步骤、或者是Workflow的每个Activity进行自由地定义。从功能上将,这个小工具仅仅是个小玩意儿,不登大雅之堂。 不过考虑到Batch Job Executor的涉及和实现是基于Enterprise Library典型的实现方式,比如基于EL的配置和对象创建方式,对于那些希望进一步了解EL的读者,或许可以通过这个小小的例子一窥EL的设计原理。对于那些EL的设计不时很了解的读者,对于以下的内容,可能在理解上可能比较困难。最好是下载源代码,结合下面的介绍,希望会帮助了更好的理解EL。(Source Code 下载:http://files.cnblogs.com/artech/Artech.BatchJobExecutor.zip)

一、Batch Job Executor使用

使用Batch Job Executor最重要的步骤就是通过配置配处理的每一个步骤进行设置,在这里我们组成Batch的步骤成为Job Step。我们可以先来看看下面的配置示例:

<?xml version="1.0" encoding="utf-8" ?>
<
configuration>
<
configSections>
<
section name="batchJobExecutor" type="Artech.BatchJobExecutor.Configuration.BatchJobExecutorSettings,Artech.BatchJobExecutor"/>
</
configSections>
<
batchJobExecutor defaultBatchJob="Batch Job 1">
<
variables>
<
add name="RootLocation" value="E:\Other Projects\Artech.BatchJobExecutor\"/>
<
add name="OutputLocation" value="E:\Output\"/>
</
variables>
<
batchJobs>
<
add name="Batch Job 1" description="The first batch job">
<
steps>
<!--
step 1-->
<
add name="Create Temp Directory" type="Artech.BatchJobExecutor.DirectoryCreationJobStep,Artech.BatchJobExecutor"
directoryToCreate="$OutputLocation$" />
<!--
step 2-->
<
add name="Notepad" type="Artech.BatchJobExecutor.ExecutableJobStep,Artech.BatchJobExecutor" executableFile="Notepad" waitForProcessExit="false">
<
arguments>
<
add name="param1" value="E:\readme.txt"/>
</
arguments>
</
add>
<!--
step 3-->
<
add name="Copy to Output Location" type="Artech.BatchJobExecutor.DirectoryMoveJobStep,Artech.BatchJobExecutor"
source="$RootLocation$ConsoleApplication2\Bin" destination="$OutputLocation$" />
<!--
step 4-->
<
add name="Execute Command 1" type="Artech.BatchJobExecutor.ExecutableJobStep,Artech.BatchJobExecutor" executableFile="$OutputLocation$debug\ConsoleApplication1.exe">
<
arguments>
<
add name="param1" value="1st Patameter"/>
<
add name="param2" value="2nd Patameter"/>
</
arguments>
</
add>
<!--
step 5-->
<
add name="Delete Temp Directory" type="Artech.BatchJobExecutor.DirectoryDeletionJobStep,Artech.BatchJobExecutor" directoryToDelete="$OutputLocation$" />
</
steps>
</
add>
<
add name="Batch Job 2" description="2nd batch job">
<
steps>
… …
</steps>
</
add>
</
batchJobs>
</
batchJobExecutor>
</
configuration>

这个配置包含两个部分:变量的定义和Batch Job的定义。前者定义在<variables>配置节中,一个常用的变量,比如基地址,可以通过name-value的方式在这里定义。在本例中,我们定义两个变量(RootLocation和OutputLocation),对变量的引用通过$variable name$的方式实现。而后者呢,则通过<batchJobs>配置节进行定义,我们可以定义一个活着多个Batch Job,在本例中我一共定义了两个批处理:Batch Job 1和Batch Job 2。

第一个批处理由5个步骤组成,它们分别是:

  • Step 1:创建临时输出目录,路经通过变量定义
  • Step 2:通过Notepad打开一个.txt文件,文件路径为E:\readme.txt
  • Step 3:将原目录移到Step1创建了输出目录
  • Step 4:执行Step 3移到输出目录下的可执行文件,参数通过<arguments>配置节指定
  • Step 5:移出Step 1创建的临时目录

有了上面的配置,运行我们Batch Job Executor,将会得到下面的界面。两个批处理名称在下拉框中列出,对于选中的当前批处理,5个Job Step在下面的Grid中列出来。点击“Start”按钮,批处理便开始执行,下面的进度条现实当前的进度。

image

二、Batch Job Executor的设计

1、Job Step

构成一个批处理的步骤通过抽象类JobStep表示,除了定义了Name和Description属性外,定义一个抽象的Execute()方法,Job Step的所有逻辑通过该方法实现。

namespace Artech.BatchJobExecutor
{
public abstract class JobStep
{
public string Name
{ get; set; }

public string Description
{ get; set; }

public abstract void Execute();
}
}

由于大部分Job Step用于基于文件系统的操作,我创建了另一个抽象类DirectoryFileJobStep,暂时还没有想到需要定义什么具体的操作,鼓且定义创建出来,以备以后不时之需:

namespace Artech.BatchJobExecutor
{
public abstract class DirectoryFileJobStep : JobStep
{
}
}

创建了4个具体的JobStep,分别用于进行目录的创建、移动和删除,以及.exe文件的执行(ExecutableJobStep),它们的关系通过下面的类型表示。

image

2、Job Step Configuration

由于所有Job Step都需要通过配置进行设置,所以配置的定义显得尤为重要。在这里我们采用Enterprise Library的Xxx-XxxData-XxxAssembler的结构(比如Exception Handler的定义就采用这样的结构)。其中Xxx代表具体使用某种功能的类型(比如WrapHandler),XxxData(比如WrapHandlerData)表示Xxx对应的配置,而XxxAssembler(WrapHandlerAssembler)则实现通过XxxData对Xxx的创建。

我们Job Step的结构大体也由上面3个部分构成,我们以ExecutableJobStep为例,它的结构大体可以通过下面的类图表示:

image

先来看看ExecutableJobStep的定义(只列出重要部分)。三个字段分别表示可执行文件的路径、参数和是否需要等待进程结束才能开始下一步骤。Execute()中通过开启进程的方式执行可执行文件。

namespace Artech.BatchJobExecutor
{
[ConfigurationElementType(typeof(ExecutableJobStepData))]
public class ExecutableJobStep : JobStep
{
private string _executableFile;
private string _arguments;
private bool _waitForProcessExit;

… …

public ExecutableJobStep(string executableFile, string arguments, bool waitForProcessExit)
{
if (string.IsNullOrEmpty(executableFile))
{
throw new ArgumentNullException("executableFile");
}

this._executableFile = executableFile;
this._arguments = arguments;
this._waitForProcessExit = waitForProcessExit;
}

public override void Execute()
{
Process process = null;
if (string.IsNullOrEmpty(this.Arguments))
{
process = Process.Start(this.ExecutableFile);
}
else
{
process = Process.Start(this.ExecutableFile, this.Arguments);
}

if (this._waitForProcessExit)
{
process.WaitForExit();
}
}
}
}

需要特别注意的是在ExecutableJobStep 上,通过ConfigurationElementTypeAttribute指定了与之相匹配的配置类型(ExecutableJobStepData)。ExecutableJobStep 的三个属性(executableFile、arguments和waitForProcessExit)都定义在ExecutableJobStepData。ExecutableJobStepData集成我们自定义的基类:JobStepData,下面是JobStepData的定义。JobStepData继承自NameTypeConfigurationElement(定了两个Configuration Property:Name和Type的ConfigurationElement),这是一个在Enterprise Library广泛使用的配置类型,因为分别自定义的类型都是通过它的Type属性进行配置的。

namespace Artech.BatchJobExecutor.Configuration
{
public class JobStepData : NameTypeConfigurationElement
{

[ConfigurationProperty("description", IsRequired = false, DefaultValue = "")]
public string Description
{
get
{
return this["description"] as string;
}
}

public JobStepData()
{
}

public JobStepData(string name, Type type)
: base(name, type)
{
}
}
}

ExecutableJobStepData直接继承自JobStepData ,定了3个配置属性分别于ExecutableJobStep的三个属性:executableFile、arguments和waitForProcessExit,以及参数列表。而以name-value形式定义的参数又定义在ArgumentEntry中。

namespace Artech.BatchJobExecutor.Configuration
{
[Assembler(typeof(ExecutableFileJobStepAssmbler))]
public class ExecutableJobStepData : JobStepData
{
[ConfigurationProperty("executableFile", IsRequired = true)]
public string ExecutableFile
{
get
{
return this["executableFile"] as string;
}
}

[ConfigurationProperty("arguments", IsRequired = false)]
public NamedElementCollection<ArgumentEntry> Arguments
{
get
{
return this["arguments"] as NamedElementCollection<ArgumentEntry>;
}
}

[ConfigurationProperty("waitForProcessExit", IsRequired = false, DefaultValue = true)]
public bool WaitForProcessExit
{
get
{
return (bool)this["waitForProcessExit"];
}
}
}

public class ArgumentEntry : NamedConfigurationElement
{
[ConfigurationProperty("value", IsRequired = true)]
public string Value
{
get
{
return this["value"] as string;
}
}

public ArgumentEntry()
{ }

public ArgumentEntry(string name)
: base(name)
{ }
}
}

在ExecutableJobStepData上应用了AssemblerAttribute,并指明了Assembler的类型:ExecutableFileJobStepAssmbler。而通过ExecutableFileJobStepAssmbler,则可以通过配置创建具体的ExecutableFileJobStep对象。ExecutableFileJobStepAssmbler实现了接口:IAssembler<JobStep, JobStepData>,在Assemble方法中,通过配置对象(objectConfiguration)创建ExecutableFileJob对象。由于可执行文件的路径(ExecutableFile属性)可能通过定义的变量定义,所以BatchJobExecutorSettings.ApplyVariable对变量进行解析。

namespace Artech.BatchJobExecutor.Configuration
{
public class ExecutableFileJobStepAssmbler : IAssembler<JobStep, JobStepData>
{
#region IAssembler<JobStep,JobStepData> Members

public JobStep Assemble(IBuilderContext context, JobStepData objectConfiguration, IConfigurationSource configurationSource, ConfigurationReflectionCache reflectionCache)
{
ExecutableJobStepData jobStepData = objectConfiguration as ExecutableJobStepData;
string spliter = " ";
StringBuilder arguments = new StringBuilder();
foreach (ArgumentEntry argument in jobStepData.Arguments)
{
arguments.Append(BatchJobExecutorSettings.ApplyVariable(argument.Value) + spliter);
}

JobStep jobStep = new ExecutableJobStep(BatchJobExecutorSettings.ApplyVariable(jobStepData.ExecutableFile), arguments.ToString().Trim(spliter.ToCharArray()), jobStepData.WaitForProcessExit);
jobStep.Name = jobStepData.Name;
jobStep.Description = jobStepData.Description;
return jobStep;
}

#endregion
}
}

从上面的类图,我们会发现我们漏掉了一个对象CustomJobStepData,它继承自JobStepData类型,并实现了两个重要的接口:IHelperAssistedCustomConfigurationData<CustomJobStepData>和ICustomProviderData。要说到具体的作用和实现,可能需要很多的文字才能阐述清楚,在这里我们可以把CustomJobStepData看成是能够实现配置文件中的配置内容和具体配置类型的适配。

image

我们有了配置相关的辅助类型,最终需要通过配置来创建与之匹配的对象,在EL中显得相对简单,我们只需要调用AssemblerBasedObjectFactory<TObject, TConfiguration>类型的Create方法就可以了。为此我创建了一个特殊的工厂类:JobStepCustomFactory ,用于创建具体的JobStep。

namespace Artech.BatchJobExecutor
{
public class JobStepCustomFactory : AssemblerBasedObjectFactory<JobStep, JobStepData>
{
public static JobStepCustomFactory Instance = new JobStepCustomFactory();
}
}

3、整个配置

在一开始,我们就介绍了如果进行批处理的配置,我们现在来看看,该配置类如何来定义:BatchJobExecutorSettings。

namespace Artech.BatchJobExecutor.Configuration
{
public class BatchJobExecutorSettings : SerializableConfigurationSection
{
[ConfigurationProperty("variables", IsRequired = true)]
public NamedElementCollection<VariableEntry> Variables
{
get
{
return this["variables"] as NamedElementCollection<VariableEntry>;
}
}

[ConfigurationProperty("batchJobs", IsRequired = true)]
public NamedElementCollection<BatchJobEntry> BatchJobs
{
get
{
return this["batchJobs"] as NamedElementCollection<BatchJobEntry>;
}
}

[ConfigurationProperty("defaultBatchJob", IsRequired = true)]
public string DefaultBatchJob
{
get
{
return this["defaultBatchJob"] as string;
}
}
public static BatchJobExecutorSettings GetConfigurationSection()
{
return ConfigurationSourceFactory.Create().GetSection("batchJobExecutor") as BatchJobExecutorSettings;
}

private static NamedElementCollection<VariableEntry> variables;

public static string ApplyVariable(string statement)
{
if (variables == null)
{
variables = GetConfigurationSection().Variables;
}

foreach (VariableEntry variable in variables)
{
statement = statement.Replace("$" + variable.Name + "$", variable.Value);
}

return statement;
}
}
}

整个Batch Job Executor的配置大体由以下两个部分组成:

  • 变量列表:这是一个NamedElementCollection<VariableEntry>类型,VariableEntry定义如下,
namespace Artech.BatchJobExecutor.Configuration
{
public class VariableEntry : NamedConfigurationElement
{
[ConfigurationProperty("value", IsRequired = true)]
public string Value
{
get
{
return this["value"] as string;
}
}

public VariableEntry()
{ }

public VariableEntry(string name)
: base(name)
{ }
}
}
  • Batch Job列表:NamedElementCollection<BatchJobEntry>类型,BatchJobEntry定义如下:
namespace Artech.BatchJobExecutor.Configuration
{
public class BatchJobEntry : NamedConfigurationElement
{
[ConfigurationProperty("description", IsRequired = false)]
public string Description
{
get
{
return this["description"] as string;
}
}
[ConfigurationProperty("steps", IsRequired = true)]
public NameTypeConfigurationElementCollection<JobStepData, CustomJobStepData> Activities
{
get
{
return this["steps"] as NameTypeConfigurationElementCollection<JobStepData, CustomJobStepData>;
}
}
}
}

表示Job Step序列的单个步骤的类型是NameTypeConfigurationElementCollection<JobStepData, CustomJobStepData>。

除了以上两个主要成员之外,在根节点上还定义了默认的Batch Job的名称,以及辅助方法ApplyVariable用于解析包含变量的表达式。

4、Batch Job的Batch Job Factory

我们最后还看看Batch Job的定义和创建,下面的类图列出来整个BatchJob创建体系的结构:通过BatchJobFactory创建BatchJob对象,BatchJobFactory最终通过EL的EnterpriseLibraryFactory实现对象的创建,而BatchJobFactory在进行对象创建工程中,会根据BatchJob类型指定的实现了ICustomFacotory的具体类型来创建对象,而我们定义的BatchJobCustomFactory实现了该接口,以及实现真正的对象创建过程。由于在配置中每个BatchJob都具有一个具体的、唯一的名称,一般地,我们通过传入具体的名称创建对应的BatchJob。但是如果我们在创建过程中,不曾传入BatchJob的名称,我们希望的是创建默认的BatchJob。EL中通过一个特殊的接口IConfigurationNameMapper实现了Default Name和具体的Batch Jon Name的匹配。BatchJobMapper实现了该接口,实现了我们需要的名称匹配关系。在这里我就不一一介绍了,有兴趣的朋友可以下载代码自行研究。

实际上,关于对象的创建一直是EL关注的问题,也是EL的核心所在。EL的ObjectBuild和ObjectBuild2就是专门为对象创建而设计的。ObjectBuild和ObjectBuild2是整个EL的基石,也是Unity、Software Factory的根基所在,涉及的类型比较复杂,非三言两语就能概括,有机会的话,我会写一些关于此方面的内容。

image


作者:蒋金楠
微信公众账号:大内老A
微博: www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号 蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
相关文章
|
12月前
|
存储 缓存 网络协议
阿里云特惠云服务器99元与199元配置与性能和适用场景解析:高性价比之选
2025年,阿里云长效特惠活动继续推出两款极具吸引力的特惠云服务器套餐:99元1年的经济型e实例2核2G云服务器和199元1年的通用算力型u1实例2核4G云服务器。这两款云服务器不仅价格亲民,而且性能稳定可靠,为入门级用户和普通企业级用户提供了理想的选择。本文将对这两款云服务器进行深度剖析,包括配置介绍、实例规格、使用场景、性能表现以及购买策略等方面,帮助用户更好地了解这两款云服务器,以供参考和选择。
|
11月前
|
机器学习/深度学习 文字识别 监控
安全监控系统:技术架构与应用解析
该系统采用模块化设计,集成了行为识别、视频监控、人脸识别、危险区域检测、异常事件检测、日志追溯及消息推送等功能,并可选配OCR识别模块。基于深度学习与开源技术栈(如TensorFlow、OpenCV),系统具备高精度、低延迟特点,支持实时分析儿童行为、监测危险区域、识别异常事件,并将结果推送给教师或家长。同时兼容主流硬件,支持本地化推理与分布式处理,确保可靠性与扩展性,为幼儿园安全管理提供全面解决方案。
503 3
|
10月前
|
域名解析 应用服务中间件 Shell
使用nps配置内网穿透加域名解析
使用nps配置内网穿透加域名解析
1044 76
|
12月前
|
人工智能 API 开发者
HarmonyOS Next~鸿蒙应用框架开发实战:Ability Kit与Accessibility Kit深度解析
本书深入解析HarmonyOS应用框架开发,聚焦Ability Kit与Accessibility Kit两大核心组件。Ability Kit通过FA/PA双引擎架构实现跨设备协同,支持分布式能力开发;Accessibility Kit提供无障碍服务构建方案,优化用户体验。内容涵盖设计理念、实践案例、调试优化及未来演进方向,助力开发者打造高效、包容的分布式应用,体现HarmonyOS生态价值。
717 27
|
供应链 项目管理 容器
深入探索 BPMN、CMMN 和 DMN:从定义到应用的全方位解析
在当今快速变化的商业环境中,对象管理组织(OMG)推出了三种强大的建模标准:BPMN(业务流程模型和符号)、CMMN(案例管理模型和符号)和DMN(决策模型和符号)。它们分别适用于结构化流程管理、动态案例处理和规则驱动的决策制定,并能相互协作,覆盖更广泛的业务场景。BPMN通过直观符号绘制固定流程;CMMN灵活管理不确定的案例;DMN以表格形式定义清晰的决策规则。三者结合可优化企业效率与灵活性。 [阅读更多](https://example.com/blog)
深入探索 BPMN、CMMN 和 DMN:从定义到应用的全方位解析
|
12月前
|
监控 Shell Linux
Android调试终极指南:ADB安装+多设备连接+ANR日志抓取全流程解析,覆盖环境变量配置/多设备调试/ANR日志分析全流程,附Win/Mac/Linux三平台解决方案
ADB(Android Debug Bridge)是安卓开发中的重要工具,用于连接电脑与安卓设备,实现文件传输、应用管理、日志抓取等功能。本文介绍了 ADB 的基本概念、安装配置及常用命令。包括:1) 基本命令如 `adb version` 和 `adb devices`;2) 权限操作如 `adb root` 和 `adb shell`;3) APK 操作如安装、卸载应用;4) 文件传输如 `adb push` 和 `adb pull`;5) 日志记录如 `adb logcat`;6) 系统信息获取如屏幕截图和录屏。通过这些功能,用户可高效调试和管理安卓设备。
|
12月前
|
数据采集 机器学习/深度学习 存储
可穿戴设备如何重塑医疗健康:技术解析与应用实战
可穿戴设备如何重塑医疗健康:技术解析与应用实战
450 4
|
12月前
|
存储 弹性计算 安全
阿里云服务器ECS通用型规格族解析:实例规格、性能基准与场景化应用指南
作为ECS产品矩阵中的核心序列,通用型规格族以均衡的计算、内存、网络和存储性能著称,覆盖从基础应用到高性能计算的广泛场景。通用型规格族属于独享型云服务器,实例采用固定CPU调度模式,实例的每个CPU绑定到一个物理CPU超线程,实例间无CPU资源争抢,实例计算性能稳定且有严格的SLA保证,在性能上会更加稳定,高负载情况下也不会出现资源争夺现象。本文将深度解析阿里云ECS通用型规格族的技术架构、实例规格特性、最新价格政策及典型应用场景,为云计算选型提供参考。
|
12月前
|
人工智能 自然语言处理 算法
DeepSeek大模型在客服系统中的应用场景解析
在数字化浪潮下,客户服务领域正经历深刻变革,AI技术成为提升服务效能与体验的关键。DeepSeek大模型凭借自然语言处理、语音交互及多模态技术,显著优化客服流程,提升用户满意度。它通过智能问答、多轮对话引导、多模态语音客服和情绪监测等功能,革新服务模式,实现高效应答与精准分析,推动人机协作,为企业和客户创造更大价值。
930 5
|
机器学习/深度学习 JSON 算法
淘宝拍立淘按图搜索API接口系列的应用与数据解析
淘宝拍立淘按图搜索API接口是阿里巴巴旗下淘宝平台提供的一项基于图像识别技术的创新服务。以下是对该接口系列的应用与数据解析的详细分析

推荐镜像

更多
  • DNS