Guest User

Untitled

a guest
Nov 10th, 2015
197
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 9.69 KB | None | 0 0
  1. //
  2. // Copyright (C) Latheesan Kanesamoorthy 2015
  3. //
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Data.SqlClient;
  7. using System.Threading;
  8.  
  9. namespace Libs
  10. {
  11.     public class DBErrorEventArg : EventArgs
  12.     {
  13.         /// <summary>
  14.         /// Class properties.
  15.         /// </summary>
  16.         public string ErrorMsg { get; private set; }
  17.         public string LastQuery { get; private set; }
  18.  
  19.         /// <summary>
  20.         /// Class constructor.
  21.         /// </summary>
  22.         /// <param name="errorMsg"></param>
  23.         /// <param name="lastQuery"></param>
  24.         public DBErrorEventArg(string errorMsg, string lastQuery)
  25.         {
  26.             ErrorMsg = errorMsg;
  27.             LastQuery = lastQuery;
  28.         }
  29.     }
  30.  
  31.     public class MSSQLConnection
  32.     {
  33.         /// <summary>
  34.         /// Class constants.
  35.         /// </summary>
  36.         const int sqlDeadlock = 1205;
  37.         const int sqlTimeout = -2;
  38.  
  39.         /// <summary>
  40.         /// Private class objects.
  41.         /// </summary>
  42.         private SqlConnection sqlConnection;
  43.         private int sqlCommandTimeout;
  44.         private string lastQuery = string.Empty;
  45.  
  46.         /// <summary>
  47.         /// Public event related objects & handler.
  48.         /// </summary>
  49.         public event ErrorHandler OnError;
  50.         public delegate void ErrorHandler(MSSQLConnection sender, DBErrorEventArg e);
  51.  
  52.         /// <summary>
  53.         /// Class constructor.
  54.         /// </summary>
  55.         /// <param name="sqlConnection"></param>
  56.         /// <param name="sqlCommandTimeout"></param>
  57.         public MSSQLConnection(SqlConnection sqlConnection, Int32 sqlCommandTimeout = 120)
  58.         {
  59.             if (null == sqlConnection)
  60.                 throw new Exception("Invalid MSSQL Database Conection Handle");
  61.  
  62.             if (sqlConnection.State != System.Data.ConnectionState.Open)
  63.                 throw new Exception("MSSQL Database Connection Is Not Open");
  64.  
  65.             this.sqlConnection = sqlConnection;
  66.             this.sqlCommandTimeout = sqlCommandTimeout;
  67.         }
  68.  
  69.         /// <summary>
  70.         /// Helper method to emit a database error to event subscribers.
  71.         /// </summary>
  72.         /// <param name="errorMsg"></param>
  73.         internal void EmitError(String errorMsg)
  74.         {
  75.             var errorDelegate = OnError;
  76.             if (errorDelegate != null)
  77.             {
  78.                 errorDelegate(this, new DBErrorEventArg(errorMsg, lastQuery));
  79.             }
  80.         }
  81.  
  82.         /// <summary>
  83.         /// Generic method to execute a query with retries to handle deadlock / timeout errors.
  84.         /// </summary>
  85.         /// <typeparam name="T"></typeparam>
  86.         /// <param name="sqlFunc"></param>
  87.         /// <returns></returns>
  88.         private T Retry<T>(Func<T> sqlFunc)
  89.         {
  90.             try
  91.             {
  92.                 int retryCount = 30;
  93.                 TimeSpan retryDelay = TimeSpan.FromSeconds(1);
  94.  
  95.                 while (true)
  96.                 {
  97.                     try
  98.                     {
  99.                         if (sqlConnection.State == System.Data.ConnectionState.Closed)
  100.                         {
  101.                             sqlConnection.Open();
  102.                         }
  103.  
  104.                         return sqlFunc();
  105.                     }
  106.                     catch (SqlException sqlException)
  107.                     {
  108.                         --retryCount;
  109.  
  110.                         if (retryCount <= 0) { throw; }
  111.  
  112.                         if (sqlException.Number == sqlDeadlock)
  113.                         {
  114.                             EmitError(string.Format("[ Attempts Left : {0} ] Failed to execute Query, SQL Deadlock Error. Retrying again in {1} seconds ...",
  115.                                 retryCount,
  116.                                 retryDelay.Seconds));
  117.                         }
  118.                         else if (sqlException.Number == sqlTimeout)
  119.                         {
  120.                             EmitError(string.Format("[ Attempts Left : {0} ] Failed to execute Query, SQL Timeout Error. Retrying again in {1} seconds ...",
  121.                                 retryCount,
  122.                                 retryDelay.Seconds));
  123.                         }
  124.                         else { throw; }
  125.  
  126.                         Thread.Sleep(retryDelay);
  127.                     }
  128.                 }
  129.             }
  130.             catch (Exception e)
  131.             {
  132.                 EmitError("Failed to execute SQL Query : " + e.Message);
  133.                 return default(T);
  134.             }
  135.         }
  136.  
  137.         /// <summary>
  138.         /// Retry helper method.
  139.         /// </summary>
  140.         /// <param name="sqlAction"></param>
  141.         private void Retry(Action sqlAction)
  142.         {
  143.             Retry(() => { sqlAction(); return true; });
  144.         }
  145.  
  146.         /// <summary>
  147.         /// Helper method to populate sql paramters onto sql command.
  148.         /// </summary>
  149.         /// <param name="sqlCommand"></param>
  150.         /// <param name="sqlParamters"></param>
  151.         private void AddSqlParameters(SqlCommand sqlCommand, Dictionary<string, object> sqlParamters)
  152.         {
  153.             if (null != sqlParamters && sqlParamters.Count > 0)
  154.             {
  155.                 foreach (KeyValuePair<string, object> sqlParamter in sqlParamters)
  156.                 {
  157.                     sqlCommand.Parameters.AddWithValue(sqlParamter.Key, sqlParamter.Value);
  158.                 }
  159.             }
  160.         }
  161.  
  162.         /// <summary>
  163.         /// Method to execute a scalar query (select a single field from row).
  164.         /// </summary>
  165.         /// <typeparam name="T"></typeparam>
  166.         /// <param name="sqlQuery"></param>
  167.         /// <returns></returns>
  168.         public T ExecuteScalar<T>(string sqlQuery, Dictionary<string, object> sqlParamters = null)
  169.         {
  170.             lastQuery = sqlQuery;
  171.  
  172.             return Retry(() =>
  173.             {
  174.                 using (SqlCommand sqlCommand = new SqlCommand(sqlQuery, sqlConnection))
  175.                 {
  176.                     sqlCommand.CommandTimeout = sqlCommandTimeout;
  177.                     AddSqlParameters(sqlCommand, sqlParamters);
  178.                     object result = sqlCommand.ExecuteScalar();
  179.  
  180.                     if (null == result || result is DBNull)
  181.                     {
  182.                         return default(T);
  183.                     }
  184.  
  185.                     Type type = typeof(T);
  186.  
  187.                     if (type == typeof(int))
  188.                     {
  189.                         int value;
  190.                         int.TryParse(result.ToString(), out value);
  191.                         result = value;
  192.                     }
  193.  
  194.                     if (type == typeof(decimal))
  195.                     {
  196.                         decimal value;
  197.                         decimal.TryParse(result.ToString(), out value);
  198.                         result = value;
  199.                     }
  200.  
  201.                     if (type == typeof(string))
  202.                     {
  203.                         result = result.ToString();
  204.                     }
  205.  
  206.                     return (T)result;
  207.                 }
  208.             });
  209.         }
  210.  
  211.         /// <summary>
  212.         /// Method to execute a reader query (select one or more rows).
  213.         /// </summary>
  214.         /// <param name="sqlQuery"></param>
  215.         /// <returns></returns>
  216.         public List<Dictionary<string, object>> ExecuteReader(string sqlQuery, Dictionary<string, object> sqlParamters = null)
  217.         {
  218.             lastQuery = sqlQuery;
  219.  
  220.             return Retry(() =>
  221.             {
  222.                 using (SqlCommand sqlCommand = new SqlCommand(sqlQuery, sqlConnection))
  223.                 {
  224.                     sqlCommand.CommandTimeout = sqlCommandTimeout;
  225.                     AddSqlParameters(sqlCommand, sqlParamters);
  226.                     List<Dictionary<string, object>> results = new List<Dictionary<string, object>>();
  227.  
  228.                     using (SqlDataReader reader = sqlCommand.ExecuteReader())
  229.                     {
  230.                         if (null == reader) { return results; }
  231.  
  232.                         while (reader.Read())
  233.                         {
  234.                             Dictionary<string, object> resultData = new Dictionary<string, object>();
  235.  
  236.                             for (int i = 0; i < reader.FieldCount; i++)
  237.                             {
  238.                                 string columnName = reader.GetName(i);
  239.                                 object columnValue = reader[columnName];
  240.                                 resultData.Add(columnName, columnValue);
  241.                             }
  242.  
  243.                             results.Add(resultData);
  244.                         }
  245.                     }
  246.  
  247.                     return results;
  248.                 }
  249.             });
  250.         }
  251.  
  252.         /// <summary>
  253.         /// Method to execute a non query (for CRUD operations).
  254.         /// </summary>
  255.         /// <param name="sqlQuery"></param>
  256.         /// <returns></returns>
  257.         public Int32 ExecuteNonQuery(string sqlQuery, Dictionary<string, object> sqlParamters = null)
  258.         {
  259.             lastQuery = sqlQuery;
  260.  
  261.             return Retry(() =>
  262.             {
  263.                 int affectedRows = 0;
  264.  
  265.                 using (SqlTransaction sqlTransaction = sqlConnection.BeginTransaction())
  266.                 {
  267.                     using (SqlCommand sqlCommand = new SqlCommand(sqlQuery, sqlConnection, sqlTransaction))
  268.                     {
  269.                         AddSqlParameters(sqlCommand, sqlParamters);
  270.                         sqlCommand.CommandTimeout = sqlCommandTimeout;
  271.                         affectedRows = sqlCommand.ExecuteNonQuery();
  272.                         sqlTransaction.Commit();
  273.                     }
  274.                 }
  275.  
  276.                 return affectedRows;
  277.             });
  278.         }
  279.     }
  280. }
Advertisement
Add Comment
Please, Sign In to add comment