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 😉
How do you use parametersb with the stored procedure from your c# code?
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.
Pingback: C# T-SQL Stored Procedure Wrapper with dbscript « devioblog
Pingback: Creating a light-weight Data Access Layer in C# (1) « devioblog
hii
its a greate artical…..