static void

SchemaConverter

An experiment in using ADO 2.0 DbProviderFactories for SQLServer/Oracle/MySql/SQLite/Firebird.

Now a CodePlex project - see there for latest code.

Reads the DataTables created by SchemaReader into plain objects (Table, Column ...). Used by DatabaseReader. There are similar converters for constraints, indexes, procedures and procedure arguments.

using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
 
namespace Library.Data.DataSchema
{
    /// <summary>
    /// Converts the DataTables returned by DbConnection.GetSchema into objects.
    /// Resolves differences between SqlServer and Oracle names
    /// </summary>
    /// <remarks>
    /// SqlServer: http://msdn.microsoft.com/en-us/library/ms254969.aspx
    /// Oracle: http://msdn.microsoft.com/en-us/library/cc716723.aspx
    /// Also supports OleDb, MySql, SQLite, DataDirect and Devart providers
    /// </remarks>
    public static class SchemaConverter
    {
 
        /// <summary>
        /// Converts the "Tables" DataTable into <see cref="DatabaseTable"/> objects.
        /// </summary>
        /// <remarks>
        /// Note the SqlServer DataTable includes views, which we explicitly remove.
        /// </remarks>
        public static List<DatabaseTable> Tables(DataTable dt)
        {
            List<DatabaseTable> list = new List<DatabaseTable>();
            //sql server
            string key = "TABLE_NAME";
            string ownerKey = "TABLE_SCHEMA";
            string typeKey = "TABLE_TYPE";
            //oracle
            if (!dt.Columns.Contains(ownerKey)) ownerKey = "OWNER";
            if (!dt.Columns.Contains(typeKey)) typeKey = "TYPE";
            //Devart.Data.Oracle - TABLE_NAME is NAME
            if (!dt.Columns.Contains(key)) key = "NAME";
            if (!dt.Columns.Contains(ownerKey)) ownerKey = "SCHEMA";
 
            foreach (DataRow row in dt.Rows)
            {
                string type = row[typeKey].ToString();
                //Sql server has base tables and views. Oracle has system and user
                if (!type.Equals("TABLE", StringComparison.OrdinalIgnoreCase) &&
                    !type.Equals("BASE TABLE", StringComparison.OrdinalIgnoreCase) &&
                   !type.Equals("User", StringComparison.OrdinalIgnoreCase)) continue;
                DatabaseTable t = new DatabaseTable();
                t.Name = row[key].ToString();
                //exclude Oracle bin tables
                if (t.Name.StartsWith("BIN$", StringComparison.OrdinalIgnoreCase)) continue;
                t.SchemaOwner = row[ownerKey].ToString();
                list.Add(t);
            }
            return list;
        }
 
        /// <summary>
        /// Converts the "Views" DataTable into <see cref="DatabaseView"/> objects.
        /// </summary>
        public static List<DatabaseView> Views(DataTable dt)
        {
            List<DatabaseView> list = new List<DatabaseView>();
            string key = "TABLE_NAME"; //yep, it's Table_Name in SqlServer.
            string ownerKey = "TABLE_SCHEMA";
            string definition = "TEXT";
            //firebird
            if (!dt.Columns.Contains(ownerKey)) ownerKey = "VIEW_SCHEMA"; //always null
            if (!dt.Columns.Contains(definition)) definition = "DEFINITION";
            //oracle
            if (!dt.Columns.Contains(key)) key = "VIEW_NAME";
            if (!dt.Columns.Contains(ownerKey)) ownerKey = "OWNER";
            //Oracle does not expose ViewColumns, only the raw sql.
            bool hasSql = dt.Columns.Contains(definition);
            //Devart.Data.Oracle
            if (!dt.Columns.Contains(key)) key = "NAME";
            if (!dt.Columns.Contains(ownerKey)) ownerKey = "SCHEMA";
 
            foreach (DataRow row in dt.Rows)
            {
                DatabaseView t = new DatabaseView();
                t.Name = row[key].ToString();
                t.SchemaOwner = row[ownerKey].ToString();
                if (hasSql) t.Sql = row[definition].ToString();
                list.Add(t);
            }
            return list;
        }
 
        /// <summary>
        /// Converts the "Columns" DataTable into <see cref="DatabaseColumn"/> objects
        /// </summary>
        public static List<DatabaseColumn> Columns(DataTable dt)
        {
            return Columns(dt, null);
        }
 
