支持大型 UDT

此示例解决方案包含两个项目。 其中一个项目基于 C# 源代码创建一个程序集 (DLL)。 该程序集包含 CLR 类型。 数据库中将添加一个表。 该表中的一个列将属于该程序集中定义的一个类型。默认情况下,此示例将使用 master 数据库。 第二个项目是一个本机 C 应用程序,用来读取表中的数据。

此示例不适用于 SQL Server 2008 之前的任何 SQL Server 版本。

有关针对大型 UDT 的支持的详细信息,请参阅大型 CLR 用户定义类型 (ODBC)

示例

第一个代码列表是 C# 源代码。 将其粘贴到名为 LargeStringUDT.cs 的文件并将它编译为 DLL。 将 LargeStringUDT.dll 复制到 C 驱动器的根目录中。

第二个 (Transact-SQL) 代码列表在 master 数据库中创建该程序集。

使用 odbc32.lib 和 user32.lib 编译第二个 (C++) 代码列表。 请确保您的 INCLUDE 环境变量包括含有 sqlncli.h 的目录。

如果您要将此示例构建为在 64 位操作系统上运行的 32 位应用程序并运行该示例,则必须使用 %windir%\SysWOW64\odbcad32.exe 中的 ODBC 管理器创建 ODBC 数据源。

此示例连接到您的计算机上默认的 SQL Server 实例。 若要连接到命名实例,请更改 ODBC 数据源的定义以使用以下格式指定实例:server\namedinstance。 默认情况下,SQL Server Express 将安装在命名实例中。

第四个 (Transact-SQL) 代码列表从 master 数据库中删除该程序集。

// LargeStringUDT.cs
// compile with: /target:library
using System;
using System.Data;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Text;

[assembly: System.CLSCompliantAttribute(true)]
[Serializable]
[Microsoft.SqlServer.Server.SqlUserDefinedType(Format.UserDefined, IsFixedLength = false, MaxByteSize = -1, IsByteOrdered = true)]
public class LargeStringUDT : INullable, IBinarySerialize {
    private bool _isNull;
    private string _largeString;

    public bool IsNull {
        get {
            return (_isNull);
        }
    }

    public static LargeStringUDT Null {
        get {
            LargeStringUDT lsUDT = new LargeStringUDT();
            lsUDT._isNull = true;
            return lsUDT;
        }
    }

    public override string ToString() {
        if (IsNull)
            return "NULL";
        else
            return _largeString;
    }

    [SqlMethod(OnNullCall = false)]
    public static LargeStringUDT Parse(SqlString s) {
        if (s.IsNull)
            return Null;

        LargeStringUDT lsUDT = new LargeStringUDT();
        lsUDT._largeString = s.Value;
        return lsUDT;
    }

    public String LargeString {
        get {
            return _largeString;
        }

        set {
            _largeString = value;
        }
    }

    public void Read(System.IO.BinaryReader r) {
        _isNull = r.ReadBoolean();
        if (!_isNull)
            _largeString = new String(r.ReadChars(r.ReadInt32()));
    }

    public void Write(System.IO.BinaryWriter w) {
        w.Write(_isNull);
        if (!_isNull) {
            w.Write(_largeString.Length);
            for (int i = 0; i < _largeString.Length; ++i)
                w.Write(_largeString[i]);
        }
    }
}

USE [MASTER]
IF EXISTS (SELECT * FROM sys.objects WHERE name = 'LargeStringUDTs')
   DROP TABLE LargeStringUDTs
GO

IF EXISTS (SELECT * FROM sys.types WHERE name = 'LargeStringUDT')
   DROP TYPE dbo.LargeStringUDT
GO

IF EXISTS (SELECT * FROM sys.assemblies WHERE name = 'LargeStringUDT')
   DROP ASSEMBLY LargeStringUDT
GO

CREATE ASSEMBLY LargeStringUDT
FROM 'C:\LargeStringUDT.dll'
WITh PERMISSION_SET=SAFE;
GO

CREATE TYPE dbo.LargeStringUDT 
EXTERNAL NAME LargeStringUDT.[LargeStringUDT];
GO

CREATE TABLE dbo.LargeStringUDTs
(ID int IDENTITY(1,1) PRIMARY KEY, LargeString LargeStringUDT)
GO

INSERT INTO dbo.LargeStringUDTs (LargeString) VALUES (CONVERT(LargeStringUDT, 'This is the first string'));
INSERT INTO dbo.LargeStringUDTs (LargeString) VALUES (CONVERT(LargeStringUDT, 'This is the second string'));
INSERT INTO dbo.LargeStringUDTs (LargeString) VALUES (Convert(LargeStringUDT, 'This is the third string'));
GO

