Click here to Skip to main content
Click here to Skip to main content

Importing data with the SSIS Object model

, 25 Jun 2007
Rate this:
Please Sign up or sign in to vote.
Describes how to use the SSIS object model for importing data to SQL server 2005

Sample image

Introduction

SQL server 2005 Integration Service ships with a rich set of APIs and in this article I am going to show you a way in which you can create and execute an SSIS package programmatically from C# code.

Background

Couple of weeks ago, I was going to import a huge size CSV (comma separated value) file into the SQL server database. Well, it is not such a difficult job that I will write an article on it. Anyone can do that using the SQL server 2005 import wizard, which is a great flexible tool to do this kind of job. But I need to do that work programmatically. And that's what made me think and search the Internet for a solution. After spending some hours I found that SQL server 2005 provides a rich set of APIs of SSIS (SQL server 2005 Integration Service) and anyone can easily create an SSIS package (called DTS Package previously) and execute that package. Unfortunately I did not find code that creates a package that imports data from the CSV file to the SQL server 2005 database. Most of the demo code that I found during my searching imported data from the SQL server to the SQL server or from the SQL server to any flat file. It would be very straightforward to implement one for my own purpose- hopefully just by following the existing demo codes. But soon I discovered that it is a subtle different then those example codes. And after two days of brainstorming I have created a code base which satisfied me. In this article I am just sharing my code and thoughts with you.

Using the code

In my code base I have created two projects for this import business. The first one (ImportLib) is a class library that implements the SSIS package creation tasks and the second one (Import) is a Windows form application which is actually a DEMO that uses the first one as a reference and initiates an Import Job. So exploring the first one is enough to get an good idea about what I actually did.

The object model that I have created is basically standing on a couple of interfaces, namely ImportLib.ISourceStorage, ImportLib.IDestinationStorage, ImportLib.IntegrationService.ISource and ImportLib.IntegrationService.IDestination. Before diving into the details I would like to express my thoughts.

After exploring the SSIS you would find that it is very easy to import data from any kind of data sources to any data destinations. The SSIS object model implemented some well defined interfaces to support this wonderful feature. As I am writing code to programmatically create packages - why shouldn't I implement my code in a way that I can extend more in the future? Currently it will be able to import data from a CSV file to SQL databases but who knows that tomorrow I don't have to import data from an Excel file to SQL server or any other kind of data destination? This idea made me write the ImportLib.ISourceStorage and ImportLib.IDestinationStorage interfaces. I guess you already got an abstract idea for what these interfaces are there – right? Yeah, For all source kind of source storage I defined the ImportLib.ISourceStorage interface. Currently I am only implementing this interface for CSV file sources but later when I will write the Excel source I will simply implement this interface again. The same idea is also applicable to the destination storage where the interface is ImportLib.IDestinationStorage.

Let's take a look on these interfaces:

/// <summary>
/// Defines a contact for the data source
/// </summary>
public interface ISourceStorage
{
    /// <summary>
    /// Initializes the data source
    /// </summary>
    /// <param name="properties">
    /// A <see cref="T:IDictionary<string,
    /// string>"/> that contains
    /// the initialization properties</param>
    void InitializeSource(IDictionary<string, string> properties);

    /// <summary>
    /// Get the schema table for the data source
    /// </summary>
    /// <returns>An instance of <
    /// see cref="T:DataTable"/> that contains the schema
    /// information of the data source</returns>
    DataTable GetSchemaTable();

    /// <summary>
    /// Get the Category of the underlying persistent storage
    /// </summary>
    StorageMedium StorageMedium { get; }

    /// <summary>
    /// Get or set the mapping manager
    /// </summary>
    ColumnMappingController MapManager { get; set; }
}

And

/// <summary>
///     Defines a contact for the Data destination
/// </summary>
public interface IDestinationStorage
{
    /// <summary>
    /// Initializes the data destination
    /// </summary>
    /// <param name="properties">
    /// A <see cref="T:IDictionary<string,
    /// string>"/> that contains
    /// the initialization properties</param>
    void InitializeDestination(IDictionary<string,
        string> properties);

    /// <summary>
    /// Get the Category of the underlying persistent storage
    /// </summary>
    StorageMedium StorageMedium { get; }

    /// <summary>
    /// Get or set the mapping manager
    /// </summary>
    ColumnMappingController MapManager { get; set; }
}

Now let's take a look at the concrete implementations. ImportLib.Delimited.DelimitedDataSource is the concrete class that implements the ImportLib.ISourceStorage. This class simply encapsulated the functionality that is defined by the interface and is specific for a CSV file. It helps to read the CSV file for the source schema using the System.Data.OleDb.OleDbConnection class. And another concrete class that I named as ImportLib.Sql.SqlStorageDestination implements the ImportLib.IDestinationStorage interface. This class provides the functionality defined by the interface and it also contains the routines that are used to create the destination datastore (database and table).

