3

我有一个 SSIS 包,其中包含需要第 3 方程序集的脚本任务。由于不允许将此程序集放在 SSIS 服务器上的 GAC 中,因此我在运行时将程序集绑定到脚本任务的静态构造函数中。这篇文章是我用作指导的。但是我想找到一种方法来避免硬编码程序集文件的路径。

我的工作代码如下所示:

  static ScriptMain()
     {
         AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
     }
     static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
     {
         if (args.Name.Contains("thirdparty"))
         {
             string path = @"C:\mydrive\Solution\Reference";
             return System.Reflection.Assembly.LoadFile(System.IO.Path.Combine(path, "thirdparty.dll"));
         }
         return null;
     }

我试过的:

1)将路径设置为包变量。这不起作用,因为静态构造函数运行时 Dts 对象尚未实例化,因此无法访问包变量。

2)尝试访问触发程序集解析事件的应用程序域,如下所示:

string appDomainPath = ((AppDomain)sender).BaseDirectory;

但这只是获取 VSTA 代码所在的目录。

我没主意了。这甚至可能吗?

4

3 回答 3

1

可以利用 Environment 将包变量潜入静态构造函数:

在“初始化”脚本任务中,Main():

Environment.SetEnvironmentVariable("LIBRARY_PATH", Dts.Variables["$Package::LIBRARY_PATH"].Value.ToString(), EnvironmentVariableTarget.Process);

然后在未来的任何地方脚本组件/任务

        static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            string library_path = Environment.GetEnvironmentVariable("LIBRARY_PATH", EnvironmentVariableTarget.Process);
            /* ...
               ... */
        }

笔记:

  • 首选使用包变量,因为它可以根据环境覆盖并通过 SQL Server 代理设置:
  • 在不需要程序集的初始任务中,我们使用具有 DTS 访问权限的 Main() 将路径插入 EnvironmentVariable,范围限定为进程(以防止泄漏到其他并发执行中)。
  • 就是这样,现在开始努力......我们的库路径将可以在每个其他下游任务的静态构造函数中访问。
于 2019-11-20T01:32:39.463 回答
0
  • 将需要 dll 的代码放在一个额外的方法中,而不是放在 Main() 中。
  • 创建一个静态变量来保存 dll 的路径。
  • 在 Main() 中,将包含您的 dll 路径的项目变量或包变量或用户变量读入您创建的静态变量中。
  • 调用您创建的方法来保存对 dll 的调用。

现在静态变量拥有正确的值,您可以在 CurrentDomain_AssemblyResolve 执行时使用它。如果您在 Main() 中使用 dll,则 CurrentDomain_AssemblyResolve 在您可以将变量读取到静态变量之前执行,因此它是空的。

static string path = null;

static ScriptMain()
{
    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}

public void Main()
{
    path = (string)Dts.Variables["User::path"].Value;
    MethodsWhichUsesDll();
}

public void MethodsWhichUsesDll()
{
    //Code which uses your dll
}

static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    if (args.Name.Contains("thirdparty"))
    {
        return System.Reflection.Assembly.LoadFile(System.IO.Path.Combine(path, "thirdparty.dll"));
    }
    return null;
}
于 2020-12-17T15:40:06.083 回答
0

我已经实现了这一点——恐怕在 VB 中,但它应该很容易翻译成 C#。

从您引用的文章中,让我走上正轨的一点是:

诀窍是要意识到 .NET 的即时 (JIT) 编译负责加载依赖程序集,因此保证在方法执行之前不会加载类型方法中引用的程序集。

因此,我的脚本任务如下所示:

' See the Main() task, where this is set
Public Shared referencePath As String

Public Sub New()
    AddHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf CurrentDomain_AssemblyResolve
End Sub

''' <summary>
''' The Ionic.zip reference isn't in the GAC, so we need to give the assembly resolver a hint as to where it resides:
''' </summary>
''' <param name="sender"></param>
''' <param name="args"></param>
''' <returns></returns>
Private Shared Function CurrentDomain_AssemblyResolve(ByVal sender As Object, ByVal args As ResolveEventArgs) As Reflection.Assembly
    If args.Name.ToLower.Contains("ionic.zip") Then
        Return Reflection.Assembly.LoadFile(Path.Combine(referencePath, "Ionic.zip.dll"))
    End If

    Return Nothing
End Function

Public Sub Main()
    ' Set the static referencePath to our SSIS variable's value - we need to do this because the Ionic.Zip reference,
    ' referred to in the ProcessEmails() method, will otherwise look in the GAC, and won't find what it's looking for.
    referencePath = Dts.Variables("ReferencePath").Value.ToString()

    Dts.TaskResult = ProcessEmails()
End Sub

我没有包含 ProcessEmails() 方法主体,但这只会执行您的 Main() 任务目前所做的任何事情。

这样,程序集解析器在调用 ProcessEmails() 方法之前不会尝试解析第 3 方程序集,从而使 Main() 有机会将其设置为 Dts.Variable。

在 GAC 中没有第 3 方 DLL 的 SQL 2017 环境中测试和工作。

于 2019-12-02T09:37:00.187 回答