// compile with: odbc32.lib
#include <stdio.h>
#include <tchar.h>
#include <stddef.h>
#include <windows.h>
#define ODBCVER 0x0350
#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>
#define _SQLNCLI_ODBC
#include "sqlncli.h"

int main() {
   // The command to execute.
   SQLTCHAR* szCmdString = (SQLTCHAR *)_T("SELECT ID, LargeString FROM dbo.LargeStringUDTs");

   int ret = 0;
   SQLRETURN rc;
   SQLHENV hEnv = NULL;
   SQLHDBC hDbc = NULL;
   SQLHSTMT hStmt = NULL;
   SQLTCHAR szConn[256]; 
   BYTE DataBuf[15];
   SQLSMALLINT iLen = 0;
   SQLLEN iDataLen = 0;

   rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnv);
   if (rc != SQL_SUCCESS) {
      printf("Failed to get HENV\n");
      return -1;
   }

   rc = SQLSetEnvAttr (hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
   if (rc != SQL_SUCCESS) {
      printf("Failed to SetEnvAttr\n");
      SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
      return -1;
   }

   rc = SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDbc);
   if (rc != SQL_SUCCESS) {
      printf("Failed to get HDBC\n");
      SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
      return -1;
   }

   rc = SQLDriverConnect(hDbc, NULL, 
      (SQLTCHAR *)_T("DRIVER={SQL Server Native Client 10.0};SERVER=(local);Trusted_Connection=Yes;database=master"), 
      SQL_NTS, szConn, 255, &iLen, SQL_DRIVER_NOPROMPT);
   if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
      printf("Failed to connect\n");
      ret = -1;
      goto End;
   }

   rc = SQLAllocHandle(SQL_HANDLE_STMT, hDbc, &hStmt);
   if (rc != SQL_SUCCESS) {
      printf("Failed to get hstmt\n");
      ret = -1;
      goto End;
   }

   // Execute the command.
   rc = SQLExecDirect(hStmt, szCmdString, SQL_NTS);
   if (rc != SQL_SUCCESS) {
      printf("Failed to get hstmt\n");
      ret = -1;
      goto End;
   }

   // Process the result set.
   SQLSMALLINT paramType;
   SQLTCHAR szData[100], szData2[100];
   SQLSMALLINT NameLengthPtr = 0, DataTypePtr, DecimalDigitsPtr, NullablePtr;
   SQLULEN ColumnSizePtr[2];
   rc = SQLDescribeCol(hStmt, 1, szData, sizeof(szData), &NameLengthPtr, &DataTypePtr, &ColumnSizePtr[0], &DecimalDigitsPtr, &NullablePtr);
   _tprintf(_T("%s"), szData);
   rc = SQLDescribeCol(hStmt, 2, szData2, sizeof(szData2), &NameLengthPtr, &DataTypePtr, &ColumnSizePtr[1], &DecimalDigitsPtr, &NullablePtr);
   _tprintf(_T("          %s\n"), szData2);

   while ( ( rc = SQLFetch(hStmt ) ) == SQL_SUCCESS ) {
      rc = SQLGetData(hStmt, 1, SQL_C_SHORT, &paramType, sizeof(SQL_C_SHORT), NULL);
      printf("%d           ", paramType);

      bool ifFirst = true;
      while ( ( rc = SQLGetData(hStmt, 2 , SQL_C_BINARY, DataBuf, sizeof(DataBuf) ,&iDataLen ) ) != SQL_NO_DATA) {
         // process number of bytes in the DataBuf;
         int NumBytes = (iDataLen > sizeof(DataBuf)) || (iDataLen == SQL_NO_TOTAL) ? sizeof(DataBuf): iDataLen;
         for ( int i = ifFirst?5:0 ; i <NumBytes ; i++ )
            putchar((char)DataBuf[i]);
         ifFirst = false;
      };
      printf("\n");
   }

   if ( rc != SQL_NO_DATA ) {
      printf("Failed to fetch data\n");
      ret= -1;
   }

End:
   if (hStmt) {
      SQLFreeStmt(hStmt,SQL_CLOSE);
      SQLFreeStmt(hStmt,SQL_DROP);
   }

   if (hDbc) {
      SQLDisconnect(hDbc);
      SQLFreeConnect(hDbc);
   }

   if (hEnv)
      SQLFreeHandle(SQL_HANDLE_ENV, hEnv);
};

USE [MASTER]
IF EXISTS (SELECT * FROM sys.objects WHERE name = 'LargeStringUDTs')
   DROP TABLE LargeStringUDTs
GO

IF EXISTS (SELECT * FROM sys.types WHERE name = 'LargeStringUDT')
   DROP TYPE dbo.LargeStringUDT
GO

IF EXISTS (SELECT * FROM sys.assemblies WHERE name = 'LargeStringUDT')
   DROP ASSEMBLY LargeStringUDT
GO