So far so good. Now let's concentrate on the SSIS specific classes and interfaces. Well here again I have defined two interfaces that I mentioned earlier. ImportLib.IntegrationService.ISource and ImportLib.IntegrationService.IDestination. Let's take a look on them.

/// <summary>
/// Defines a contact for the SQL Server
/// Integration Service source objects.
/// </summary>
/// <remarks>
/// Different SSIS source objects should satisfy this interface.
/// Like
/// Flat file data source, OleDB data source etc.
/// </remarks>
public interface ISource
{
    /// <summary>
    /// get or set the <
    /// see cref="T:ImportLib.IDataSource"/> instance
    /// that represents the physical data source.
    /// </summary>
    ISourceStorage DataSource
    {
        get;
        set;
    }

    /// <summary>
    /// Creates a connection manager instance for a given source file
    /// into the context of the given <
    /// see cref="T:ImportLib.IntegrationService
    /// .SsisPackage"/>
    /// object.
    /// </summary>
    /// <param name="package">
    /// An instance of <
    /// see
    /// cref="T:ImportLib.IntegrationService
    /// .SsisPackage"/>.</param>
    void CreateConnection(DTSPackage package);

    /// <summary>
    /// Creates the source dataflow component.
    /// </summary>
    /// <param name="package">The package
    /// instance.</param>
    /// <param name="dataflowTask">
    /// The dataflow task under which the component will be
    /// created.</param>
    /// <returns>An instance of <see
    /// cref="T:SsisDataflowComponent"/>.</returns>
    DataflowComponent CreateSourceDataFlowComponent(
        DTSPackage package, DTSExecutable dataflowTask);

    /// <summary>
    /// Initialize the source dataflow component
    /// </summary>
    /// <param name="sourceDataFlowComponent">
    /// An instance of <see cref="T:
    /// SsisDataflowComponent"/></param>
    void InitializeDataflowComponent(DataflowComponent
        sourceDataFlowComponent);
}

And another one is:

/// <summary>
/// Defines a contact for the SQL Server Integration
/// Service Destination objects.
/// </summary>
/// <remarks>
/// Different data destinations should satisfy
/// this interface. such as Flat file
/// destinations, OleDB destination etc.
/// </remarks>
public interface IDestination
{
    /// <summary>
    /// Get or set the <see cref="T:ImportLib.
    /// IDataDestination"/> instance
    /// that represents the physical data destination.
    /// </summary>
    IDestinationStorage DataDestination
    {
        get;
        set;
    }

    /// <summary>
    /// Get or set the <see cref="T:ImportLib.
    /// ISsisSource"/> instance
    /// </summary>
    ISource SsisSource
    {
        get;
        set;
    }

    /// <summary>
    /// Creates a connection manager instance for a given source file
    /// into the context of the
    /// given <see cref="T:ImportLib.IntegrationService.
    /// SsisPackage"/>
    /// object.
    /// </summary>
    /// <param name="package">
    /// An instance
    /// of <see cref="T:ImportLib.
    /// IntegrationService.SsisPackage"/>.</param>
    void CreateConnection(DTSPackage package);

    /// <summary>
    /// Creates the task that will create the
    /// destination storage (ex. database, table etc);
    /// </summary>
    /// <param name="package">The
    /// package where the task should be created</param>
    /// <returns>
    /// An instance of <see cref="T:ImportLib.IntegrationService.
    /// SsisExecutable"/></returns>
    DTSExecutable CreateStorageCreationTask(DTSPackage package);

    /// <summary>
    /// Creates the destination dataflow component.
    /// </summary>
    /// <param name="package">The package
    /// instance.</param>
    /// <param name="dataflowTask">
    /// The dataflow task under which the component will
    /// be created.</param>
    /// <returns>
    /// An instance of <see cref="T:SsisDataflowComponent
    /// "/>.</returns>
    DataflowComponent CreateDestinationDataFlowComponent
        (DTSPackage package, DTSExecutable dataflowTask);

    /// <summary>
    /// Initialize the destination dataflow component
    /// </summary>
    /// <param name="destinationDataFlowComponent">
    /// An instance of <see
    /// cref="T:SsisDataflowComponent"/></param>
    void InitializeDataflowComponent(DataflowComponent
        destinationDataFlowComponent);
}

