本文是关于 SQLXAgent 工具的系列文章的第五部分,将详细解析 SQLXAgent 包的运行机制。首先需要明确的是,SQLXAgent 包与传统的 DTSX 包有很大的不同。SQLXAgent 包通常只包含一个任务,并且这个任务是一个脚本任务,需要编写代码来实现。可以使用任何 .NET 语言来创建包,只要最终编译成 .PKG 程序集(实质上是一个带有 "PKG" 扩展名的 DLL)。
在本文中,可能会展示一些代码片段,这些代码可能不是最新版本,但与实际代码非常接近。在编写文章过程中,可能会发现一些问题,这就是为什么代码可能不完全匹配的原因。
包是通过 SQLXPkgRunner 命令行应用程序运行的。一切从 JobThread.ExecuteJob 方法开始,该方法调用应用程序。下面省略了不相关的代码,但这些内容在本系列文章的第四部分中有详细讨论。
public virtual void ExecuteJob()
{
if (!this.IsWorking)
{
foreach (StepItem step in this.Job.Steps)
{
if (step.StepIsEnabled)
{
switch (step.StepType)
{
case "SQL":
// ...
break;
case "PKG":
try
{
if (string.IsNullOrEmpty(step.SsisFilePath))
{
// ...
}
else
{
string pkgDLLFileName = step.SsisFilePath;
string path = System.IO.Path.Combine(Globals.AppPath, "SQLXPkgRunner.exe");
string args = string.Format("-p\"{0}\" -s\"{1}\" -c\"{2}\"", pkgDLLFileName, step.ID, step.ConnectionString);
Process app = new Process();
ProcessStartInfo info = new ProcessStartInfo()
{
Arguments = args,
CreateNoWindow = true,
FileName = path,
UseShellExecute = true,
};
app.StartInfo = info;
app.Start();
app.WaitForExit();
int result = app.ExitCode;
if (result > 0)
{
status = "FAIL";
SQLXExceptionEnum exception = Globals.IntToEnum(result, SQLXExceptionEnum.Unknown);
switch (exception)
{
case SQLXExceptionEnum.PkgFileNotFound:
reason = string.Concat(SQLXExceptionCodes.Codes[(int)exception], "- ", pkgDLLFileName);
break;
default:
reason = SQLXExceptionCodes.Codes[(int)exception];
break;
}
}
else
{
status = "SUCCESS";
reason = string.Empty;
}
}
}
catch (Exception ex)
{
status = "FAIL";
reason = ex.Message;
}
// DebugMsgs...
break;
}
}
else
{
// ...
}
}
}
}
SQLXPkgRunner 应用程序没有窗口或界面,因为它实际上是在 Windows 服务中执行的。当应用程序启动时,会对参数进行一些基本的检查。
static int Main(string[] args)
{
var options = new CommandlineOptions();
CommandLine.Parser.Default.ParseArguments(args, options);
Globals.SetExtensionFileSystemObjects(Assembly.GetExecutingAssembly());
int result = 0;
if (args.Length > 0)
{
if (string.IsNullOrEmpty(options.Package))
{
result = (int)SQLXExceptionEnum.CmdLineArgPkgFilename;
}
else if (!File.Exists(options.Package))
{
result = (int)SQLXExceptionEnum.PkgFileNotFound;
}
else if (string.IsNullOrEmpty(options.StepID))
{
result = (int)SQLXExceptionEnum.CmdLineArgStepID;
}
else if (string.IsNullOrEmpty(options.ConnectionString))
{
result = (int)SQLXExceptionEnum.CmdLineArgPkgConnString;
}
else
{
string connStr = "";
result = GetSQLXConnString(ref connStr);
}
if (result == 0)
{
result = LoadDllAndRun(options.Package.Trim(), options.StepID.Trim(), options.ConnectionString.Trim());
}
}
else
{
result = (int)SQLXExceptionEnum.NoCmdLineArgs;
}
return result;
}
如果一切检查通过,将加载并运行包程序集。假设(并建议)一个包只包含一个从 SQLXAgentPkgBase 派生的类,因为只查找找到的第一个对象,并且使用它。这个方法中的大部分代码都涉及到可能的异常。
private static int LoadDllAndRun(string path, string stepID, string connString)
{
int result = 0;
SQLXAgentPkgBase pkg = null;
var dll = Assembly.LoadFile(path);
bool foundObject = false;
try
{
Type type = dll.GetExportedTypes().FirstOrDefault(x => x.BaseType.Name.IsLike("%SQLXAgentPkgBase"));
if (type != null)
{
pkg = (SQLXAgentPkgBase)(Activator.CreateInstance(type));
if (pkg != null)
{
foundObject = true;
pkg.Run(stepID, connString);
string failReason = pkg.FailReason;
}
}
if (!foundObject)
{
result = (int)SQLXExceptionEnum.SQLXPkgBaseClassNotFound;
}
}
catch (BadImageFormatException)
{
result = (int)SQLXExceptionEnum.PkgRunnerBadImageFormat;
}
catch (System.IO.FileNotFoundException)
{
result = (int)SQLXExceptionEnum.PkgRunnerFileNotFound;
}
catch (System.IO.FileLoadException)
{
result = (int)SQLXExceptionEnum.PkgRunnerFileLoad;
}
catch (ArgumentException)
{
result = (int)SQLXExceptionEnum.PkgRunnerArgument;
}
catch (NotSupportedException)
{
result = (int)SQLXExceptionEnum.PkgRunnerNotSupported;
}
catch (System.Reflection.TargetInvocationException)
{
result = (int)SQLXExceptionEnum.PkgRunnerTargetInvocation;
}
catch (MethodAccessException)
{
result = (int)SQLXExceptionEnum.PkgRunnerMethodAccess;
}
catch (System.Runtime.InteropServices.InvalidComObjectException)
{
result = (int)SQLXExceptionEnum.PkgRunnerInvalidComObject;
}
catch (MissingMethodException)
{
result = (int)SQLXExceptionEnum.PkgRunnerMissingMethod;
}
catch (TypeLoadException)
{
result = (int)SQLXExceptionEnum.PkgRunnerTypeLoad;
}
catch (Exception)
{
result = (int)SQLXExceptionEnum.PkgRunnerUnexpected;
}
return result;
}