我有一个SSIS包,可以将数据从Microsoft Access复制到SQL Server。这两组表几乎相同。
背景:不出所料,我们的表架构随着我们开发产品而增长。因此,我们需要使用新列更新SSIS包。这是一个非常沉闷的任务,因此我尝试以编程方式在C#中创建SSIS包。这很顺利,但我想让这个过程变得更加容易。
问题:程序包生成过程要求在运行C#时存在源(Access)和目标(SQL Server)。这对我们当前的流程来说效果不佳。所以我想要:
我对两种解决方案中的第一种有相当强烈的偏好。但是我不知道如何让它们中的任何一个起作用。
更多细节:我一直在使用以下SSIS代码(来自Microsoft的示例)。我想我想要运行一些东西而不必执行AcquireConnections或ReinitializeMetaData - 我想自己提供元数据。显然,我提供的内容必须与验证和运行包时实际存在的内容完全匹配。
public IDTSComponentMetaData100 AddDestAdapter(IDTSPipeline100 pipeline, ConnectionManager destConnMgr, out IDTSDesigntimeComponent100 destDesignTimeComp)
{
IDTSComponentMetaData100 destComp = pipeline.ComponentMetaDataCollection.New();
destComp.ComponentClassID = OLEDB_DEST_GUID;
destComp.ValidateExternalMetadata = true;
destDesignTimeComp = destComp.Instantiate();
destDesignTimeComp.ProvideComponentProperties();
destComp.Name = "OleDB Destination - Sql Server";
destDesignTimeComp.SetComponentProperty("AccessMode", 0);
destDesignTimeComp.SetComponentProperty("OpenRowset", quotedTableName);
// set connection
destComp.RuntimeConnectionCollection[0].ConnectionManager = DtsConvert.GetExtendedInterface(destConnMgr);
destComp.RuntimeConnectionCollection[0].ConnectionManagerID = destConnMgr.ID;
// get metadata
destDesignTimeComp.AcquireConnections(null);
destDesignTimeComp.ReinitializeMetaData();
destDesignTimeComp.ReleaseConnections();
extCols = destComp.InputCollection[0].ExternalMetadataColumnCollection;
return destComp;
}
public void AddPathsAndConnectColumns()
{
IDTSOutput100 srcOutput = srcComp.OutputCollection[0];
IDTSOutputColumnCollection100 srcOutputCols = srcOutput.OutputColumnCollection;
IDTSInput100 destInput = destComp.InputCollection[0];
IDTSInputColumnCollection100 destInputCols = destInput.InputColumnCollection;
IDTSExternalMetadataColumnCollection100 destExtCols = destInput.ExternalMetadataColumnCollection;
Hashtable destColtable = new Hashtable(destExtCols.Count);
foreach (IDTSExternalMetadataColumn100 extCol in destExtCols)
{
destColtable.Add(extCol.Name, extCol);
}
// colConvertTable stores a pair of columns which need a type conversion
// colConnectTable stores a pair of columns which dont need a type conversion and can be connected directly.
Hashtable colConvertTable = new Hashtable(srcOutputCols.Count);
Hashtable colConnectTable = new Hashtable(srcOutputCols.Count);
foreach (IDTSOutputColumn100 outputCol in srcOutputCols)
{
// Get the column name to look for in the destination.
// Match column by name if match table is not used.
String colNameToLookfor = outputCol.Name;
IDTSExternalMetadataColumn100 extCol = (String.IsNullOrEmpty(colNameToLookfor)) ? null : (IDTSExternalMetadataColumn100)destColtable[colNameToLookfor];
// Does the destination column exist?
if (extCol != null)
{
colConnectTable.Add(outputCol.ID, extCol);
}
}
// Convert transform not needed. Connect src and destination directly.
pipeline.PathCollection.New().AttachPathAndPropagateNotifications(srcOutput, destInput);
IDTSVirtualInput100 destVirInput = destInput.GetVirtualInput();
foreach (object key in colConnectTable.Keys)
{
int colID = (int)key;
IDTSExternalMetadataColumn100 extCol = (IDTSExternalMetadataColumn100)colConnectTable[key];
// Create an input column from an output col of previous component.
destVirInput.SetUsageType(colID, DTSUsageType.UT_READONLY);
IDTSInputColumn100 inputCol = destInputCols.GetInputColumnByLineageID(colID);
if (inputCol != null)
{
// map the input column with an external metadata column
destDesignTimeComp.MapInputColumn(destInput.ID, inputCol.ID, extCol.ID);
}
}
}
可能的探索路线:
答案 0 :(得分:2)
IDTSExternalMetadataColumn100和IDTSExternalMetadataColumnCollection100是您正在寻找的用例的朋友。当对AcquireConnections,ReinitializeMetaData,ReleaseConnections的调用成功时,从外部源获取有关外部列的元数据并填充在集合中,并且每个外部列都映射到IDTSInputColumn100 / IDTSOutputColumn100列(取决于组件的类型)。这些调用负责获取元数据并创建所需的IDTSExternalMetadataColumn100和IDTSInputColumn100 / IDTSOutputColumn100列集合。
不幸的是,(至少在我知道的范围内),我们无法覆盖ReinitializeMetaData方法(此外,覆盖可能会导致其他问题,因此最好不要覆盖它们)。但我们仍然可以达到最终结果,尽管是以更详细的方式。这意味着我们将自己创建所需的IDTSExternalMetadataColumn100和IDTSInputColumn100 / IDTSOutputColumn100列(以编程方式)。
请查看下面的代码,了解涉及源组件的示例。您可以对Destination组件执行相同的操作,但是如果使用Destination组件,则不使用IDTSOutputColumn,而是使用IDTSInputColumn。
以下代码直接使用SSIS对象模型库;正如您所看到的,您需要做很多事情。我写了一个库来简化其中的一些事情。该库包含在类似用例中创建目标组件的示例。看here;特别是方法GenerateProjectToLoadTextFilesToSqlServerDatabase结尾的代码。
namespace ConsoleApplication5
{
// A struct ot represent an external column
public struct Column
{
public String Name;
public String SSISDataType;
public int Length;
public int Precision;
public int Scale;
public int CodePage;
public Column(String name, String ssisDataType, int length, int precision, int scale, int codePage)
{
Name = name;
SSISDataType = ssisDataType;
Length = length;
Precision = precision;
Scale = scale;
CodePage = codePage;
}
}
public class Packager
{
public Packager()
{
build();
}
private void build()
{
#region Package Related
// Package related
Package package = new Package();
Executable e = package.Executables.Add("STOCK:PipelineTask");
TaskHost thMainPipe = e as TaskHost;
MainPipe dataFlowTask = thMainPipe.InnerObject as MainPipe;
thMainPipe.Name = "MyDFT";
thMainPipe.DelayValidation = true;
#endregion
#region Add Connection Manager
// Add Connection Manager
ConnectionManager cm = package.Connections.Add("OLEDB");
cm.Name = "OLEDB ConnectionManager";
cm.ConnectionString = "Data Source=(local);" +
"Initial Catalog=AdventureWorks;Provider=SQLOLEDB.1;" +
"Integrated Security=SSPI;";
#endregion
#region Add a OleDB Source and set up basic properties
// Add an OLE DB source to the data flow.
IDTSComponentMetaData100 component = dataFlowTask.ComponentMetaDataCollection.New();
component.Name = "OLEDBSource";
component.ComponentClassID = "Microsoft.OLEDBSource"; // check for the exact component class ID on your machine
// Get the design time instance of the component.
CManagedComponentWrapper instance = component.Instantiate();
// Initialize the component
instance.ProvideComponentProperties();
// Specify the connection manager.
if (component.RuntimeConnectionCollection.Count > 0)
{
component.RuntimeConnectionCollection[0].ConnectionManager = DtsConvert.GetExtendedInterface(package.Connections[0]);
component.RuntimeConnectionCollection[0].ConnectionManagerID = package.Connections[0].ID;
}
// Set the custom properties.
instance.SetComponentProperty("AccessMode", 2);
instance.SetComponentProperty("SqlCommand", "Select * from Production.Product");
#endregion
#region Core example showcasing use of IDTSExternalMetadataColumn when external data source is not available.
// Typically here we call acquireconnection, reinitmetadata etc to get the metadata from a data source that exists.
// Instead we will populate the metadata ourselves
#region Get External Columns Metadata
// Get the collection of external columns
List<Column> externalColumns = new List<Column>();
// Hard Coding Here. But grab them from your metadata source programmatically.
Column columnA = new Column("col_a", "DT_STR", 24, 0, 0, 1252);
Column columnB = new Column("col_b", "DT_STR", 36, 0, 0, 1252);
Column columnC = new Column("col_c", "DT_STR", 48, 0, 0, 1252);
externalColumns.Add(columnA);
externalColumns.Add(columnB);
externalColumns.Add(columnC);
#endregion
#region Add External Columns to our required IDTSOutput100
// Grab the appropriate output as needed. We will be adding ExternalColumns to this Output
IDTSOutput100 output = component.OutputCollection[0];
// Add each external column to the above IDTSOutPut
foreach (Column extCol in externalColumns)
{
IDTSExternalMetadataColumn100 col = output.ExternalMetadataColumnCollection.New();
col.Name = extCol.Name;
col.Scale = extCol.Scale;
col.Precision = extCol.Precision;
col.Length = extCol.Length;
col.CodePage = extCol.CodePage;
col.DataType = (Wrapper.DataType)Enum.Parse(typeof(Wrapper.DataType), extCol.SSISDataType);
}
#endregion
#region Create OutputColumn if it does not exist/or grab the output column if it Exists. Then associate it to the External Column
// Now associate the External Column to an Output Column.
// Here, we will simply associate the external column to an output column if the name matches (because of our use case)
foreach (IDTSExternalMetadataColumn100 extCol in output.ExternalMetadataColumnCollection)
{
bool outputColExists = false;
// Set DataTypes and Associate with external column if output col exists
foreach (IDTSOutputColumn100 outputCol in output.OutputColumnCollection)
{
if (outputCol.Name == extCol.Name) // is map based on name
{
// Set the data type properties
outputCol.SetDataTypeProperties(extCol.DataType, extCol.Length, extCol.Precision, extCol.Scale, extCol.CodePage);
// Associate the external column and the output column
outputCol.ExternalMetadataColumnID = extCol.ID;
outputColExists = true;
break;
}
}
// Create an IDTSOutputColumn if not exists.
if (!(outputColExists))
{
IDTSOutputColumn100 outputCol = output.OutputColumnCollection.New();
outputCol.Name = extCol.Name; // map is based on name
// Set the data type properties
outputCol.SetDataTypeProperties(extCol.DataType, extCol.Length, extCol.Precision, extCol.Scale, extCol.CodePage);
// Associate the external column and the output column
outputCol.ExternalMetadataColumnID = extCol.ID;
}
}
#endregion
#endregion
#region Save the Package to disk
new Application().SaveToXml(@"C:\Temp\Pkg.dtsx", package, null);
#endregion
}
}
}
答案 1 :(得分:0)
只需使用BimlExpress,这是Visual Studio的免费插件。它提供了极大的灵活性,而无需与DTS API进行无聊且易于出错的交互。