Требования определяемого пользователем типа данных

При создании определяемого пользователем типа (user-defined type, UDT) для его установки в Microsoft SQL Server нужно решить ряд важных технических вопросов. В большинстве случаев рекомендуется создавать определяемый пользователем тип как структуру, хотя можно создавать его и в виде класса. Чтобы определяемый пользователем тип можно было зарегистрировать в SQL Server, его определение должно соответствовать спецификациям на создание определяемого пользователем типа.

Требования к реализации определяемого пользователем типа

Чтобы определяемый пользователем тип работал в SQL Server, его определение должно удовлетворять следующим условиям.

Определяемый пользователем тип должен задавать атрибут Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute. Использование System.SerializableAttribute необязательно, но рекомендуется.

  • Структура или класс определяемого пользователем типа должны реализовать интерфейс System.Data.SqlTypes.INullable посредством создания метода Null типа static (в Microsoft Visual Basic — Shared). SQL Server по умолчанию может работать со значениями типа NULL. Это необходимо, чтобы программный код, принадлежащий определяемому пользователем типу, умел распознавать значения NULL.

  • Определяемый пользователем тип должен содержать общедоступный метод типа static (или Shared) Parse, поддерживающий преобразование объекта из строки с помощью синтаксического анализа, и общедоступный метод ToString для преобразования объекта в строку.

  • Определяемый пользователем тип, имеющий заданный пользователем формат сериализации, должен реализовать интерфейс System.Data.IBinarySerialize и предоставлять методы Read и Write.

  • Определяемый пользователем тип должен реализовать интерфейс System.Xml.Serialization.IXmlSerializable, либо все его общедоступные поля и свойства должны быть XML-сериализуемы или содержать атрибут XmlIgnore, если требуется переопределить стандартную сериализацию.

  • У объекта определяемого пользователем типа может быть только одна сериализация. Если программы сериализации или десериализации обнаружат несколько различных представлений конкретного объекта, проверка закончится ошибкой.

  • Для побайтовых сравнений атрибут SqlUserDefinedTypeAttribute.IsByteOrdered должен иметь значение true. Если интерфейс IComparable не реализован, а атрибут SqlUserDefinedTypeAttribute.IsByteOrdered имеет значение false, то побайтовые сравнения завершатся ошибкой.

  • Определяемый пользователем тип, заданный в виде класса, должен иметь общедоступный конструктор без аргументов. Дополнительно можно создать перегруженные конструкторы класса.

  • Определяемый пользователем тип должен предоставлять доступ к элементам данных как к общедоступным полям или процедурам свойств.

  • Общедоступные имена не должны быть длиннее 128 символов и должны подчиняться правилам именования SQL Server для идентификаторов, как описано в разделе Идентификаторы.

  • Столбцы sql_variant не могут содержать экземпляры определяемого пользователем типа.

  • Унаследованные элементы недоступны из Transact-SQL, потому что система типов SQL Server ничего не знает об иерархии наследования среди определяемых пользователем типов. Однако можно использовать наследование при определении структуры классов и можно вызывать такие методы в реализации этих типов с помощью управляемого кода.

  • Элементы структуры или класса нельзя перегружать, кроме конструктора класса. Если создан перегруженный метод, при регистрации сборки или создании типа в SQL Server ошибка не возникает. Определение наличия перегруженного метода происходит во время выполнения, а не при создании типа. Класс может иметь перегруженные методы при условии, что они не вызываются. При вызове перегруженного метода возникает ошибка.

  • Любые элементы, объявленные как static (или Shared) должны быть объявлены константами или данными только для чтения. Элементы, объявленные как статические, нельзя изменять.

  • Если для поля SqlUserDefinedTypeAttribute.MaxByteSize задано значение -1, ограничение на размер сериализованного определяемого пользователем типа такое же, как и для объектов LOB (в настоящее время — 2 ГБ). Размер определяемого пользователем типа не может превышать величины, заданной в поле MaxByteSized.

ПримечаниеПримечание

По желанию можно реализовать интерфейс System.IComparable, предоставляющий единственный метод CompareTo, хотя сервер не использует этот метод для сравнения. Он используется на клиенте в ситуациях, когда нужно провести точное сравнение или сортировку значений определяемого пользователем типа.

Собственная сериализация

Выбор атрибутов сериализации при создании определяемого пользователем типа зависит от его типа. Формат сериализации Native использует очень простую структуру, позволяющую SQL Server хранить определяемый пользователем тип на диске в эффективном собственном формате. Формат Native рекомендуется для простых определяемых пользователем типов, содержащих только поля следующих типов:

