using System; using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Data.SqlClient; namespace ZeroLevel.SqlServer { public class SqlDbProvider : IDbProvider { #region Fields private readonly SqlDbConnectionFactory _factory; private const int Timeout = 60000; public string ConnectionString { get { return _factory.ConnectionString; } } public string Server { get { return _factory.Server; } } public string Base { get { return _factory.Base; } } #endregion #region .Ctor /// /// Конструктор. /// public SqlDbProvider(SqlDbConnectionFactory factory) { _factory = factory; } #endregion #region ExecuteNonResult public void ExecuteNonResult(IEnumerable commands) { using (DbConnection connection = _factory.CreateConnection()) { foreach (var zcmd in commands) { using (var cmd = CreateCommand(connection, zcmd.Query, zcmd.Parameters, Timeout)) { cmd.ExecuteNonQuery(); } } } } public int ExecuteNonResult(string query) { return ExecuteNonResult(query, null); } public int ExecuteNonResult(string query, DbParameter[] par) { using (DbConnection connection = _factory.CreateConnection()) { using (var cmd = CreateCommand(connection, query, par, Timeout)) { return cmd.ExecuteNonQuery(); } } } public T Insert(string insert_query, SqlParameter[] par) { DbConnection connection = _factory.CreateConnection(); try { using (var cmd = CreateCommand(connection, insert_query, par, Timeout)) { var result = cmd.ExecuteScalar(); return (T)result; } } finally { connection.Dispose(); } } #endregion #region ExecuteQueryDataTable public DataTable ExecuteQueryDataTable(string query) { var ds = ExecuteQuerySqlDataSet(query); if (ds != null && ds.Tables.Count > 0) { return ds.Tables[0]; } return null; } public DataTable ExecuteQueryDataTable(string query, DbParameter[] par) { var ds = ExecuteQuerySqlDataSet(query, par); if (ds != null && ds.Tables.Count > 0) { return ds.Tables[0]; } return null; } #endregion #region ExecuteQuerySqlDataSet public DataSet ExecuteQuerySqlDataSet(string query) { var ds = new DataSet("DataSet"); using (var connection = _factory.CreateConnection()) { using (var cmd = CreateCommand(connection, query, null, Timeout)) { using (var da = new SqlDataAdapter(cmd)) { da.Fill(ds); } } } return ds; } public DataSet ExecuteQuerySqlDataSet(string query, DbParameter[] par) { var ds = new DataSet("DataSet"); using (var connection = _factory.CreateConnection()) { using (var cmd = CreateCommand(connection, query, par, Timeout)) { using (var da = new SqlDataAdapter(cmd)) { da.Fill(ds); } } } return ds; } #endregion #region ExecuteScalar public object ExecuteScalar(string query) { using (var connection = _factory.CreateConnection()) { using (var cmd = CreateCommand(connection, query, null, Timeout)) { return cmd.ExecuteScalar(); } } } public object ExecuteScalar(string query, DbParameter[] par) { using (var connection = _factory.CreateConnection()) { using (var cmd = CreateCommand(connection, query, par, Timeout)) { return cmd.ExecuteScalar(); } } } #endregion #region ExecuteStoredProcedure public int ExecProcedure(string name) { using (var connection = _factory.CreateConnection()) { using (var command = new SqlCommand(name, connection) { CommandType = CommandType.StoredProcedure }) { command.CommandTimeout = 300000; return command.ExecuteNonQuery(); } } } #endregion #region LazySelect public void LazySelect(string query, DbParameter[] par, Func readHandler, int timeout = Timeout) { using (var connection = _factory.CreateConnection()) { using (var cmd = CreateCommand(connection, query, par, Timeout)) { using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection)) { try { while (reader.Read()) { if (false == readHandler(reader)) break; } } catch (Exception ex) { Log.Error(ex, "Error executing query {0}.", cmd.CommandText); } finally { // Always call Close when done reading. reader.Close(); } } } } } #endregion #region ExistsTable private const string QueryExistsTable = @"IF OBJECT_ID (N'[{0}]', N'U') IS NOT NULL SELECT 1 AS res ELSE SELECT 0 AS res"; public bool ExistsTable(string tableName) { return Convert.ToInt32(ExecuteScalar(String.Format(QueryExistsTable, tableName))) == 1; } #endregion #region Commands private static SqlParameter[] ProcessParameters(DbParameter[] par) { if (par != null) { var result = new SqlParameter[par.Length]; for (int i = 0; i < par.Length; i++) { if (par[i] is SqlParameter) { result[i] = (SqlParameter)par[i]; } else { result[i] = new SqlParameter(par[i].ParameterName, par[i].Value ?? DBNull.Value); result[i].Size = par[i].Size; } } return result; } return new SqlParameter[0]; } private static SqlCommand CreateCommand(DbConnection connection, string query, DbParameter[] parameters, int timeout) { var command = connection.CreateCommand(); command.CommandText = query; command.CommandType = CommandType.Text; if (timeout > 0) command.CommandTimeout = timeout; if (parameters != null && parameters.Length > 0) command.Parameters.AddRange(ProcessParameters(parameters)); return (SqlCommand)command; } #endregion #region SQL Server execute plan reset private const string CLEAN_PLAN_CACHEE_QUERY = "DBCC FREEPROCCACHE WITH NO_INFOMSGS;"; /// /// Выполняет удаление всех элементов из кэша планов. /// Применимо для ускорения работы SQL Server, при очистке кэша создаются новые планы /// исполнения для новых значений запросов. /// public void CleanPlanCachee() { using (var connection = _factory.CreateConnection()) { using (var cmd = CreateCommand(connection, CLEAN_PLAN_CACHEE_QUERY, null, Timeout)) { cmd.ExecuteNonQuery(); } } } #endregion #region Static methods /// /// Создает базу данных /// /// Сервер /// Название базы данных public static void CreateDatabase(string server, string database, string login, string password) { if (string.IsNullOrEmpty(server)) { throw new ArgumentException("Не указано имя сервера"); } if (string.IsNullOrEmpty(database)) { throw new ArgumentException("Не указано имя базы данных"); } using (var connection = new SqlConnection(SqlDbConnectionFactory.BuildConnectionString(server, "master", login, password))) { connection.Open(); using (var command = connection.CreateCommand()) { command.CommandText = String.Format("CREATE DATABASE {0}", database); command.ExecuteNonQuery(); } } } /// /// Выполняет проверку существования базы данных с указанным именем /// public static bool CheckDatabaseExists(string serverName, string databaseName) { string sqlExistsDBQuery; bool result = false; try { using (var tmpConn = new SqlConnection(String.Format("server={0};Trusted_Connection=yes", serverName))) { tmpConn.Open(); sqlExistsDBQuery = string.Format("SELECT database_id FROM sys.databases WHERE Name = '{0}'", databaseName); using (SqlCommand sqlCmd = new SqlCommand(sqlExistsDBQuery, tmpConn)) { object resultObj = sqlCmd.ExecuteScalar(); int databaseID = 0; if (resultObj != null) { int.TryParse(resultObj.ToString(), out databaseID); } result = (databaseID > 0); } } } catch (Exception ex) { Log.Error(ex, "Сбой при попытке подключения к серверу {0} и проверке наличия базы данных {1}", serverName, databaseName); result = false; } return result; } /// /// Удаляет базу данных /// public static void DropDatabase(string server, string database, string login, string password) { if (string.IsNullOrEmpty(server)) { throw new ArgumentException("Не указано имя сервера"); } if (string.IsNullOrEmpty(database)) { throw new ArgumentException("Не указано имя базы данных"); } using (var connection = new SqlConnection(SqlDbConnectionFactory.BuildConnectionString(server, "master", login, password))) { connection.Open(); using (var command = connection.CreateCommand()) { command.CommandText = String.Format("ALTER DATABASE {0} SET SINGLE_USER WITH ROLLBACK IMMEDIATE;\r\nDROP DATABASE [{1}]", database, database); command.ExecuteNonQuery(); } } } #endregion } }