These interfaces strictly define the SSIS specific routines such as creating connections, creating data flow components along with some initialization routines. Defining SSIS dataflow is out of scope of this article, for detailed information on these SSIS components, please read MSDN. Now we will take a look at the concrete implementations. ImportLib.IntegrationService.Specialized.FlatFileSource is the concrete implementation of the ImportLib.IntegrationService.ISource interface. Have a look at this class. Here the important thing is we have to explicitly create the source columns ( CreateSourceColumns method ) for the connection which SSIS will never create for you.

namespace ImportLib.IntegrationService.Specialized
{
    /// <summary>
    /// Contains the implementations of the
    /// <see
    /// cref="T:ImportLib.IntegrationService.
    /// ISsisSource"/>
    /// interface.
    /// </summary>
    public class FlatFileSource : ISource
    {
        // The delimited data source instance
        private DelimitedDataSource delimitedDataSource;
        // The Moniker text for the flatfile source
        public const string FlatFileMoniker = @"FLATFILE";
        // The source data flow component GUID
        public const string SourceDataFlowComponentID
          = "{90C7770B-DE7C-435E-880E-E718C92C0573}";
        // schema table
        private DataTable schemaTable = null;
        // The connection manager instance
        private ConnectionManager connectionManager;

        /// <summary>
        /// Creates a new instance
        /// </summary>
        public FlatFileSource()
        {

        }

        #region ISsisSource Members

        /// <summary>
        /// get or set the <see
        /// cref="T:ImportLib.IDataSource"/> instance
        /// that represents the physical data source.
        /// </summary>
        public ISourceStorage DataSource
        {
            get { return delimitedDataSource; }
            set
            {
                // initializing
                delimitedDataSource = value as DelimitedDataSource;
                // Get the schema table
                schemaTable = value.GetSchemaTable();
                // Assert
                Debug.Assert(delimitedDataSource != null);
            }
        }

        /// <summary>
        /// Creates a connection manager instance for a given source file
        /// into the context of the given <see
        /// cref="T:ImportLib.IntegrationService.SsisPackage"/>
        /// object.
        /// </summary>
        /// <param name="package">An instance of
        /// <see cref="T:ImportLib.IntegrationService.
        /// SsisPackage"/>.</param>
        public void CreateConnection(DTSPackage package)
        {
            #region Logging
            Logger.WriteInformation(
              "Creating connection to the source file.");
            #endregion
            // creating a connection manager
            // instance using the FLATFILE moniker
            connectionManager =
              package.InnerObject.Connections.Add(FlatFileMoniker);
            connectionManager.ConnectionString =
                     delimitedDataSource.FileName;
            connectionManager.Name =
               "SSIS Connection Manager for Files";
            connectionManager.Description =
            string.Concat("SSIS Connection Manager");
            // Setting some common properties of the connection manager object
            connectionManager.Properties
            ["ColumnNamesInFirstDataRow"].
                 SetValue(connectionManager,
              delimitedDataSource.FirstRowIsHeader);
            connectionManager.Properties["Format"].SetValue
               (connectionManager, "Delimited");
            connectionManager.Properties["HeaderRowDelimiter"].

                 SetValue(connectionManager,
              delimitedDataSource.HeaderRowDelimiter);
            if (delimitedDataSource.TextQualifier != null)
            {   // If user has been specified a text qualifier then put
               // it into the connection string property
                connectionManager.Properties["TextQualifier"]

                 .SetValue(connectionManager,
                    delimitedDataSource.TextQualifier);
            }
            // create the source columns into the connection manager
            CreateSourceColumns();

            #region Logging
            Logger.WriteInformation(
                "Creating connection to the source file.....Completed");
            #endregion
        }

        /// <summary>
        /// Creates the source dataflow component.
        /// </summary>
        /// <param name="package">The package
        /// instance.</param>
        /// <param name="dataflowTask">
        /// The dataflow task under which the component will be
        /// created.</param>
        /// <returns>An instance of <see
        /// cref="T:SsisDataflowComponent"/>.</returns>
        public DataflowComponent CreateSourceDataFlowComponent
                  (DTSPackage package, DTSExecutable dataflowTask)
        {
            // create the component
            DataflowComponent sourceDataFlowComponent =
             new DataflowComponent(dataflowTask, SourceDataFlowComponentID,
                        "Source Data Flow component");

            return sourceDataFlowComponent;
        }

