Search code examples
c#.netsql-serversql-server-ce

Programmatically create a SQL Server CE table from DataTable


Does anyone know the best way to create a SQL Server CE (Compact 3.5) table based on the schema of a DataTable at runtime? I don’t want to have to formulate a CREATE TABLE statement based on all the different possible datatypes, etc.

As a bonus – do you then know how to fill it directly from a datatable?


Solution

  • I coded a reasonable solution, but was hoping to avoid case statements for the SQL types:

    Firstly a neat trick to convert from a .NET type to a SqlDBType:

    /// <summary>
    /// Gets the correct SqlDBType for a given .NET type. Useful for working with SQL CE.
    /// </summary>
    /// <param name="type">The .Net Type used to find the SqlDBType.</param>
    /// <returns>The correct SqlDbType for the .Net type passed in.</returns>
    public static SqlDbType GetSqlDBTypeFromType(Type type)
    {
        TypeConverter tc = TypeDescriptor.GetConverter(typeof(DbType));
        if (/*tc.CanConvertFrom(type)*/ true)
        {
            DbType dbType = (DbType)tc.ConvertFrom(type.Name);
            // A cheat, but the parameter class knows how to map between DbType and SqlDBType.
            SqlParameter param = new SqlParameter();
            param.DbType = dbType;
            return param.SqlDbType; // The parameter class did the conversion for us!!
        }
        else
        {
            throw new Exception("Cannot get SqlDbType from: " + type.Name);
        }
    }
    

    A case statement for the types for use in SQL Statements:

        /// <summary>
                /// The method gets the SQL CE type name for use in SQL Statements such as CREATE TABLE
                /// </summary>
                /// <param name="dbType">The SqlDbType to get the type name for</param>
                /// <param name="size">The size where applicable e.g. to create a nchar(n) type where n is the size passed in.</param>
                /// <returns>The SQL CE compatible type for use in SQL Statements</returns>
                public static string GetSqlServerCETypeName(SqlDbType dbType, int size)
                {
                    // Conversions according to: http://msdn.microsoft.com/en-us/library/ms173018.aspx
                    bool max = (size == int.MaxValue) ? true : false;
                    bool over4k = (size > 4000) ? true : false;
    
                    switch (dbType)
                    {
                        case SqlDbType.BigInt:
                            return "bigint";
                        case SqlDbType.Binary:
                            return string.Format("binary ({0})", size);
                        case SqlDbType.Bit:
                            return "bit";
                        case SqlDbType.Char:
                            if (over4k) return "ntext";
                            else return string.Format("nchar({0})", size);
    ETC...
    

    Then finally the CREATE TABLE statement:

        /// <summary>
        /// Genenerates a SQL CE compatible CREATE TABLE statement based on a schema obtained from
        /// a SqlDataReader or a SqlCeDataReader.
        /// </summary>
        /// <param name="tableName">The name of the table to be created.</param>
        /// <param name="schema">The schema returned from reader.GetSchemaTable().</param>
        /// <returns>The CREATE TABLE... Statement for the given schema.</returns>
        public static string GetCreateTableStatement(string tableName, DataTable schema)
        {
            StringBuilder builder = new StringBuilder();
            builder.Append(string.Format("CREATE TABLE [{0}] (\n", tableName));
    
            foreach (DataRow row in schema.Rows)
            {
                string typeName = row["DataType"].ToString();
                Type type = Type.GetType(typeName);
    
                string name = (string)row["ColumnName"];
                int size = (int)row["ColumnSize"];
    
                SqlDbType dbType = GetSqlDBTypeFromType(type);
    
                builder.Append(name);
                builder.Append(" ");
                builder.Append(GetSqlServerCETypeName(dbType, size));
                builder.Append(", ");
            }
    
            if (schema.Rows.Count > 0) builder.Length = builder.Length - 2;
    
            builder.Append("\n)");
            return builder.ToString();
        }