CHAPTER 8 ■ SQL AZURE 268 DateTime connectingStart = DateTime.Now; int timeout = 0; while ( connection.State == ConnectionState.Connecting && timeout <= 1) { DateTime timeNow = DateTime.Now; TimeSpan timespanElapsed = new TimeSpan(timeNow.Hour - connectingStart.Hour, timeNow.Minute - connectingStart.Minute, timeNow.Second - connectingStart.Second); timeout = TimeSpan.Compare(timespanElapsed, timeOutSetting); } } private void CloseConn() { lock ( connection) { while (null != connection && connection.State != ConnectionState.Broken && connection.State == ConnectionState.Open) { connection.Close(); } } } #endregion #region Public Methods public void BeginTrans() { if ( connection.State != ConnectionState.Closed && connection.State != ConnectionState.Broken) { CloseConn(); } OpenConn(); transaction = connection.BeginTransaction(); } public void CommitTrans() { if (null != transaction) { transaction.Commit(); } CloseConn(); transaction = null; } public void RollBackTrans() CHAPTER 8 ■ SQL AZURE 269 { if (null != transaction) { transaction.Rollback(); } CloseConn(); transaction = null; } public SqlDataReader GetDataReader(SqlCommand command) { try { OpenConn(); command.Connection = connection; command.Transaction = transaction; command.CommandTimeout = COMMAND TIMEOUT; return command.ExecuteReader(CommandBehavior.CloseConnection); } finally { if ( transaction != null) { RollBackTrans(); } command.Dispose(); } } public Object ExecuteNonQuery(SqlCommand command) { OpenConn(); command.Connection = connection; command.Transaction = transaction; command.CommandTimeout = COMMAND TIMEOUT; try { int rowsAffected = command.ExecuteNonQuery(); return (command.Parameters.Contains("@ReturnValue")) ? Convert.ToInt32(command.Parameters["@ReturnValue"].Value) : rowsAffected; } finally { if ( transaction != null) CHAPTER 8 ■ SQL AZURE 270 { RollBackTrans(); } command.Dispose(); } } public string ExecuteScalar(SqlCommand command) { try { OpenConn(); command.Connection = connection; command.Transaction = transaction; command.CommandTimeout = COMMAND TIMEOUT; return command.ExecuteScalar().ToString(); } finally { if ( transaction != null) { RollBackTrans(); } command.Dispose(); } } public DataSet ExecuteDataSet(SqlCommand command) { DataSet oDataSet = new DataSet(); try { SqlDataAdapter da; oDataSet.Locale = CultureInfo.InvariantCulture; OpenConn(); command.Connection = connection; command.Transaction = transaction; command.CommandTimeout = COMMAND TIMEOUT; da = new SqlDataAdapter(command); da.Fill(oDataSet); } finally { command.Dispose(); } return oDataSet; } CHAPTER 8 ■ SQL AZURE 271 public int ExecuteDataTable(SqlCommand command, ref DataTable datatable) { int rowAffected = 0; try { SqlDataAdapter da; datatable.Locale = CultureInfo.InvariantCulture; OpenConn(); command.Connection = connection; command.Transaction = transaction; command.CommandTimeout = COMMAND TIMEOUT; da = new SqlDataAdapter(command); rowAffected = da.Fill(datatable); } finally { command.Dispose(); } return rowAffected; } #endregion } } SQLDataAccessHelper Class A helper class SQLDataAccessHelper is designed to help applications use SQLDataAccessComponent based on the XML service configuration data in a simple and agile way. The responsibilities of this class are to: • Extract the SQL data-access-configuration data by using the service name as the key from the deserialized XML data classes. • Invoke a SQL query to execute a stored procedure depending on the type of command. If the service request is a SQL stored procedure, populate the parameter with the corresponding SQL data type and assign values to the parameter. • Invoke a SQL query to execute the selected script text. • Compose the return results as the display message for UI updating. • Handle errors. • Serialize and deserialize XML. CHAPTER 8 ■ SQL AZURE 272 This class has two parameterized constructors. Both constructors take the deserialized XML data object as an input parameter. There are five public access methods that have been exposed from this helper class. • XmlRetrive() and XmlPersist(), used to deserialize and serialize XML data files and data objects. • Execute(), used to execute a SQL query or stored procedure predefined in the XML data file. The application just needs to pass the subject name of the SQL service and a reference to an object that will hold a possible return object from the SQL services. This method will invoke either a SQL query or execute a stored procedure from the SQLAccessComponent according to the command type specified from the data file. This makes the code extremely concise and lean for a SQL Azure or on-premises SQL application. • ExecuteSelected(), used to execute a SQL query script text block selected by a user on the fly. This function can be invoked by a user using the F5 shortcut key after the user selected a block of script text. • CreateStoredProcedure(), which has not been integrated into the user interface of SQLAzureConnect yet. It is ready for any other client application to invoke. With the current SQLAzureConnect this function can be mimicked by using the ExecuteSelected() feature. Compose a connection string using the XML data. The connection string will come up in two distinct formats, SQL Azure cloud connection string format and on-premises connection string format to allow this tool to support both cloud SQL services and on-premises SQL server access. Listing 8-14 is the source code of the class SQLDataAccessHelper. We have explained all member methods and their responsibilities. The other member methods, such as PopulateParameters() and PopulateStoredProcedureParameters(), are fairly straightforward. Listing 8-14. Implementation for Class SQLDataAccessHelper using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; using System.Data.Sql; using System.Data.SqlClient; using System.Xml; using System.Xml.Serialization; using System.IO; using Microsoft.SqlServer.Management.Smo; using Microsoft.SqlServer.Management.Common; using System.Text.RegularExpressions; namespace SQLAzureConnect { using SQLAzureConnect.Schema.SQLDatabaseAccess; using SQLAzureConnect.Schema.SQLParameter;