Skip to main content

A SQL CLR C# stored procedure to return results fro a DataTable. Inconveniently, there is no built-in way of translating from System.Data class to Microsoft.SqlServer.Server classes. So here it is.

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Text;
using Microsoft.SqlServer.Server;
using Vielife.Amr.DataAccess;
using Vielife.Amr.DataAccess.Interfaces;

namespace Conchango.SqlServer.SqlClrToolkit
{
    /// <summary>
    /// Holds the C# stored proc for calculating measures
    /// </summary>
    public static class SqlPipeUtil
    {
        /// <summary>
        /// Send some data over the SqlPipe
        /// </summary>
        /// <param name="tbl">The data to send</param>
        private static void SendDataTableOverPipe(DataTable tbl)
        {
            // Build our record schema
            List<SqlMetaData> outputColumns = new List<SqlMetaData>(tbl.Columns.Count);
            foreach(DataColumn col in tbl.Columns)
            {
                SqlMetaData OutputColumn = new SqlMetaData(col.ColumnName,
                    TypeConverter.ToSqlDbType(col.DataType), col.MaxLength);

                outputColumns.Add(OutputColumn);
            }

            // Build our SqlDataRecord and start the results
            SqlDataRecord record = new SqlDataRecord(outputColumns.ToArray());
            SqlContext.Pipe.SendResultsStart(record);

            // Now send all the rows
            foreach (DataRow row in tbl.Rows)
            {
                for (int col = 0; col < tbl.Columns.Count; col++)
                {
                    record.SetValue(col, row.ItemArray[col]);
                }
                SqlContext.Pipe.SendResultsRow(record);
            }

            // And complete the results
            SqlContext.Pipe.SendResultsEnd();
        }

        /// <summary>
        /// Private ctor
        /// </summary>
        private SqlPipeUtils()
        {
        }
    }

    /// <summary>
    /// Convert a base data type to another base data type
    /// </summary>
    /// <remarks>
    /// Based on code from http://dotnetpulse.blogspot.com/2006/04/convert-net-type-to-sqldbtype-or.html
    /// </remarks>
    public sealed class TypeConverter
    {
        private struct DbTypeMapEntry
        {
            public Type Type;
            public DbType DbType;
            public SqlDbType SqlDbType;

            public DbTypeMapEntry(Type type, DbType dbType, SqlDbType sqlDbType)
            {
                this.Type = type;
                this.DbType = dbType;
                this.SqlDbType = sqlDbType;
            }
        };

        private readonly static List<DbTypeMapEntry> _DbTypeList = new List<DbTypeMapEntry>();

        #region Constructors

        static TypeConverter()
        {
            DbTypeMapEntry dbTypeMapEntry = new DbTypeMapEntry(typeof(bool), DbType.Boolean, SqlDbType.Bit);
            _DbTypeList.Add(dbTypeMapEntry);

            dbTypeMapEntry= new DbTypeMapEntry(typeof(byte), DbType.Double, SqlDbType.TinyInt);
            _DbTypeList.Add(dbTypeMapEntry);

            dbTypeMapEntry = new DbTypeMapEntry(typeof(byte[]), DbType.Binary, SqlDbType.Image);
            _DbTypeList.Add(dbTypeMapEntry);

            dbTypeMapEntry = new DbTypeMapEntry(typeof(DateTime), DbType.DateTime, SqlDbType.DateTime);
            _DbTypeList.Add(dbTypeMapEntry);

            dbTypeMapEntry = new DbTypeMapEntry(typeof(Decimal), DbType.Decimal, SqlDbType.Decimal);
            _DbTypeList.Add(dbTypeMapEntry);

            dbTypeMapEntry = new DbTypeMapEntry(typeof(double), DbType.Double, SqlDbType.Float);
            _DbTypeList.Add(dbTypeMapEntry);

            dbTypeMapEntry  = new DbTypeMapEntry(typeof(Guid), DbType.Guid, SqlDbType.UniqueIdentifier);
            _DbTypeList.Add(dbTypeMapEntry);

            dbTypeMapEntry = new DbTypeMapEntry(typeof(Int16), DbType.Int16, SqlDbType.SmallInt);
            _DbTypeList.Add(dbTypeMapEntry);

            dbTypeMapEntry = new DbTypeMapEntry(typeof(Int32), DbType.Int32, SqlDbType.Int);
            _DbTypeList.Add(dbTypeMapEntry);

            dbTypeMapEntry = new DbTypeMapEntry(typeof(Int64), DbType.Int64, SqlDbType.BigInt);
            _DbTypeList.Add(dbTypeMapEntry);

            dbTypeMapEntry = new DbTypeMapEntry(typeof(object), DbType.Object, SqlDbType.Variant);
            _DbTypeList.Add(dbTypeMapEntry);

            dbTypeMapEntry = new DbTypeMapEntry(typeof(string), DbType.String, SqlDbType.VarChar);
            _DbTypeList.Add(dbTypeMapEntry);

            dbTypeMapEntry = new DbTypeMapEntry(typeof(string), DbType.String, SqlDbType.NVarChar);
            _DbTypeList.Add(dbTypeMapEntry);
        }

