# Monday, March 15, 2010

Apparently SMO can't get the SqlDataType of a UserDefinedDataType.

SQL Server Management Objects (SMO) is a pretty powerful API into Microsoft SQL Server.  I've been pretty happy using it in various scenarios over the years.

Recently, I was surprised to find out that SMO can't get the SqlDataType underlying a UserDefinedDataType.  This is reproducible using the Microsoft Pubs sample database.

Attempt #1:

string databaseName = "pubs";
string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings[databaseName].ToString();

SqlConnection oneSqlConnection = new SqlConnection(connectionString);
ServerConnection oneServerConnection = new ServerConnection(oneSqlConnection);
Server oneServer = new Server(oneServerConnection);
Database oneDatabase = new Database(oneServer, databaseName);
oneDatabase.Refresh();

UserDefinedDataType tidUserDefinedDataType =
 
   new Microsoft.SqlServer.Management.Smo.UserDefinedDataType(oneDatabase, "tid", "dbo");
bool initializeDidSucceed = tidUserDefinedDataType.Initialize(true);

In this scenario, for reasons unknown to me, initializeDidSucceed is false!

Now, if we try to access tidUserDefinedDataType.SystemType which seems like the appropriate data item, we get this exception:

Microsoft.SqlServer.Management.Smo.PropertyNotSetException: To accomplish this action, set property SystemType.
   at Microsoft.SqlServer.Management.Smo.SqlSmoObject.OnPropertyMissing(String propname, Boolean useDefaultValue)
   at Microsoft.SqlServer.Management.Smo.PropertyCollection.RetrieveProperty(Int32 index, Boolean useDefaultOnMissingValue)
   at Microsoft.SqlServer.Management.Smo.PropertyCollection.GetValueWithNullReplacement(String propertyName, Boolean throwOnNullValue, Boolean useDefaultOnMissingValue)
   at Microsoft.SqlServer.Management.Smo.UserDefinedDataType.get_SystemType()


Attempt #2:

string databaseName = "pubs";
string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings[databaseName].ToString();

SqlConnection oneSqlConnection = new SqlConnection(connectionString);
ServerConnection oneServerConnection = new ServerConnection(oneSqlConnection);

Server oneServer = new Server(oneServerConnection);
Database oneDatabase = new Database(oneServer, databaseName);
oneDatabase.Refresh();

Table oneTable = new Table(oneDatabase, "titles", "dbo");
oneTable.Refresh();

Now the following values are returned:

oneTable.Columns[0].Name                             = title_id
oneTable.Columns[0].DataType.Name               = tid
oneTable.Columns[0].DataType.SqlDataType      = UserDefinedDataType
oneTable.Columns[0].DataType.MaximumLength  = 6

So, we can successfully get the length of the underlying data type, but we can't get the SqlDataType.


Attempt #3:

USE pubs

DECLARE @CurrentSchemaName VARCHAR(128)
DECLARE @UserDefinedDataTypeName VARCHAR(128)

SET @CurrentSchemaName = 'dbo'
SET @UserDefinedDataTypeName = 'tid'

SELECT name
FROM sys.types
WHERE user_type_id =
  (
   SELECT system_type_id
   FROM sys.types
     INNER JOIN sys.schemas
       ON sys.types.schema_id = sys.schemas.schema_id
   WHERE sys.schemas.name = @CurrentSchemaName
     AND sys.types.name = @UserDefinedDataTypeName
  )

Using straight SQL, we appear to be able to get the answer we are looking for: "varchar".


I wonder why SMO is missing this capability?