        /// <summary>
        /// Initialize the source dataflow component
        /// </summary>
        /// <param name="sourceDataFlowComponent">
        /// An instance of <see
        /// cref="T:SsisDataflowComponent"/>
        /// </param>
        public void InitializeDataflowComponent(
            DataflowComponent sourceDataFlowComponent)
        {
            #region Logging
            Logger.WriteInformation(
            "Initializing the managed instance for the source file.");
            #endregion
            // load the COM for the given GUID
            CManagedComponentWrapper managedFlatFileInstance =
                  sourceDataFlowComponent.ComponentInstance;
            // get the populate the properties
            managedFlatFileInstance.ProvideComponentProperties();
            // putting the connection
            if (
              sourceDataFlowComponent.InnerObject.
            RuntimeConnectionCollection.Count > 0)
            {   // If connection is necessary
                sourceDataFlowComponent.InnerObject.
              RuntimeConnectionCollection[0].ConnectionManagerID =
                    connectionManager.ID;
                    sourceDataFlowComponent.InnerObject.
            RuntimeConnectionCollection[0].ConnectionManager =
                                DtsConvert.ToConnectionManager90
                                      (connectionManager);

            }
            // establish a connection
            managedFlatFileInstance.AcquireConnections(null);
            // Initialize the metadata
            managedFlatFileInstance.ReinitializeMetaData();
            // create the mapping now
            IDTSExternalMetadataColumn90 exOutColumn;
            foreach (IDTSOutputColumn90 outColumn in
                sourceDataFlowComponent.InnerObject.
                      OutputCollection[0].OutputColumnCollection)
            {   // create the MAP
                exOutColumn =
                    sourceDataFlowComponent.InnerObject.
                    OutputCollection[0].
                 ExternalMetadataColumnCollection[outColumn.Name];
                // map it
                managedFlatFileInstance.MapOutputColumn(
                    sourceDataFlowComponent.InnerObject.
                      OutputCollection[0].ID, outColumn.ID,
                    exOutColumn.ID, true);
            }
            // Release the connection now
            managedFlatFileInstance.ReleaseConnections();

            #region Logging
            Logger.WriteInformation
                 ("Initializing the managed instance for the source
                   file......completed");
            #endregion
        }

        #endregion

        /// <summary>
        /// Creates the source columns for the flat file connection manager
        /// instance
        /// </summary>
        private void CreateSourceColumns()
        {
            // get the actual connection manger instance
            RuntimeWrapper.IDTSConnectionManagerFlatFile90
            flatFileConnection =
                connectionManager.InnerObject
               as RuntimeWrapper.IDTSConnectionManagerFlatFile90;

            RuntimeWrapper.IDTSConnectionManagerFlatFileColumn90 column;
            RuntimeWrapper.IDTSName90 name;

            // trace the current count
            Debug.WriteLine(flatFileConnection.Columns.Count);

            DataTable schemaTable
             = DataSource.GetSchemaTable(); // get the schema table

            foreach (DataRow row in schemaTable.Rows)
            {   // iterate
                string colName
                = row["ColumnName"] as string;
                     // get the col name
                // now create a new column for the connection manager
                column
                 = flatFileConnection.Columns.Add();
                        // if this is the last row

                if (schemaTable.Rows.IndexOf(row)
                     == (schemaTable.Rows.Count - 1))
                    column.ColumnDelimiter =
                        delimitedDataSource.HeaderRowDelimiter;
                // add the row delimiter
                else
                    column.ColumnDelimiter = delimitedDataSource.Delimiter;

                column.TextQualified =
                delimitedDataSource.TextQualifier != null;
                column.ColumnType = "Delimited";
                column.DataType =
                 RuntimeWrapper.DataType.DT_WSTR;
                column.DataPrecision = 0;
                column.DataScale = 0;
                name = (RuntimeWrapper.IDTSName90)column;
                name.Name = colName;
            }
        }
    }
}

And now let's have a look at the counterpart, the destination implementation.

namespace ImportLib.IntegrationService.Specialized
{
    /// <summary>
    /// Contains the implementation of the IDataDestination interface.
    /// </summary>
    public class SqlServerDataDestination : IDestination
    {
        // The SQL server data destination
        private SqlStorageDestination sqlDataDestination;
        // The Moniker text for the Ole DB
        public const string OleDBMoniker = "OLEDB";
        // the component ID
        public const string OleDBDestinationDataFlowComponentID
                 = "{E2568105-9550-4F71-A638-B7FE42E66922}";
        // source
        private ISource ssisSource;
        // connection manager
        private ConnectionManager connectionManager;

        #region ISsisDestination Members

        /// <summary>
        /// Get or set the <see
        /// cref="T:ImportLib.IDataDestination"/> instance
        /// that represents the physical data destination.
        /// </summary>
        public IDestinationStorage DataDestination
        {
            get { return sqlDataDestination; }
            set
            {
                // initializing
                sqlDataDestination = value as SqlStorageDestination;
                // Assert
                Debug.Assert(sqlDataDestination != null);
            }
        }

        /// <summary>
        /// Get or set the <see
        /// cref="T:ImportLib.ISsisSource"/> instance
        /// </summary>
        public ISource SsisSource
        {
            get { return ssisSource; }
            set { ssisSource = value; }
        }