        private TypeConverter()
        {
        }

        #endregion

        #region Methods

        /// <summary>
        /// Convert db type to .Net data type
        /// </summary>
        /// <param name="dbType"></param>
        /// <returns></returns>
        public static Type ToNetType(DbType dbType)
        {
            DbTypeMapEntry entry = Find(dbType);
            return entry.Type;
        }

        /// <summary>
        /// Convert TSQL type to .Net data type
        /// </summary>
        /// <param name="sqlDbType"></param>
        /// <returns></returns>
        public static Type ToNetType(SqlDbType sqlDbType)
        {
            DbTypeMapEntry entry = Find(sqlDbType);
            return entry.Type;
        }

        /// <summary>
        /// Convert .Net type to Db type
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static DbType ToDbType(Type type)
        {
            DbTypeMapEntry entry = Find(type);
            return entry.DbType;
        }

        /// <summary>
        /// Convert TSQL data type to DbType
        /// </summary>
        /// <param name="sqlDbType"></param>
        /// <returns></returns>
        public static DbType ToDbType(SqlDbType sqlDbType)
        {
            DbTypeMapEntry entry = Find(sqlDbType);
            return entry.DbType;
        }

        /// <summary>
        /// Convert .Net type to TSQL data type
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static SqlDbType ToSqlDbType(Type type)
        {
            DbTypeMapEntry entry = Find(type);
            return entry.SqlDbType;
        }

        /// <summary>
        /// Convert DbType type to TSQL data type
        /// </summary>
        /// <param name="dbType"></param>
        /// <returns></returns>
        public static SqlDbType ToSqlDbType(DbType dbType)
        {
            DbTypeMapEntry entry = Find(dbType);
            return entry.SqlDbType;
        }

        private static DbTypeMapEntry Find(Type type)
        {
            foreach (DbTypeMapEntry entry in _DbTypeList)
            {
                if (entry.Type == type)
                {
                    return entry;
                }
            }

            throw new ApplicationException("Referenced an unsupported Type");
        }

        private static DbTypeMapEntry Find(DbType dbType)
        {
            foreach (DbTypeMapEntry entry in _DbTypeList)
            {
                if (entry.DbType == dbType)
                {
                    return entry;
                }
            }

            throw new ApplicationException("Referenced an unsupported DbType");
        }

        private static DbTypeMapEntry Find(SqlDbType sqlDbType)
        {
            foreach(DbTypeMapEntry entry in _DbTypeList)
            {
                if (entry.SqlDbType == sqlDbType)
                {
                    return entry;
                }
            }

            throw new ApplicationException("Referenced an unsupported SqlDbType");
        }

        #endregion
    }
}