65.9K
CodeProject 正在变化。 阅读更多。
Home

SQLXAgent - SQL Express 的作业 - 第 5 部分(共 6 部分)

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2017 年 9 月 30 日

CPOL

2分钟阅读

viewsIcon

7318

downloadIcon

43

创建和运行类似 SQL Server Enterprise 的作业 - 包如何运行。

第 1 部分 - 使用 SQLXAgent
第 2 部分 - 架构和设计决策
第 3 部分 - CSV 和 Excel 导入器代码
第 4 部分 - 作业调度代码
第 5 部分 - 包的运行方式 (本文)
第 6 部分 - 有趣的编码

引言

本文是描述 SQLXAgent 实用程序的系列文章的第 5 部分。在本文中,我将描述用于运行 SQLXAgent 包的代码。

需要牢记的重要一点是,SQLXAgent 包与您(可能)所了解的 DTSX 包没有任何合理的相似之处。最接近的类比是说所有 SQLXAgent 包都只包含一个任务,而该任务是一个脚本任务(因为您必须编写代码)。可以使用您可能喜欢的任何 .Net 语言创建包,只要它们编译成 .PKG 程序集(它只不过是一个具有“PKG”扩展名的 DLL)。

注意: 本文中提供的代码片段可能不会完全反映代码的最新和最佳版本。如果它与实际代码不完全匹配,那么它会非常接近。这就是在撰写文章时注意到问题的情况。

包的运行方式

包是通过 SQLXPkgRunner 命令行应用程序运行的。一切都从 JobThread.ExecuteJob 方法开始,在该方法中调用该应用程序。从下面的代码片段中省略了不相关的代码,但本文系列的第 4 部分中讨论了相关内容。

/// <summary>
/// Executes all of the steps for the job in (step.Position) sequential order.
/// </summary>
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
                            {
                                // this should never happen, but we check nonetheless
                                if (string.IsNullOrEmpty(step.SsisFilePath))
                                {
                                    ...
                                }
                                else
                                {
                                    string pkgDLLFileName = step.SsisFilePath;
                                    string path = System.IO.Path.Combine(Globals.AppPath, 
                                                                         "SQLXPkgRunner.exe");
                                    // we need to pass the package's (fully qlaified) filename,
                                    // the step ID, and the step connection string to the 
                                    // SQLXPkgRunner app
                                    string args = string.Format("-p\"{0}\" -s\"{1}\" -c\"{2}\"", 
                                                                pkgDLLFileName, 
                                                                step.ID, 
                                                                step.ConnectionString);
                                    // configure and start the app's process
                                    Process app = new Process();
                                    ProcessStartInfo info = new ProcessStartInfo()
                                    {
                                        Arguments       = args,
                                        CreateNoWindow  = true,
                                        FileName        = path,
                                        UseShellExecute = true,
                                    };
                                    app.StartInfo = info;
                                    app.Start();
                                    // we want to wait for it to exit
                                    app.WaitForExit();
                                    // and deal with the results.
                                    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());
    // assume success
    int    result     = 0;
    // if we have arguments
    if (args.Length > 0)
    {
        // if the package path is null/empty, error
        if (string.IsNullOrEmpty(options.Package))
        {
            result = (int)SQLXExceptionEnum.CmdLineArgPkgFilename;
        }
        // if the specified dll does not exist, error
        else if (!File.Exists(options.Package))
        {
            result = (int)SQLXExceptionEnum.PkgFileNotFound;
        }
        // if the step ID is null/empty, error
        else if (string.IsNullOrEmpty(options.StepID))
        {
            result = (int)SQLXExceptionEnum.CmdLineArgStepID;
        }
        // if the step's connectionstring is null/empty, error
        else if (string.IsNullOrEmpty(options.ConnectionString))
        {
            result = (int)SQLXExceptionEnum.CmdLineArgPkgConnString;
        }
        // make sure we can get the SQLXAgent connection string, 
        // but I don't remember why... :)
        else
        {
            string connStr = "";
            // result will be 0 (ok), 5 (config file not found), 
            // or 6 (conn string not found in config file)
            result = GetSQLXConnString(ref connStr);
        }
        // if we get here and result is still 0, load the package and 
        // run it.
        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;
    // load the package DLL
    var dll = Assembly.LoadFile(path);
    // assume it's invalid
    bool foundObject = false;
    try
    {
        // see if an object derived from  SQLXAgentPkgBase exists in the 
        // package DLL
        Type type = dll.GetExportedTypes().FirstOrDefault(x=>x.BaseType.Name.IsLike("%SQLXAgentPkgBase"));
        if (type != null)
        {
            // try to instantiate the object
            pkg = (SQLXAgentPkgBase)(Activator.CreateInstance(type));
            if (pkg != null)
            {
                // try to run the package
                foundObject = true;
                pkg.Run(stepID, connString);
                string failReason = pkg.FailReason;
            }
        }
		// can't find an appropriate class in the package asembly
        if (!foundObject)
        {
            result = (int)SQLXExceptionEnum.SQLXPkgBaseClassNotFound;
        }
    }
    // all of the exceptions that might be thrown here, and the return 
    // codes that are applicable
    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;
}

正如您所看到的,运行一个包是 SQLXAgent 机器中一个很小的齿轮。其中最有趣的部分是按需加载包 DLL,并确保它具有预期的派生类。

历史

  • 2017 年 9 月 29 日 - 首次发布。
     
© . All rights reserved.