        /// <summary>
        /// Creates a connection manager instance for a given source file
        /// into the context of the given <see
        /// cref="T:ImportLib.IntegrationService.SsisPackage"/>
        /// object.
        /// </summary>
        /// <param name="package">An instance of
        ///  <see cref="T:ImportLib.IntegrationService.SsisPackage
        /// "/>.</param>
        public void CreateConnection(DTSPackage package)
        {
            // Creating a connection using the oledb moniker
            connectionManager =
              package.InnerObject.Connections.Add(OleDBMoniker);
            connectionManager.ConnectionString =
                    GetSsisConnectionString();
            connectionManager.Name =
               "SSIS Connection Manager for Oledb";
            connectionManager.Description =
          string.Concat(
             "SSIS Connection Manager for ",
                   sqlDataDestination.DatabaseName);
        }

        /// <summary>
        /// Creates the task that will create the destination
        /// storage (ex. database, table etc);
        /// </summary>
        /// <param name="package">The package
        /// where the task should be created</param>
        /// <returns>An instance of
        /// <see cref="T:ImportLib.
        /// IntegrationService.SsisExecutable"/></returns>
        public DTSExecutable CreateStorageCreationTask(DTSPackage package)
        {
            // get the SQL task type
            Type taskType = typeof(ExecuteSQLTask);
            // create a task of type ExecuteSQLTask
            DTSExecutable executable = new DTSExecutable(package, taskType);

            // now configuring the new task
            TaskHost taskHost = executable.InnerObject as TaskHost;
                     // get the Task host instance
            ExecuteSQLTask sqlTask = taskHost.InnerObject as ExecuteSQLTask;
                     // get the SQL task from the host
            sqlTask.Connection = connectionManager.Name;
                     // set the connection manager
            sqlTask.SqlStatementSource =
                sqlDataDestination.GetDestinationTableCreationSql
                    (ssisSource.DataSource.GetSchemaTable());
                     // set the SQL that generates the table

            return executable;
        }