        /// <summary>
        /// Converts the "Columns" DataTable into <see cref="DatabaseColumn"/> objects for a specified table
        /// </summary>
        public static List<DatabaseColumn> Columns(DataTable dt, string tableName)
        {
            List<DatabaseColumn> list = new List<DatabaseColumn>();
            //sql server
            const string key = "column_name";
            const string tableKey = "table_name";
            string ordinalKey = "ordinal_position";
            string datatypeKey = "data_type";
            string nullableKey = "is_nullable";
            string lengthKey = "character_maximum_length";
            string precisionKey = "numeric_precision";
            string scaleKey = "numeric_scale";
            string dateTimePrecision = "datetime_precision";
            string defaultKey = "column_default";
            //oracle
            if (!dt.Columns.Contains(ordinalKey)) ordinalKey = "id";
            if (!dt.Columns.Contains(datatypeKey)) datatypeKey = "datatype";
            if (!dt.Columns.Contains(nullableKey)) nullableKey = "nullable";
            if (!dt.Columns.Contains(lengthKey)) lengthKey = "length";
            if (!dt.Columns.Contains(precisionKey)) precisionKey = "precision";
            if (!dt.Columns.Contains(scaleKey)) scaleKey = "scale";
            if (!dt.Columns.Contains(dateTimePrecision)) dateTimePrecision = null;
            if (!dt.Columns.Contains(defaultKey)) defaultKey = null; //not in Oracle catalog
            //sqlite
            string autoIncrementKey = "AUTOINCREMENT";
            string primaryKeyKey = "PRIMARY_KEY";
            string uniqueKey = "UNIQUE";
            if (!dt.Columns.Contains(autoIncrementKey)) autoIncrementKey = null;
            if (!dt.Columns.Contains(primaryKeyKey)) primaryKeyKey = null;
            if (!dt.Columns.Contains(uniqueKey)) uniqueKey = null;
            //firebird
            if (!dt.Columns.Contains(datatypeKey)) datatypeKey = "column_data_type";
            if (!dt.Columns.Contains(lengthKey)) lengthKey = "COLUMN_SIZE";
 
            dt.DefaultView.Sort = ordinalKey;
            //this could be more than one table, so filter the view
            if (!string.IsNullOrEmpty(tableName))
                dt.DefaultView.RowFilter = "[" + tableKey + "] = '" + tableName + "'";
 
            foreach (DataRowView row in dt.DefaultView)
            {
                DatabaseColumn c = new DatabaseColumn();
                c.Name = row[key].ToString();
                c.TableName = row[tableKey].ToString();
                c.Ordinal = Convert.ToInt32(row[ordinalKey], CultureInfo.CurrentCulture);
                c.DBDataType = row[datatypeKey].ToString();
 
                string nullable = row[nullableKey].ToString();
                //could be Y, YES, N, NO, true, false.
                if (nullable.StartsWith("Y", StringComparison.OrdinalIgnoreCase)) //Y or YES
                    c.Nullable = true;
                else if (nullable.StartsWith("N", StringComparison.OrdinalIgnoreCase)) //N or NO
                    c.Nullable = false;
                //sqlite has a boolean type
                else if ((bool)row[nullableKey])
                    c.Nullable = true;
                //the length unless it's an OleDb blob or clob
                c.Length = GetNullableInt(row[lengthKey]);
                c.Precision = GetNullableInt(row[precisionKey]);
                c.Scale = GetNullableInt(row[scaleKey]);
                if (dateTimePrecision != null)
                {
                    c.DateTimePrecision = GetNullableInt(row[dateTimePrecision]);
                }
 
                if (!string.IsNullOrEmpty(defaultKey))
                {
                    string d = row[defaultKey].ToString();
                    if (!string.IsNullOrEmpty(d)) c.DefaultValue = d.Trim(new[] { ' ', '\'', '=' });
                }
                if (!string.IsNullOrEmpty(primaryKeyKey) && (bool)row[primaryKeyKey])
                    c.IsPrimaryKey = true;
                if (!string.IsNullOrEmpty(autoIncrementKey) && (bool)row[autoIncrementKey])
                    c.IsIdentity = true;
                if (!string.IsNullOrEmpty(uniqueKey) && (bool)row[uniqueKey])
                    c.IsUniqueKey = true;
 
                list.Add(c);
            }
            return list;
        }
 
         public static List<DataType> DataTypes(DataTable dataTable)
        {
            List<DataType> list = new List<DataType>();
            if (dataTable == null || dataTable.Rows.Count == 0) return list;
 
            foreach (DataRow row in dataTable.Rows)
            {
                DataType d = new DataType();
                d.TypeName = row["TypeName"].ToString();
                d.NetDataType = row["DataType"].ToString();
                d.ProviderDbType = Convert.ToInt32(row["ProviderDbType"], CultureInfo.InvariantCulture);
                d.LiteralPrefix = row["LiteralPrefix"].ToString();
                d.LiteralSuffix = row["LiteralSuffix"].ToString();
                d.CreateFormat = row["CreateFormat"].ToString();
                list.Add(d);
            }
            return list;
        }
 
        private static int? GetNullableInt(object o)
        {
            try
            {
                return (o != DBNull.Value) ? Convert.ToInt32(o, CultureInfo.CurrentCulture) : (int?)null;
            }
            catch (OverflowException)
            {
                //this occurs for blobs and clobs using the OleDb provider
                return -1;
            }
        }
    }
}