bool, byte, sbyte, short, ushort, int, uint, long, ulong, float, double, SqlByte, SqlInt16, SqlInt32, SqlInt64, SqlDateTime, SqlSingle, SqlDouble, SqlMoney, SqlBoolean

Для типов, содержащих поля перечисленных выше типов, хорошо подойдет также формат Native, например для структур (structs) в языке Visual C# (или Structures, как они называются в языке Visual Basic). Например, определяемый пользователем тип, для которого указан формат сериализации Native, может содержать в качестве поля другой определяемый пользователем тип, для которого также указан формат сериализации Native. Для более сложных определяемых пользователем типов, содержащих типы данных, которые не вошли в приведенный список, нужно вместо этого задать формат сериализации UserDefined.

Формат Native накладывает следующие ограничения.

  • Параметру Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute.MaxByteSize не должно быть задано значение.

  • Все поля должны быть сериализуемыми.

  • Если определяемый пользователем тип задан в виде класса, а не структуры, нужно задать для атрибута System.Runtime.InteropServices.StructLayoutAttribute значение StructLayout.LayoutKindSequential. Этот атрибут управляет физической компоновкой полей данных. Он заставляет члены структуры располагаться в памяти в том порядке, в каком они описаны. SQL Server использует этот атрибут для задания порядка полей в определяемом пользователем типе с несколькими значениями.

Примером определяемого пользователем типа, для которого задана сериализация типа Native, является тип Point в разделе Разработка кода для определяемых пользователем типов.

Сериализация, заданная пользователем

Настройка формата UserDefined у атрибута Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute предоставляет разработчику полный доступ к двоичным форматам. При задании для свойства атрибута Format значения UserDefined нужно проделать с программным кодом следующее.

  • Указать необязательное свойство атрибута IsByteOrdered. Значение по умолчанию — false.

  • Задать свойство MaxByteSize атрибута Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute.

  • Написать программный код, реализующий методы Read и Write данного определяемого пользователем типа, реализовав интерфейс System.Data.Sql.IBinarySerialize.

Примером определяемого пользователем типа, для которого задана сериализация типа UserDefined, является тип Currency в разделе Разработка кода для определяемых пользователем типов.

ПримечаниеПримечание

Начиная с версии RTM SQL Server 2005, определяемые пользователем типы среды CLR с задаваемой пользователем сериализацией могут иметь индексированные поля как часть материализованных вычисляемых столбцов или представлений. В подобных ситуациях недетерминированная сериализация или десериализация определяемого пользователем типа может повредить индекс. Поэтому она была удалена из SQL Server 2005 с пакетом обновления 1 (SP1). Чтобы индексировать поля определяемого пользователем типа в SQL Server 2005 с пакетом обновления 1 (SP1), они должны или использовать собственную сериализацию, или быть сохраняемыми. Все существующие индексы полей определяемого пользователем типа должны продолжать функционировать как раньше.

Атрибуты сериализации

Атрибуты определяют, каким образом сериализация используется для создания хранимых представлений определяемых пользователем типов, а также для передачи таких типов клиенту по значению. Задание атрибута Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute обязательно при создании определяемого пользователем типа. Атрибут Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute указывает, что класс является определяемым пользователем типом, и задает тип хранения для определяемого пользователем типа. По желанию можно задать атрибут Serializable, хотя SQL Server этого не требует.

Атрибут Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute имеет следующие свойства.

  • Format
    Задает формат сериализации — Native или UserDefined, в зависимости от типов данных, содержащихся в определяемом пользователем типе.

  • IsByteOrdered
    Значение типа Boolean, определяющее, как SQL Server проводит двоичные сравнения значений данного типа.

  • IsFixedLength
    Указывает, имеют ли все экземпляры данного определяемого пользователем типа одинаковую длину.

  • MaxByteSize
    Максимальный размер экземпляра в байтах. Необходимо указать MaxByteSize с форматом сериализации UserDefined. Для определяемого пользователем типа, для которого задана определяемая пользователем сериализация, MaxByteSize означает общий размер этого определяемого пользователем типа в сериализованном виде, как указано пользователем. Значение MaxByteSize должно лежать в диапазоне от 1 до 8000; можно установить для него значение -1, чтобы указать, что размер определяемого пользователем типа больше 8000 байтов (общий размер не может превышать максимального размера LOB). Рассмотрим определяемый пользователем тип со свойством строкового типа длиной 10 символов (System.Char). Когда определяемый пользователем тип сериализуется с помощью BinaryWriter, общий размер сериализованной строки будет равен 22 байтам: 2 байта на символ в Юникоде (UTF-16), умноженные на максимальное количество символов, плюс 2 управляющих байта, добавляемые при сериализации двоичного потока. Поэтому при определении значения MaxByteSize следует учитывать общий размер сериализованного определяемого пользователем типа: размер данных, сериализованных в двоичной форме, плюс служебные данные, добавляемые при сериализации.

  • ValidationMethodName
    Имя метода, используемого для проверки экземпляров определяемого пользователем типа.