        /// <summary>
        /// Creates the destination dataflow component.
        /// </summary>
        /// <param name="package">The package
        ///  instance.</param>
        /// <param name="dataflowTask">The dataflow task
        /// under which
        /// the component will be created.</param>
        /// <returns>An instance of <see
        /// cref="T:SsisDataflowComponent"/>.</returns>
        public DataflowComponent CreateDestinationDataFlowComponent
                  (DTSPackage package, DTSExecutable dataflowTask)
        {
            #region Logging
            Logger.WriteInformation(
              "Creating managed instances for the destination database");
            #endregion
            // create the component now
            DataflowComponent destinationDataFlowComponent
             = new DataflowComponent(dataflowTask,
                OleDBDestinationDataFlowComponentID,
            "Destination Oledb Component");

            // Before going thru the initialization we need
            // to create the destination table
            // because the SSIS object model will try to
            // access that table fore reading the metadata
            sqlDataDestination.CreateDataStore
                    (ssisSource.DataSource.GetSchemaTable());

            // get the COM instance
            CManagedComponentWrapper managedOleInstance =
                        destinationDataFlowComponent.ComponentInstance;
            // populate the properties
            managedOleInstance.ProvideComponentProperties();
            // setting the connection
            if (destinationDataFlowComponent.InnerObject.
                      RuntimeConnectionCollection.Count > 0)
            {   // If connection is necessary
                destinationDataFlowComponent.InnerObject.
                     RuntimeConnectionCollection[0].
                    ConnectionManagerID =
                    connectionManager.ID;
                destinationDataFlowComponent.InnerObject.
                     RuntimeConnectionCollection[0].
                        ConnectionManager =
                                DtsConvert.ToConnectionManager90
                                    (connectionManager);

            }
            // Set the custom properties.
            managedOleInstance.SetComponentProperty(
                  "AccessMode", 0);
                          // Table of View mode
            managedOleInstance.SetComponentProperty
                ("AlwaysUseDefaultCodePage", false);
                          // Default Codepage
            managedOleInstance.SetComponentProperty
                        ("DefaultCodePage", 1252);
                          // Set it
            managedOleInstance.SetComponentProperty
                          ("FastLoadKeepIdentity", false);
                          // Fast load
            managedOleInstance.SetComponentProperty
                           ("FastLoadKeepNulls", false);
            managedOleInstance.SetComponentProperty
                          ("FastLoadMaxInsertCommitSize", 0);
            managedOleInstance.SetComponentProperty
                                 ("FastLoadOptions",
                            "TABLOCK,CHECK_CONSTRAINTS");
            managedOleInstance.SetComponentProperty("OpenRowset",
                string.Format("[{0}].[dbo].[{1}]",
                   sqlDataDestination.DatabaseName,
                        sqlDataDestination.TableName));



            #region Logging
            Logger.WriteInformation(
          "Creating managed instances for the destination
            database....completed");
            #endregion
            return destinationDataFlowComponent;
        }

        /// <summary>
        /// Initialize the destination dataflow component
        /// </summary>
        /// <param name="destinationDataFlowComponent">
        /// An instance of <see
        /// cref="T:SsisDataflowComponent"/></param>
        public void InitializeDataflowComponent(DataflowComponent
                  destinationDataFlowComponent)
        {
            #region Logging
            Logger.WriteInformation(
                "Creating the destination columns and their mappings");
            #endregion
            // Get the COM instance
            CManagedComponentWrapper managedOleInstance

                     = destinationDataFlowComponent.ComponentInstance;

            // Now activate a connection and create the mappings

            // Establish a connection
            managedOleInstance.AcquireConnections(null);
            // initialize the metadata
            managedOleInstance.ReinitializeMetaData();
            // Get the destination's default input and virtual input.
            IDTSInput90 input = destinationDataFlowComponent.
                      InnerObject.InputCollection[0];
            IDTSVirtualInput90 vInput = input.GetVirtualInput();

            // Iterate through the virtual input column collection.
            foreach (IDTSVirtualInputColumn90 vColumn
                   in vInput.VirtualInputColumnCollection)
            {
                bool res = sqlDataDestination.MapManager.
              IsSuppressedSourceColumn
                  (vColumn.Name,ssisSource.DataSource.GetSchemaTable());
                if (!res)
                {
                    // Call the SetUsageType method of the destination
                    //  to add each available virtual input column as
                    // an input column.
                    managedOleInstance.SetUsageType(
                       input.ID, vInput, vColumn.LineageID,
                    DTSUsageType.UT_READONLY);
                }
            }

            IDTSExternalMetadataColumn90 exColumn;
            foreach (IDTSInputColumn90 inColumn in
                destinationDataFlowComponent.InnerObject.
                  InputCollection[0].InputColumnCollection)
            {   // create the map
                exColumn = destinationDataFlowComponent.InnerObject.
                      InputCollection[0].
                 ExternalMetadataColumnCollection[inColumn.Name];
                string destName = sqlDataDestination.MapManager.
                      GetDestinationColumn(exColumn.Name).ColumnName;
                exColumn.Name = destName;

                managedOleInstance.MapInputColumn(
                           destinationDataFlowComponent.
                   InnerObject.InputCollection[0].ID,
                           inColumn.ID, exColumn.ID);
            }
            // Now release the connection
            managedOleInstance.ReleaseConnections();

            // Now remove the table that we did create
            // for the SSIS object model
            sqlDataDestination.DeleteDataStore();

            #region Logging
            Logger.WriteInformation

              ("Creating the destination columns and their
                 mappings.....completed");
            #endregion
        }

        #endregion

        /// <summary>
        /// Get the SSIS compatible connection string.
        /// </summary>
        /// <returns>A Connection string that is compatible with
        /// SSIS</returns>
        /// <remarks>
        ///     The SSIS Oledb connections uses a provider different than
        /// usual SQL client provider.
        /// It is "SQLNCLI". Without this provider the SSIS cant
        /// create a connection. (!)
        /// On the other hand this provider (SQLNCLI) cant be used as SQL
        /// Client (.NET class) connections.
        /// So this procedure converts a SQL client connection string to SSIS
        /// compatible connection
        /// string by modifying the provider.
        /// </remarks>
        private string GetSsisConnectionString()
        {
            //connectionManager.ConnectionString =
              "Data Source=VSTS;
            Initial Catalog=TEST;Provider=SQLNCLI;Integrated
                 Security=SSPI;Auto Translate=false;";
            //ConMgr.ConnectionString = "
            // Data Source=VSTS;Initial Catalog=TEST;Integrated
            // Security=True";
            string connectionString
                  = sqlDataDestination.ConnectionString;
                 // get the SQL connection string
            Dictionary<string,
              string> connectionProperties =
                 new Dictionary<string, string>();

            foreach( string part in
            connectionString.Split(";".ToCharArray()))
            {   // Iterate thru the properties of the connection string
                string[] keyValue = part.Split("=".ToCharArray(),
                    StringSplitOptions.RemoveEmptyEntries);
                if (keyValue != null && keyValue.Length == 2)
                {
                    string propertyName = keyValue[0].Trim();
                              // the name of the property
                    string valueName = keyValue[1].Trim();
                              // the value of the property
                    // create the entry
                    connectionProperties.Add(propertyName, valueName);
                }
            }
            // Now update these followings
            connectionProperties["Provider"]
                        = "SQLNCLI";
            connectionProperties["Integrated Security"]
                      = "SSPI";
            connectionProperties["Auto Translate"]
                         = "false";

            // Now we are going to create the SSIS compatible
            // connectionstring

            StringBuilder ssisCompatibaleConnectionString
                     = new StringBuilder();
            for (Dictionary<string, string>.Enumerator iterator
               =
            connectionProperties.GetEnumerator();
                   iterator.MoveNext(); )
            {   // Iterate
                if (ssisCompatibaleConnectionString.Length > 0)
                {   // If already there is some properties added
                    ssisCompatibaleConnectionString.Append(";");
                }
                ssisCompatibaleConnectionString.Append(
                    string.Format("{0}={1}",
                     iterator.Current.Key, iterator.Current.Value));
            }

            return ssisCompatibaleConnectionString.ToString();
        }
    }
}

Along with these core classes for import business, I have also written a Log provider for the SSIS package by which I keep Log into the GUI while the package is executing. Creating a log provider means you have to extend the LogProviderBase class provided by Microsoft. Here is my Log provider class.

namespace ImportLib.IntegrationService.Logging
{
    /// <summary>
    /// A custom log provider class
    /// </summary>
    /// <author>
    ///     Moim Hossain
    /// </author>
    [DtsLogProvider(DisplayName = "LogProvider",

  Description = "
    Log provider for DTS packages.",
               LogProviderType = "Custom")]
    public class EventLogProvider : LogProviderBase
    {
        /// <summary>
        /// Configuration string
        /// </summary>
        public override string ConfigString
        {
            get
            {
                return string.Empty;
            }
            set
            {

            }
        }

