首页 > 解决方案 > 如何从以编程方式生成的 SSIS 包中获取 GraphLayout XML

问题描述

如果在 Visual Studio 中打开 SSIS DTSX 包,设计器会在包末尾的 CDATA 块内创建一些额外的 DesignTimeProperties。

这看起来像

<DTS:DesignTimeProperties >< ![CDATA[<?xml version="1.0"?>
<!--This CDATA section contains the layout information of the package.The section includes information such as (x, y) coordinates, width, and height.-->
<!--If you manually edit this section and make a mistake, you can delete it. -->
<!--The package will still be able to load normally but the previous layout information will be lost and the designer will automatically re-arrange the elements on the design surface.-->
<Objects Version="8">
  <!--Each node below will contain properties that do not affect runtime behavior.-->
   <Package design-time-name="Package">
   <LayoutInfo >
     <GraphLayout Capacity="16" xmlns="clr-namespace:Microsoft.SqlServer.IntegrationServices.Designer.Model.Serialization;assembly=Microsoft.SqlServer.IntegrationServices.Graph" 
          xmlns:mssgle="clr-namespace:Microsoft.SqlServer.Graph.LayoutEngine;assembly=Microsoft.SqlServer.Graph" 
          xmlns:assembly="http://schemas.microsoft.com/winfx/2006/xaml" 
          xmlns:mssgm="clr-namespace:Microsoft.SqlServer.Graph.Model;assembly=Microsoft.SqlServer.Graph">
       <NodeLayout
         Size="146.5,41.5"
         Id="Package\Datenflusstask"
         TopLeft="71.9999971389772,45.7600003636678" />
       <NodeLayout
         Size="155,41.5"
         Id="Package\Datenflusstask 1"
         TopLeft="126.666661633386,127.573334347195" />
   ....

问题是,只有在 Visual Studio 中打开包并在集成服务项目中添加此信息时才会添加此信息。

ManagedDTS如果我以编程方式创建一个包,则缺少此信息。

我确实 在程序集中的命名空间内找到了SerializerHelper类,您可以在其中调用Microsoft.SqlServer.IntegrationServices.Designer.Model.SerializationMicrosoft.SqlServer.IntegrationServices.Graph.dll

var graphLayoutXml = SerializerHelper.Save(graphModelElement);

但不幸的是缺少坐标(NaN 或 0,0..)

我现在所做的是将包加载到字符串中

string contents = String.Empty;
Microsoft.SqlServer.Dts.Runtime.Package package = 
    new Microsoft.SqlServer.Dts.Runtime.Package();
using (StreamReader r = new StreamReader("DemoPackageWithoutDesignTimeProperties.dtsx"))
{
    contents = r.ReadToEnd();
}
package.LoadFromXML(contents, null);

然后通过

ControlFlowGraphModelElement controlFlowGraphModelElement = 
    new ControlFlowGraphModelElement();
controlFlowGraphModelElement.Initialize(package as IDTSSequence);

之后尝试通过 GraphLayoutXml

GraphModelElement graphModelElement = new GraphModelElement();
graphModelElement.Container = controlFlowGraphModelElement.Container;
var graphLayoutXml = SerializerHelper.Save(graphModelElement);    

显然我错过了一些布局。任何人都可以帮助正确的方法...

我知道 中的其他几个类Microsoft.SqlServer.IntegrationServices.Graph.dll,例如 LayoutGraphGraphLayoutGraphControl中,Microsoft.SqlServer.Graph.dll但不幸的是,文档没有那么有用..

标签: c#sql-serverssisetldts

解决方案


为什么它不工作?

首先,我想从@Fredipux 的精彩评论开始:

此 GraphLayout 结构描述了 Visual Studio SSIS 设计器中的 SSIS 任务和组件布局和大小;布局是在包编辑期间定义的。以编程方式生成包时,没有定义设计器布局,因为包是在 VS 设计器之外创建的

这是正确的!!要点是Microsoft.SqlServer.PipelineHostMicrosoft.SqlServer.DTSPipelineWrap和是 .NET 包装器Microsoft.SqlServer.DTSRuntimeWrapMicrosoft.SqlServer.ManagedDTS允许您在设计无意义的情况下以编程方式创建和执行包。如果您参考官方 SSIS 文档并浏览所有部分和小节,您会注意到只提到了这三个程序集,而其他程序集Microsoft.SqlServer.IntegrationServices.Graph.dll则没有提及,因为它们与 Visual Studio 设计器相关。

您可以简单地注意到,上面提到的四个程序集属于Microsoft.SqlServer.Dts命名空间,而另一个属于Microsoft.SqlServer.IntegrationServices.

如何在 Visual Studio 中安排任务?

请注意,在 Visual Studio 中,您可以在菜单条中转到

Format >> Auto Layout >> Diagram

在此处输入图像描述

然后将安排控制流中的所有任务

在官方文档中,我搜索了为 ManagedDTS 和其他程序集列出的所有属性和方法,但我没有发现提到可以使用AutoLayout()方法。此外,即使使用 ezApi 和 BIML,我也没有发现此选项可用。

此外,我尝试使用 Visual Studio 对象资源管理器搜索此方法,但没有成功。

可以尝试的东西

我认为添加布局的最简单方法是手动构建布局 XML 子句(从现有包中获取 XML 结构并更改 Id、大小和 TopLeft 属性)并将其添加到包 XML。

在运行了许多示例之后,这看起来是可能的,但当有许多连接的任务时非常复杂。在以下小节中,我说明了有关GraphLayoutXML 节点以及如何使用 C# 脚本添加信息的一些有用信息:

GraphLayout XML 节点

我尝试创建一个控制台应用程序来构建布局 XML 部分并将其添加到包中。但在展示代码和实验之前,我想说明一些关于 Graphlayout XML 的信息。

Graphlayout 节点位于 XML 的以下路径中:

DTS:DesignTimeProperties/Objects/Package/LayoutInfo/GraphLayout

它包含有关在控制流甚至优先约束连接器中添加的所有对象的设计信息。在这个 XML 节点中,您可能会发现三种类型的节点(可能更多):

  1. NodeLayout:描述任务,例如:

      <NodeLayout
      Size="151,42"
      Id="Package\Data Flow Task"
      TopLeft="369,179" />
    
  2. EdgeLayout:描述优先约束连接器,它更复杂,因为它包含有关连接器曲线的所有信息。例如:

    <EdgeLayout
      Id="Package.PrecedenceConstraints[Constraint]"
      TopLeft="308.5,125">
      <EdgeLayout.Curve>
        <mssgle:Curve
          StartConnector="{assembly:Null}"
          EndConnector="136,54"
          Start="0,0"
          End="136,46.5">
          <mssgle:Curve.Segments>
            <mssgle:SegmentCollection
              Capacity="5">
              <mssgle:LineSegment
                End="0,23" />
              <mssgle:CubicBezierSegment
                Point1="0,23"
                Point2="0,27"
                Point3="4,27" />
              <mssgle:LineSegment
                End="132,27" />
              <mssgle:CubicBezierSegment
                Point1="132,27"
                Point2="136,27"
                Point3="136,31" />
              <mssgle:LineSegment
                End="136,46.5" />
            </mssgle:SegmentCollection>
          </mssgle:Curve.Segments>
        </mssgle:Curve>
      </EdgeLayout.Curve>
      <EdgeLayout.Labels>
        <EdgeLabelCollection />
      </EdgeLayout.Labels>
    </EdgeLayout>
    
  3. AnnotationLayout:描述注释。例如:

    <AnnotationLayout
      Text="This is an annotation"
      ParentId="Package"
      FontInfo="{assembly:Null}"
      Size="121,60"
      Id="edef7a97-4253-4bb8-907e-6079f42467c6"
      TopLeft="421,152" />
    

实验

我创建了一个控制台应用程序,它手动构建该DTS:DesignTimeProperties部分而不添加优先约束连接器信息(因为它很复杂)。我只尝试添加有关具有以下 ID 的两个任务的信息:

  1. 执行 T-SQL 语句任务
  2. 数据流任务

这些任务是使用优先约束连接器连接的。

我使用以下代码来更改包 .dtsx 文件:

class Program
{
    static void Main(string[] args)
    {
        string package;
        string xmlLayout =  "  <DTS:DesignTimeProperties><![CDATA[<?xml version=\"1.0\"?>\r\n" +
                            "<!--This CDATA section contains the layout information of the package. The section includes information such as (x,y) coordinates, width, and height.-->\r\n" + 
                            "<!--If you manually edit this section and make a mistake, you can delete it. -->\r\n" +
                            "<!--The package will still be able to load normally but the previous layout information will be lost and the designer will automatically re-arrange the elements on the design surface.-->\r\n" +
                            "  <Objects\r\n" +
                            "  Version=\"8\">\r\n" +
                            "   <!--Each node below will contain properties that do not affect runtime behavior.-->\r\n" +
                             "  <Package\r\n" +
                            "    design-time-name=\"Package\">\r\n" +
                            "    <LayoutInfo>\r\n" +
                            "      <GraphLayout\r\n" +
                            "        Capacity=\"4\" xmlns=\"clr-namespace:Microsoft.SqlServer.IntegrationServices.Designer.Model.Serialization;assembly=Microsoft.SqlServer.IntegrationServices.Graph\" xmlns:mssgle=\"clr-namespace:Microsoft.SqlServer.Graph.LayoutEngine;assembly=Microsoft.SqlServer.Graph\" xmlns:assembly=\"http://schemas.microsoft.com/winfx/2006/xaml\">";


        xmlLayout += "\r\n" + AddNodeLayout("Execute T-SQL Statement Task", 193, 83);

        xmlLayout += "\r\n" + AddNodeLayout("Data Flow Task", 369, 179);

        xmlLayout += "\r\n" +   "      </GraphLayout>\r\n" +
                                "    </LayoutInfo>\r\n" +
                                "  </Package>\r\n" +
                                "</Objects>]]></DTS:DesignTimeProperties>\r\n";

        using (System.IO.StreamReader sr = new System.IO.StreamReader(@"G:\SSIS_Test\Integration Services Project1\Package3.dtsx"))
        {
            package = sr.ReadToEnd();
            sr.Close();
        }

        package = package.Substring(0, package.LastIndexOf("</DTS:Executable>")) +
            xmlLayout + package.Substring(package.LastIndexOf("</DTS:Executable>"));

        using (System.IO.StreamWriter sw = new System.IO.StreamWriter(@"G:\SSIS_Test\Integration Services Project1\Package3.dtsx"))
        {
            sw.Write(package);
            sw.Close();
        }
    }

    static string AddNodeLayout(string TaskId, int x, int y)
    {

        return "        <NodeLayout\r\n" +
      "          Size=\"225,42\"\r\n" +
      "          Id=\"Package\\" + TaskId + "\"\r\n" +
      "          TopLeft=\"" + x.ToString() + "," + y.ToString() + "\" />";


    }

}

结果与讨论

该部分已成功添加,但在 Visual Studio 中打开包时没有任何意义,因为任务未按描述排列(AutoLayout() 由 Visual Studio 执行)

在此处输入图像描述

我尝试移除两个任务之间的连接器并重新运行实验,任务安排成功:

在此处输入图像描述

这意味着如果未定义任务对象的任何布局,Visual Studio 将忽略添加的部分并在打开包时执行 AutoLayout() 函数。

基于此,如果您决定采用这种方法(甚至采用任何其他方法),您必须知道如何EdgeLayout生成节点。


更新 - 搜索 AutoLayout() 方法

根据您的评论,您不想手动添加每个任务位置,但您正在寻找执行AutoLayout()方法。

检查项目中导入的程序集后,我使用 Object Explorer for AutoLayout()method 进行了小搜索。我认为找到了两种相关的方法:

  • Microsoft.SqlServer.IntegrationServices.Designer.View.BaseGraphControl.AutoLayout()
  • Microsoft.SqlServer.IntegrationServices.Designer.View.IGraphControlEx.AutoLayout()

我不知道它们是否可以从 C# 脚本执行,因为它们不带任何参数。我认为它们不是为从 C# 脚本中使用而构建的,但它们是 Visual Studio 的内部方法。但是你可以试一试。

在此处输入图像描述

注意:在我看来,以编程方式执行此方法是没有用的,因为一旦打开包,它将由 Visual Studio 执行,并且根本不影响包的执行


推荐阅读