Свойство IsByteOrdered

Когда свойство Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute.IsByteOrdered имеет значение true, это дает гарантию, что сериализованные двоичные данные можно использовать для семантического упорядочивания информации. Таким образом, каждый экземпляр объекта побайтно упорядоченного определяемого пользователем типа может иметь лишь одно сериализованное представление. Когда SQL Server проводит операцию сравнения на сериализованных байтах, ее результаты должны быть такими же, как если бы та же операция сравнения проводилась в управляемом коде. Если параметру IsByteOrdered присвоено значение true, поддерживаются также следующие функции.

  • Создание индексов для столбцов этого типа.

  • Создание первичных и внешних ключей, а также ограничений CHECK и UNIQUE для столбцов этого типа.

  • Использование предложений Transact-SQL ORDER BY, GROUP BY и PARTITION BY. В этих случаях для определения порядка используется двоичное представление типа.

  • Использование операторов сравнения в инструкциях Transact-SQL.

  • Сохранение вычисляемых столбцов этого типа.

Следует заметить, что, когда для свойства IsByteOrdered задано значение true, форматы сериализации Native и UserDefined поддерживают следующие операции сравнения:

  • Равно (=)

  • Не равно (!=)

  • Знак «больше» >)

  • Знак «меньше» (<)

  • Больше или равно (>=)

  • Меньше или равно (<=)

Реализация допустимости значений NULL

Помимо задания нужных атрибутов для сборок, создаваемый класс должен также поддерживать допустимость значений NULL. Определяемые пользователем типы, загружаемые в SQL Server, могут работать со значениями NULL, но, чтобы определяемый пользователем тип узнавал значение NULL, класс должен реализовать интерфейс INullable. Дополнительные сведения и пример реализации допустимости значений NULL в определяемом пользователем типе см. в разделе Разработка кода для определяемых пользователем типов.

Преобразование строк

Для поддержки преобразования определяемого пользователем типа в строку и восстановления из строки нужно реализовать в классе методы Parse и ToString. Метод Parse позволяет преобразовывать строку в определяемый пользователем тип. Он должен быть объявлен как static (или Shared в Visual Basic) и принимать параметр типа System.Data.SqlTypes.SqlString. Дополнительные сведения и пример реализации методов Parse и ToString см. в разделе Разработка кода для определяемых пользователем типов.

Сериализация XML

Определяемые пользователем типы должны поддерживать преобразование из типа данных xml и в этот тип данных, подчиняясь соглашению для XML-сериализации. Пространство имен System.Xml.Serialization содержит классы, которые используются для сериализации объектов в документы или потоки формата XML. По желанию можно реализовать сериализацию xml с помощью интерфейса IXmlSerializable, предоставляющего настраиваемую XML-сериализацию и десериализацию.

Помимо явных преобразований определяемого пользователем типа в xml, XML-сериализация позволяет.

  • Применять Xquery к значениям экземпляров определяемого пользователем типа после преобразования в тип данных xml.

  • Использовать определяемые пользователем типы в параметризованных запросах и веб-методах с собственными веб-службами с поддержкой XML в SQL Server. Дополнительные сведения см. в разделе Обработка XML-данных и пользовательских типов данных среды CLR.

  • Использовать определяемые пользователем типы для получения массовой загрузки XML-данных.

  • Сериализовать объекты DataSets, содержащие таблицы со столбцами определяемого пользователем типа.

Определяемые пользователем типы не сериализуются в запросах FOR XML. Для выполнения запроса FOR XML, показывающего XML-сериализацию определяемого пользователем типа, нужно явно преобразовать каждый столбец определяемого пользователем типа в тип данных xml в предложении SELECT. Можно явно преобразовать столбцы в типы varbinary, varchar или nvarchar.