Database Operations Using C#: Table Operations

In this article, I am going to explain how you can create a database table, using C# code -- but the main aim here is to run a SQL script, using C# code. Now, one can argue, why do we have to do this, when we have the stored procedure doing the same thing for us? Well, there are several reasons why it’s really flexible to do it. If you have a system with versioning, you can use this code to apply the different versions. Now, we will start by just focusing on how to create a table but later on, we will see how powerful this code can be, though which we can achieve more.

Below is the Main class which will have the caller’s code. In the constants, I have defined the connection strings. An object of the ExecuteSql is created, which will help us to run our script. Note that this will only run the script on the master db. Next is creating the object for the schema writer. Here lies the code, which will load the script to create a table.

  1. class Program  
  2. {  
  3.     private static ExecuteSql _executeMaster;  
  4.   
  5.     private static string _connectionString =  
  6.         "Data Source=.;Integrated Security=SSPI;Initial Catalog=testme;Application Name=Test;Connection Timeout=300";  
  7.   
  8.     private static DatabaseSchemaCreator databaseSchemaCreator;  
  9.     private static string _masterDatabaseName = "master";  
  10.   
  11.     static void Main(string[] args)  
  12.     {  
  13.         _executeMaster = new ExecuteSql(openConnectionToMaster);  
  14.         databaseSchemaCreator = new DatabaseSchemaCreator(_executeMaster);   
  15.         databaseSchemaCreator.ApplyScript(@"C:\Users\Documents\Visual Studio 2015\Projects\DB\", "testme");  
  16.         Console.WriteLine("done");  
  17.         Console.ReadLine();  
  18.     }  
  19.         
  20.   
  21.     private static SqlConnection openConnectionToMaster()  
  22.     {  
  23.         var connectionStringBuilder = new SqlConnectionStringBuilder(_connectionString);  
  24.         connectionStringBuilder.InitialCatalog = _masterDatabaseName;  
  25.         var conn = new SqlConnection(connectionStringBuilder.ConnectionString);  
  26.         conn.Open();  
  27.         return conn;  
  28.     }  
  29.   
  30. }  

In the apply script path, there is a path to the script, meaning that the script lies in a file, so it can be anything; it can be a create table or drop table script. Given below is also the Db script with the DB name on the top and as I mentioned earlier, we will be running our script on the master db. We need to mention which db, we want to point to; else our table will be created on the master. 

  1. databaseSchemaCreator = new DatabaseSchemaCreator(_executeMaster);     
  2. databaseSchemaCreator.ApplyScript(@"C:\Users\Documents\Visual Studio 2015\Projects\DB\", "testme");    
  1. USE [$(DBNAME)]  
  2.   
  3.   
  4. CREATE TABLE [Jobss](  
  5.     [Id] [uniqueidentifier] NOT NULL,  
  6.     [Name] [nvarchar](maxNULL,  
  7.     [Type] [nvarchar](maxNULL,  
  8.     [UserName] nvarchar(500) NOT NULL,  
  9.     [AssignedNode] [nvarchar](maxNULL,  
  10.     [Status] [nvarchar](maxNULL  
  11.  CONSTRAINT [PK_Jobss] PRIMARY KEY CLUSTERED   
  12. (  
  13.     [Id] ASC  
  14. ))  

This is the class which can be used for multiple purposes. Now, imagine, if you have a system where you have a new version released every month or so and it uses db as well. How will you manage the DB changes? How will your update know which scripts are new and which are already installed? The best solution is to name all your scripts with a number and save the last number you executed. Thus, in our case, this applies to the script, which will load all the script from the local file system and check in the DB the number of the scripts which are already installed and start running the new scripts in the right order.

  1. public class DatabaseSchemaCreator  
  2.     {  
  3.         private readonly ExecuteSql _executeSql;  
  4.   
  5.         public DatabaseSchemaCreator( ExecuteSql executeSql)  
  6.         {  
  7.             _executeSql = executeSql;  
  8.         }  
  9.   
  10.         public void ApplyScript(string releasesPath, string databaseName)  
  11.         {  
  12.             if (!Directory.Exists(releasesPath)) return;  
  13.   
  14.             var scriptsDirectoryInfo = new DirectoryInfo(releasesPath);  
  15.             var scriptFiles = scriptsDirectoryInfo.GetFiles("*.sql", SearchOption.TopDirectoryOnly);  
  16.   
  17.               
  18.             foreach (var scriptFile in scriptFiles)  
  19.             {  
  20.                 var sql = File.ReadAllText(scriptFile.FullName);  
  21.                 sql = ReplaceScriptTags(sql, databaseName);  
  22.                 _executeSql.ExecuteNonQuery(sql);  
  23.             }  
  24.         }  
  25.   
  26.   
  27.         private string ReplaceScriptTags(string script, string name)  
  28.         {  
  29.             script = script.Replace("$(DBNAME)", name);  
  30.             return script;  
  31.         }  
  32.   

  33.     }  

This is the helper class, which will run SQL on the provided connection. Note, there is no transient error handling implemented here. In my later articles, I will try to explain with the help of the same code, using the transient handling. The reason to handle these errors is to be able to be run the code on Cloud. With the use of transient error handling, proper retry logic will be in place. 

  1. public class ExecuteSql  
  2.     {  
  3.         private readonly Func<SqlConnection> _openConnection;  
  4.   
  5.         public ExecuteSql(Func<SqlConnection> openConnection)  
  6.         {  
  7.             _openConnection = openConnection;  
  8.         }  
  9.   
  10.         public void ExecuteCustom(Action<SqlConnection> action)  
  11.         {  
  12.             using (var connection = _openConnection())  
  13.             {  
  14.                 action(connection);  
  15.             }  
  16.         }  
  17.   
  18.         public int ExecuteScalar(string sql, int timeout = 30, IDictionary<stringobject> parameters = null)  
  19.         {  
  20.             parameters = parameters ?? new Dictionary<stringobject>();  
  21.   
  22.             var result = 0;  
  23.   
  24.             using (var connection = _openConnection())  
  25.             {  
  26.                 using (var transaction = connection.BeginTransaction())  
  27.                 {  
  28.                     using (var command = connection.CreateCommand())  
  29.                     {  
  30.                         command.Transaction = transaction;  
  31.   
  32.                         foreach (var parameter in parameters)  
  33.                         {  
  34.                             command.Parameters.AddWithValue(parameter.Key, parameter.Value);  
  35.                         }  
  36.                         command.CommandTimeout = timeout;  
  37.   
  38.                         {  
  39.                             command.CommandText = sql;  
  40.                             command.CommandType = CommandType.Text;  
  41.                             result = (int) (command.ExecuteScalar() ?? default(int));  
  42.                         }  
  43.   
  44.                         transaction.Commit();  
  45.                     }  
  46.                 }  
  47.             }  
  48.   
  49.             return result;  
  50.         }  
  51.   
  52.         public void ExecuteTransactionlessNonQuery(string sql, int timeout = 30)  
  53.         {  
  54.             using (var connection = _openConnection())  
  55.             {  
  56.                 using (var command = connection.CreateCommand())  
  57.                 {  
  58.                     command.CommandType = CommandType.Text;  
  59.                     command.CommandTimeout = timeout;  
  60.                     command.CommandText = sql;  
  61.                     command.ExecuteNonQuery();  
  62.                 }  
  63.             }  
  64.         }  
  65.   
  66.         public void ExecuteNonQuery(string sql, int timeout = 30, IDictionary<stringobject> parameters = null)  
  67.         {  
  68.             parameters = parameters ?? new Dictionary<stringobject>();  
  69.   
  70.             using (var connection = _openConnection())  
  71.             {  
  72.                 using (var transaction = connection.BeginTransaction())  
  73.                 {  
  74.                     using (var command = connection.CreateCommand())  
  75.                     {  
  76.                         command.Transaction = transaction;  
  77.                         command.CommandTimeout = timeout;  
  78.   
  79.                         {  
  80.                             command.Parameters.Clear();  
  81.                             foreach (var parameter in parameters)  
  82.                             {  
  83.                                 if (sql.Contains(parameter.Key))  
  84.                                 {  
  85.                                     command.Parameters.AddWithValue(parameter.Key, parameter.Value);  
  86.                                 }  
  87.                             }  
  88.                             command.CommandText = sql;  
  89.                             command.CommandType = CommandType.Text;  
  90.                             command.ExecuteNonQuery();  
  91.                         }  
  92.   
  93.                         transaction.Commit();  
  94.                     }  
  95.                 }  
  96.             }  
  97.         }  
  98.   
  99.   
  100.     }   
Do comment, if you need any help.


Similar Articles