        /// <summary>
        /// Open log
        /// </summary>
        public override void OpenLog()
        {
            base.OpenLog();
        }

        /// <summary>
        /// Closing log
        /// </summary>
        public override void CloseLog()
        {
            base.CloseLog();
        }

        /// <summary>
        /// Initializations
        /// </summary>
        /// <param name="connections"></param>
        /// <param name="events"></param>
        /// <param name="refTracker"></param>
        public override void InitializeLogProvider
      (Connections connections,
      IDTSInfoEvents events,
        ObjectReferenceTracker refTracker)
        {
            base.InitializeLogProvider(connections, events, refTracker);
        }

        /// <summary>
        /// Write a log
        /// </summary>
        /// <param name="logEntryName"></param>
        /// <param name="computerName"></param>
        /// <param name="operatorName"></param>
        /// <param name="sourceName"></param>
        /// <param name="sourceID"></param>
        /// <param name="executionID"></param>
        /// <param name="messageText"></param>
        /// <param name="startTime"></param>
        /// <param name="endTime"></param>
        /// <param name="dataCode"></param>
        /// <param name="dataBytes"></param>
        public override void Log(string logEntryName,
               string computerName,
               string operatorName,
               string sourceName,
               string sourceID,
               string executionID, string messageText,
               DateTime startTime,
               DateTime endTime, int dataCode, byte[] dataBytes)
        {
            //base.Log(logEntryName, computerName, operatorName,
            // sourceName, sourceID, executionID,
            // messageText, startTime, endTime, dataCode, dataBytes);

            LogEventArgs e = new
                   LogEventArgs(logEntryName,computerName,messageText);
            //
            OnLogCreated(e);
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnLogCreated(LogEventArgs e)
        {
            LogCreatedDelegate mLogCreated = this.LogCreated;

            if (mLogCreated != null)
            {
                mLogCreated(this, e);
            }
        }

        /// <summary>
        ///
        /// </summary>
        public event LogCreatedDelegate LogCreated;
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public delegate void LogCreatedDelegate ( object sender , LogEventArgs e );
}

Well, the last but not least important thing is the mapping. That means, during the importing task it is possible to import one column's data to another one. So you can provide the mapping between the source and destination column. You can even ignore some source column's data during the importing task. Although, I have not created a GUI for defining the mappings, I have created the mapping from the code. But it is simply a matter of time to create an interactive GUI where use can specify the mappings. Here is the class that encapsulates the mapping information:

namespace ImportLib.Mappings
{
    /// <summary>
    ///     Encapsulates the mapping information for a source data source
    /// and a destination data source
    /// </summary>
    [Serializable()]
    public class ColumnMappingController
    {
        /// <summary>
        /// Creates a new instance
        /// </summary>
        public ColumnMappingController()
        {
            mappings = new List<Map>();
        }

        private List<Map> mappings;

        /// <summary>
        /// Get or set the mappings
        /// </summary>
        public List<Map> Mappings
        {
            get { return mappings; }
            set { mappings = value; }
        }

