У кого-нибудь есть функция С#, которая сопоставляет тип данных SQL столбца с его эквивалентом CLR?

Я сажусь писать массивный оператор switch(), чтобы преобразовать типы данных SQL в типы данных CLR, чтобы генерировать классы из хранимых процедур MSSQL. Я использую эту диаграмму в качестве справки. Прежде чем я зайду слишком далеко в то, что, вероятно, займет весь день и будет огромной болью для полного тестирования, я хотел бы обратиться к сообществу SO, чтобы узнать, не написал ли кто-нибудь еще или нашел что-то на C # для выполнения этого, казалось бы, распространенного и, конечно, утомительное занятие.


person Chris McCall    schedule 29.06.2009    source источник
comment
В прошлом я реализовал очень похожую систему, но не для C#. Давно собирался построить что-то подобное, но руки пока не дошли. Будет ли ваша реализация закрыта или она может быть с открытым исходным кодом? Потому что я уверен, что я и другие были бы очень заинтересованы в реализации того, что вы описываете, с открытым исходным кодом.   -  person Daniel Pryden    schedule 31.08.2009
comment
Это для коммерческой разработки.   -  person Chris McCall    schedule 31.08.2009
comment
Это могло быть недоступно в 2009 году, но System.Web может иметь то, что требуется здесь: stackoverflow.com/a/28561947/4228193< /а> . try { return Convert.ChangeType(value_to_convert, Parameter.ConvertDbTypeToTypeCode(SqlMetaData_instance.DbT‌​ype); } также доступно: ConvertTypeCodeToDbType (msdn.microsoft.com/en-us/library/)   -  person mpag    schedule 12.12.2017


Ответы (12)


Это тот, который мы используем. Вы можете настроить его (например, обнуляемые/необнуляемые типы и т. д.), но это должно сэкономить вам большую часть ввода.

public static Type GetClrType(SqlDbType sqlType)
{
    switch (sqlType)
    {
        case SqlDbType.BigInt:
            return typeof(long?);

        case SqlDbType.Binary:
        case SqlDbType.Image:
        case SqlDbType.Timestamp:
        case SqlDbType.VarBinary:
            return typeof(byte[]);

        case SqlDbType.Bit:
            return typeof(bool?);

        case SqlDbType.Char:
        case SqlDbType.NChar:
        case SqlDbType.NText:
        case SqlDbType.NVarChar:
        case SqlDbType.Text:
        case SqlDbType.VarChar:
        case SqlDbType.Xml:
            return typeof(string);

        case SqlDbType.DateTime:
        case SqlDbType.SmallDateTime:
        case SqlDbType.Date:
        case SqlDbType.Time:
        case SqlDbType.DateTime2:
            return typeof(DateTime?);

        case SqlDbType.Decimal:
        case SqlDbType.Money:
        case SqlDbType.SmallMoney:
            return typeof(decimal?);

        case SqlDbType.Float:
            return typeof(double?);

        case SqlDbType.Int:
            return typeof(int?);

        case SqlDbType.Real:
            return typeof(float?);

        case SqlDbType.UniqueIdentifier:
            return typeof(Guid?);

        case SqlDbType.SmallInt:
            return typeof(short?);

        case SqlDbType.TinyInt:
            return typeof(byte?);

        case SqlDbType.Variant:
        case SqlDbType.Udt:
            return typeof(object);

        case SqlDbType.Structured:
            return typeof(DataTable);

        case SqlDbType.DateTimeOffset:
            return typeof(DateTimeOffset?);

        default:
            throw new ArgumentOutOfRangeException("sqlType");
    }
}
person Greg Beech    schedule 29.06.2009
comment
Как вы используете этот метод? - person crdx; 11.10.2011
comment
@user457104 user457104 - Эээ... вы называете это как любой другой метод. - person Greg Beech; 12.10.2011
comment
Нет, я имею в виду, я не понимаю, для чего ты его используешь. Используете ли вы возвращаемое значение для динамического создания типа? Используете ли вы его для сравнения с чем-то другим? В коде, в какой ситуации я хотел бы преобразовать SqlDbType в Type? Я уверен, что есть хорошее применение для этого, но я не могу думать об этом. Поэтому и спрашиваю. - person crdx; 13.10.2011
comment
@ user457104 — мы используем его в нашем инструменте, который генерирует классы-оболочки .NET для наших хранимых процедур. Он определяет типы параметров SQL и с помощью этой функции создает оболочку с соответствующим типом CLR. На самом деле это не такой уж распространенный вариант использования. - person Greg Beech; 13.10.2011
comment
Где находится «Иерархид»? - person FrenkyB; 18.03.2015
comment
Я получаю сообщение об ошибке: Статические типы нельзя использовать в качестве параметров. - person Dave; 29.05.2015
comment
Зачем возвращать типы CLR, допускающие значение NULL? - person daehaai; 03.07.2015
comment
Обнуляемые столбцы @activebiz... Вы также можете протестировать столбец на обнуляемость, если хотите по-настоящему оживить его... - person Chris McCall; 30.06.2016

Вот версия, которая принимает значение nullable.

    public static Type GetClrType(SqlDbType sqlType, bool isNullable)
    {
        switch (sqlType)
        {
            case SqlDbType.BigInt:
                return isNullable ? typeof(long?) : typeof(long);

            case SqlDbType.Binary:
            case SqlDbType.Image:
            case SqlDbType.Timestamp:
            case SqlDbType.VarBinary:
                return typeof(byte[]);

            case SqlDbType.Bit:
                return isNullable ? typeof(bool?) : typeof(bool);

            case SqlDbType.Char:
            case SqlDbType.NChar:
            case SqlDbType.NText:
            case SqlDbType.NVarChar:
            case SqlDbType.Text:
            case SqlDbType.VarChar:
            case SqlDbType.Xml:
                return typeof(string);

            case SqlDbType.DateTime:
            case SqlDbType.SmallDateTime:
            case SqlDbType.Date:
            case SqlDbType.Time:
            case SqlDbType.DateTime2:
                return isNullable ? typeof(DateTime?) : typeof(DateTime);

            case SqlDbType.Decimal:
            case SqlDbType.Money:
            case SqlDbType.SmallMoney:
                return isNullable ? typeof(decimal?) : typeof(decimal);

            case SqlDbType.Float:
                return isNullable ? typeof(double?) : typeof(double);

            case SqlDbType.Int:
                return isNullable ? typeof(int?) : typeof(int);

            case SqlDbType.Real:
                return isNullable ? typeof(float?) : typeof(float);

            case SqlDbType.UniqueIdentifier:
                return isNullable ? typeof(Guid?) : typeof(Guid);

            case SqlDbType.SmallInt:
                return isNullable ? typeof(short?) : typeof(short);

            case SqlDbType.TinyInt:
                return isNullable ? typeof(byte?) : typeof(byte);

            case SqlDbType.Variant:
            case SqlDbType.Udt:
                return typeof(object);

            case SqlDbType.Structured:
                return typeof(DataTable);

            case SqlDbType.DateTimeOffset:
                return isNullable ? typeof(DateTimeOffset?) : typeof(DateTimeOffset);

            default:
                throw new ArgumentOutOfRangeException("sqlType");
        }
    }
person Tom    schedule 22.09.2015

Вам не нужна функция. Я думаю, вы ищете

dt.Columns[i].DataType.UnderlyingSystemType

dt — таблица данных

Это вернет тип CLR для соответствующего столбца. Надеюсь, это поможет, и, кстати, это мой первый ответ на переполнение стека

person dsyed    schedule 24.11.2015

Это не дает прямого ответа на заданный вопрос, но отвечает на общий связанный вопрос. Получив IDataReader, вы можете вызвать IDataRecord.GetFieldType(int) чтобы «[получить] информацию Type, соответствующую типу Object, которая будет возвращена из GetValue».

person Doug McClean    schedule 29.06.2009
comment
Если у вас есть объект, который был прочитан из поля, вы также можете использовать SqlMetaData.InferFromValue (msdn.microsoft.com/en-us/library/ для определения его типа. - person adrianbanks; 29.06.2009

Я включаю это расширение (вы можете легко заменить строковый ключ в словаре на SqlDbType, как это реализовал Грег, или даже поддерживать оба) в мою модель и предоставить свойство, которое преобразует тип CLR:

    namespace X.Domain.Model
    {
        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Text;
        public class StoredProcedureParameter : DomainObject
        {
            public StoredProcedureParameter() { }

            public string StoredProcedure { get; set; }

            public string ProcedureSchema { get; set; }

            public string ProcedureName { get; set; }

            public string ParameterName { get; set; }

            public string ParameterOrder { get; set; }

            public string ParameterMode { get; set; }

            public string SqlDataType { get; set; }

            public Type DataType { get { return this.SqlDataType.ToClrType(); } }
        }

        static class StoredProcedureParameterExtensions
        {
            private static Dictionary<string, Type> Mappings;
            public static StoredProcedureParameterExtensions()
            {
                Mappings = new Dictionary<string, Type>();
                Mappings.Add("bigint", typeof(Int64));
                Mappings.Add("binary", typeof(Byte[]));
                Mappings.Add("bit", typeof(Boolean));
                Mappings.Add("char", typeof(String));
                Mappings.Add("date", typeof(DateTime));
                Mappings.Add("datetime", typeof(DateTime));
                Mappings.Add("datetime2", typeof(DateTime));
                Mappings.Add("datetimeoffset", typeof(DateTimeOffset));
                Mappings.Add("decimal", typeof(Decimal));
                Mappings.Add("float", typeof(Double));
                Mappings.Add("image", typeof(Byte[]));
                Mappings.Add("int", typeof(Int32));
                Mappings.Add("money", typeof(Decimal));
                Mappings.Add("nchar", typeof(String));
                Mappings.Add("ntext", typeof(String));
                Mappings.Add("numeric", typeof(Decimal));
                Mappings.Add("nvarchar", typeof(String));
                Mappings.Add("real", typeof(Single));
                Mappings.Add("rowversion", typeof(Byte[]));
                Mappings.Add("smalldatetime", typeof(DateTime));
                Mappings.Add("smallint", typeof(Int16));
                Mappings.Add("smallmoney", typeof(Decimal));
                Mappings.Add("text", typeof(String));
                Mappings.Add("time", typeof(TimeSpan));
                Mappings.Add("timestamp", typeof(Byte[]));
                Mappings.Add("tinyint", typeof(Byte));
                Mappings.Add("uniqueidentifier", typeof(Guid));
                Mappings.Add("varbinary", typeof(Byte[]));
                Mappings.Add("varchar", typeof(String));

            }

            public static Type ToClrType(this string sqlType)
            {
                Type datatype = null;
                if (Mappings.TryGetValue(sqlType, out datatype))
                    return datatype;
                throw new TypeLoadException(string.Format("Can not load CLR Type from {0}", sqlType));
            }
        }
    }
person jared.g    schedule 29.10.2013

Вы можете попробовать Wizardby. Однако он сопоставляет так называемые «собственные» типы данных с DbType, которые затем легко преобразовать в типы CLR. Если это подходит, вам понадобится соответствующий IDbTypeMapper — либо SqlServer2000TypeMapper, либо SqlServer2005TypeMapper.

person Anton Gogolev    schedule 29.06.2009

Я думаю, что для этого нет встроенных функций, но вы можете использовать VS для создания классов для своих таблиц, а затем попытаться их отредактировать.

person Ahmed Said    schedule 29.06.2009

Почему бы не создать типизированный набор данных, а дизайнер VS сделает сопоставление за вас? Если проект не должен адаптироваться во время выполнения к различным схемам, вам следует использовать методы генерации кода для создания своих классов, будь то встроенные конструкторы (т. е. типизированные наборы данных) или пользовательские (схема->XML->XSLT->. cs).

person Remus Rusanu    schedule 29.06.2009
comment
Он должен адаптироваться к различным схемам. Я пишу генератор, который принимает имя SP и создает класс C# из входных и выходных параметров с использованием SQL-DMO (SQL 2000). - person Chris McCall; 29.06.2009
comment
Это тоже правильный подход. Грег уже дал хороший ответ. Обычно я бы рекомендовал также рассмотреть возможность использования типов, ориентированных на поток, для больших типов (n/varchar/varbinary(max) и xml), но вы говорите, что используете SQL2K, поэтому это не применимо). - person Remus Rusanu; 29.06.2009

Обычно я просто использую свойство Value для преобразования SqlType в собственный тип .NET. Это делает работу большую часть времени. Если у меня есть угловой случай, я просто напишу быструю вспомогательную функцию.

int i = dataReader.GetSqlInt32(0).Value;
person Aaron Daniels    schedule 29.06.2009

Я понимаю, что вы обсуждаете написание оператора switch, но вот альтернатива для Sql Server (аналогичные концепции работают для других БД)

Рассмотрите возможность использования SysObjects для получения полных типов данных и создания вашего класса:

declare @ProcName varchar(255)
select @ProcName='Table, View, or Proc'
SELECT --DISTINCT 
    b.name 
    , c.name Type
    , b.xtype
    , b.length 
    , b.isoutparam
FROM 
    sysObjects a 
INNER JOIN sysCOLUMNs b ON a.id=b.id 
INNER JOIN systypes c ON b.xtype=c.xtype  
WHERE 
    a.name=@ProcName
order by b.colorder

Теперь вы просто перечисляете DataTable вместо более длинного оператора.

person Community    schedule 09.09.2009

person    schedule
comment
Единственный способ обработки данных — иметь возможность обрабатывать метаданные. Чтобы иметь возможность обрабатывать метаданные, нужно уметь использовать его данные. Чтобы иметь возможность использовать свои данные, они должны быть правильно описаны... - person Yordan Georgiev; 08.06.2011
comment
Я не могу поверить, что никто не заметил этого за последние 4,5 года, но bigint SQL не эквивалентен короткому C#. Я думаю, вы имели в виду long (также известный как Int64)! - person ubercam; 29.10.2014
comment
Есть ли причина, по которой varchar имеет типы данных NULL? - person tbone; 02.06.2015
comment
Хорошая точка зрения. Единственная причина, которую я мог придумать, это просто тот факт, что код был в статусе work in progress - то есть без тестирования сейчас я изменил его на NOT NULL по интуиции... если только кто-нибудь не обосновывает, почему этого не следует делать... - person Yordan Georgiev; 02.06.2015
comment
Хорошая таблица, но отсутствует один столбец MSSQL system_type_id - person user1325696; 09.09.2015

person    schedule
comment
Кто-нибудь знает, Dictionary.TryGetValue() этот подход, показанный здесь, быстрее или медленнее, чем switch...case подход, показанный в другом месте этой темы? - person Dib; 23.10.2015