Generating a C# Stored Procedure Wrapper in TSQL

I love compiling languages. Whenever you change something that consequently does not fit the rest of the program, something breaks and you get a compiler error. Great!

If you develop database applications, this nice change-it-break-it mechanism stops to work, as the relation between database objects and their usage by a C# application is in no way hard-coded.

Therefore I developed a small piece of T-SQL code which will generate a static C# class which encapsulates all stored procedures in a given database in the form:

public static class MyStoredProc {
    public static SqlCommand MyProc1(SqlConnection conn)
    {
        SqlCommand cmd = new SqlCommand("MyProc1", conn);
        ...
        return cmd;
    }
    ...
}

The strategy is simple: loop through all desired procedures, and for each procedure loop through its parameters twice: once for the declaration, second time for the parameter values.

Create file and class header:

PRINT 'using System;
using System.Data;
using System.Data.SqlClient;
'

PRINT    'namespace ' + @NameSpace
PRINT    '{'
PRINT    'public static class ' + @Class + '
{'

Now we declare the loop for the procedure names (the code uses SQL2000 dictionary objects, which are also present on SQL2005; adaption should be simple):

DECLARE cP CURSOR FOR
SELECT  dbo.sysobjects.name
FROM     dbo.sysobjects
WHERE    (dbo.sysobjects.name NOT LIKE 'dt[_]%')
AND    ... other conditions ...
AND     (dbo.sysobjects.xtype = 'P')
ORDER BY dbo.sysobjects.name

OPEN cP
FETCH cP INTO @Proc

WHILE @@FETCH_STATUS = 0 BEGIN

PRINT '    public static SqlCommand ' + @Proc + '(SqlConnection conn'

Next we declare the cursor for the procedure parameters in the correct order:

DECLARE c CURSOR FOR
SELECT  dbo.syscolumns.name, dbo.systypes.name, dbo.syscolumns.length
FROM     dbo.sysobjects
INNER JOIN dbo.syscolumns ON dbo.sysobjects.id = dbo.syscolumns.id
INNER JOIN dbo.systypes ON dbo.syscolumns.xtype = dbo.systypes.xtype
WHERE    (dbo.sysobjects.name = @Proc) AND (dbo.systypes.name <> N'sysname')
ORDER BY dbo.sysobjects.name, dbo.syscolumns.colid

OPEN c
FETCH c INTO @Column, @Type, @Length

WHILE @@FETCH_STATUS = 0 BEGIN

For parameter declaration, we need to translate the SQL datatypes into C# datatypes. In this example, we use the following mapping:

int -> int?
datetime -> DateTime?
n/varchar -> string
bit -> bool?

PRINT  '        , ' +
    CASE @Type
    WHEN 'int' THEN 'int?'
    WHEN 'datetime' THEN 'DateTime?'
    WHEN 'nvarchar' THEN 'string'
    WHEN 'varchar' THEN 'string'
    WHEN 'bit' THEN 'bool?'
    ELSE '**datatype-not-supported**' END +
    ' ' + SUBSTRING(@Column, 2, LEN(@Column)-1)

All the wrapping method does is to create a SqlCommand using the SqlConnection:

PRINT ' )'
PRINT ' {'
PRINT '   SqlCommand cmd = new SqlCommand("' + @Proc + '", conn);'
PRINT '   cmd.CommandType = CommandType.StoredProcedure;'
PRINT '   cmd.CommandTimeout = 0;'

Then we loop through the parameters again to set the SqlCommand’s Parameters’ values. As shown above, parameter types are mapped to C# Nullable<T> types (question-mark notation), except for strings, as they can have the value null. Thus we have to treat string parameters differently from nullable (in C#) parameters:

OPEN c
FETCH c INTO @Column, @Type, @Length

WHILE @@FETCH_STATUS = 0 BEGIN

    IF @Type='nvarchar' OR @Type='varchar'
        PRINT ' cmd.Parameters.AddWithValue("' + @Column + '", ' +
            SUBSTRING(@Column, 2, LEN(@Column)-1) + ' != null ? (object)' +
            SUBSTRING(@Column, 2, LEN(@Column)-1) + ' : DBNull.Value);'
    ELSE
        PRINT ' cmd.Parameters.AddWithValue("' + @Column + '", ' +
            SUBSTRING(@Column, 2, LEN(@Column)-1) + '.HasValue ? (object)' +
            SUBSTRING(@Column, 2, LEN(@Column)-1) + '.Value : DBNull.Value);'

Finally, return the new SqlCommand:

PRINT '        return cmd;'
PRINT '    }'

Run this and paste the result into your C# application 😉

5 thoughts on “Generating a C# Stored Procedure Wrapper in TSQL

  1. The full script generates a C# parameter for each T-SQL parameter. Calling the generated C# method with all parameters provided will return an SqlCommand object with all parameters set.

  2. Pingback: C# T-SQL Stored Procedure Wrapper with dbscript « devioblog

  3. Pingback: Creating a light-weight Data Access Layer in C# (1) « devioblog

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.