        private Column[] destinationColumns;

        /// <summary>
        /// Get or set the destination columns
        /// </summary>
        public Column[] DestinationColumns
        {
            get { return destinationColumns; }
            set { destinationColumns = value; }
        }

        /// <summary>
        /// Get the destination columns array where the
        /// columns are not bind to any source columns
        /// </summary>
        public Column[] UnmappedDestinationColumns
        {
            get
            {
                List<Column> unmappedColumns = new List<Column>();
                foreach (Column destinationColumn in destinationColumns)
                {   // iterate
                    if (!ContainsInDestinationMap(destinationColumn))
                    {   // if no mapping found
                        unmappedColumns.Add(destinationColumn);
                    }
                }
                return unmappedColumns.ToArray();
            }
        }

        /// <summary>
        /// Get the source columns where the columns are not bind with any
        /// destination columns
        /// </summary>
        public Column[] SuppressedSourceColumns(DataTable srcSchemaTable)
        {
            List<Column> suppressedColumns = new List<Column>();
            foreach (DataRow row in srcSchemaTable.Rows)
            {   // iterate
                string columnName = row["columnName"] as string;
                if (!ContainsInSourceMap(columnName))
                {   // if no mapping found
                    suppressedColumns.Add(new Column(columnName));
                }
            }
            return suppressedColumns.ToArray();
        }

        /// <summary>
        /// Determine if the specified source column is in suppressed
        /// list or not
        /// </summary>
        public bool IsSuppressedSourceColumn(
               string sourceColumnName, DataTable srcSchemaTable)
        {
            return Array.IndexOf<Column>(
                SuppressedSourceColumns(srcSchemaTable),
                new Column(sourceColumnName)) > -1;
        }

        /// <summary>
        /// Get the destination column for a given source column
        /// </summary>
        /// <param name="sourceColumnName">The
        /// specified source column</param>
        /// <returns>An instance of <see
        /// cref="T:ImportLib.Mappings.Column"/>
        /// which is the destination for the given source column
        /// name.</returns>
        public Column GetDestinationColumn(string sourceColumnName)
        {
            foreach (Map map in mappings)
            {   // iterate
                if (map.SourceColumn.ColumnName.Equals(sourceColumnName))
                    return map.DestinationColumn;
            }
            throw new ApplicationException
            ("No mapping defined for the source column " +
                  sourceColumnName);
        }

        /// <summary>
        /// Determines if the specified column is contains into the
        /// mapping as a source
        /// </summary>
        /// <param name="destinationColumn">The source
        /// column</param>
        /// <returns>
        /// <c>true</c> if the source column found into
        /// the mapping, <c>false</c> otherwise.</returns>
        private bool ContainsInSourceMap(string sourceColumnName)
        {
            foreach (Map map in mappings)
            {   // iterate
                if (map.SourceColumn.ColumnName.Equals(sourceColumnName))
                    return true;
            }
            return false;
        }

        /// <summary>
        /// Determines if the specified column is contains into the
        /// mapping as a destination
        /// </summary>
        /// <param name="destinationColumn">
        /// The destination column</param>
        /// <returns><c>true</c>
        /// if the destination column found into the mapping,
        ///  <c>false</c> otherwise.</returns>
        private bool ContainsInDestinationMap(Column destinationColumn)
        {
            foreach (Map map in mappings)
            {   // iterate
                if (map.DestinationColumn.Equals(destinationColumn))
                    return true;
            }
            return false;
        }

        /// <summary>
        /// Validate the content
        /// </summary>
        private void Validate()
        {
            //if (sourceColumns == null)
             throw
              new NullReferenceException
          ("SourceColumns is not set to an instance of an object");
            if (destinationColumns == null)
               throw
            new NullReferenceException(
             "DestinationColumns is
                 not set to an instance of an object");
        }
    }
}

You will see that when I create an import job I give an instance of this class to the Import manager. So you can either create this instance from the code (like I did) or enable your user to create this from a GUI.

I hope you will enjoy this!

Points of Interest

I have not written any code that can perform any transformations during importing, say you need to perform some work on the data before it is imported into the destination data store. I hope to write some code on that part soon and then I will try to publish an article on that topic.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Moim Hossain
Architect BlueCielo ECM Solutions
Netherlands Netherlands
Just another developer.
Follow on   Twitter

Comments and Discussions

 
GeneralExcel Source - connection manager incorrect type PinmemberHari prasad3-Aug-10 21:15 
GeneralRe: Excel Source - connection manager incorrect type PinmemberMoim Hossain3-Aug-10 21:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140827.1 | Last Updated 25 Jun 2007
Article Copyright 2007 by Moim Hossain
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid