2015-10-08
zelder
2017-06-06
08/10
2015

Прямые запросы в базу без ORM.

Цель: построение прямых запросов в базу и получение моделей без использования ORM.

Класс для запросов LowAccessor.

/// <summary>
/// Низкий доступ.
/// </summary>
internal class LowAccessor
{

        /// <summary>
        /// Запрос в базу и возврат модели.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="configuration"></param>
        /// <param name="sqlCommand"></param>
        /// <param name="sqlParams"></param>
        /// <param name="onError"></param>
        /// <returns></returns>
        public List<T> SimpleSqlRequest<T> (String configuration, String sqlCommand, IDictionary<String, Object> sqlParams, Action<Exception> onError) where T : IAdvSql
        {
            var recas = new List<T>();
            SqlConnection connection = null;
            SqlCommand cmd = null;
            try
            {
                using (connection = new SqlConnection(configuration))
                {
                    connection.Open();
                    using (cmd = connection.CreateCommand())
                    {
                        cmd.CommandText = sqlCommand;
                        cmd.CommandTimeout = 15;
                        cmd.CommandType = CommandType.Text;
                        //+ параметры
                        if (sqlParams != null && sqlParams.Any())
                        {
                            foreach (var p in sqlParams)
                            {
                                cmd.AddParameter(p.Key, p.Value);
//+ или
//DbParameter parameter = new SqlParameter(p.Key, (SqlDbType)p.ParamType);
                                //parameter.Value = p.Value;
                                //cmd.Parameters.Add(parameter);
                            }
                        }
                        if (cmd.Connection.State != ConnectionState.Open)
                            cmd.Connection.Open();
                        
                        using (var reader = cmd.ExecuteReader())
                        {
                            FullfillList(recas, reader);
//+ второй результат из запроса (как правило, из хранимки)
                            //reader.NextResult();
                            //FullfillList(recas2, reader);
// и т.д.
                        }
                    }
                }
            }
            catch (Exception e)
            {
                if (onError != null) onError(e);
            }
            finally
            {
                if (connection != null) connection.Dispose();
                if (cmd != null) cmd.Dispose();
            }
            return recas;
        }
        /// <summary>
        /// Биндиг модели.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="recas"></param>
        /// <param name="reader"></param>
        private void FullfillList<T> (List<T> recas, DbDataReader reader) where T : IAdvSql
        {
            T obj = default(T);
            while (reader.Read())
            {
                obj = Activator.CreateInstance<T>();
                obj.FromEntity(reader);
                recas.Add(obj);
            }
        }
        /// <summary>
        /// Биндиг модели.
        /// </summary>
        /// <param name="recas"></param>
        /// <param name="reader"></param>
        private void FullfillList(List<ObjectWithProperties> recas, DbDataReader reader)
        {
            while (reader.Read())
            {
                var result = new ObjectWithProperties();
                for (Int32 f = 0; f < reader.VisibleFieldCount; f++)
                {
                    result[reader.GetName(f)] = reader.GetFieldValue<object>(f);
//result[reader.GetName(f)] = reader.GetValue(f); // зависит от версии .NET
                }
                recas.Add(result);
            }
        }

}

Если необходимо отказаться от отражения (Activator.CreateInstance), то можно создать не Генерик метод SimpleSqlRequest аналогичный текущему.
Использующий для возврата тип ObjectWithProperties (содержащий словарь данных, и возможно, реализацию приведения к нужным типам).
Метод FullfillList подставится необходимый. 


Пример моделей IAdvSql с реализацией биндинга 
    /// <summary>
    /// Интерфейс модели с биндингом данных.
    /// </summary>
    public interface IAdvSql
    {
        void FromEntity(IDataReader entity);
    }
    /// <summary>
    /// Простой пример с биндингом данных.
    /// </summary>
    public class AdvMini : IAdvSql
    {
        public String Layout { get; set; }
        public String Style { get; set; }
        public Int32 FileSize { get; set; }
        public DateTime Date { get; set; }
        public void FromEntity(IDataReader entity)
        {
            Layout = entity.GetString(0);
            Style = entity.GetString(1);
            FileSize = entity.GetInt32(2);
            Date = entity.GetDateTime(entity.GetOrdinal("CreationDateTime"));
        }
    }


Простой пример клиента с запросом.
    /// <summary>
    /// Клиент.
    /// </summary>
    internal class AdvClient : LowAccessor
    {
        private String _configurationString = "";
        private Action<Exception> _onError = null;
        public AdvBannerClient(String configurationString, Action<Exception> onError)
        {
            _configurationString = String.IsNullOrWhiteSpace(configurationString)
? "data source=127.0.0.1;initial catalog=SiteBase;persist security info=True;user id=username;password=pass;MultipleActiveResultSets=True;App=DynamicSite"
: configurationString;
            _onError = onError;
        }
        private List<ObjectWithProperties> SqlRequest(String sqlCommand, IDictionary<String, Object> sqlParams)
        {
            return this.SimpleSqlRequest(_configurationString, sqlCommand, sqlParams, _onError);
        }
        private List<T> SqlRequest<T>(String sqlCommand, IDictionary<String, Object> sqlParams) where T : IAdvSql
        {
            return this.SimpleSqlRequest<T>(_configurationString, sqlCommand, sqlParams, _onError);
        }

        /// <summary>
        /// Пример запроса.
        /// </summary>
        /// <returns></returns>
        public List<AdvMini> GetRequest()
        {
            var sql = String.Format(@"
SELECT TOP 5
                          [Layout]
                          ,[Style]
                          ,[FileSize]
                          ,[CreationDateTime]
                      FROM [SiteBase].[dbo].[tAdv_Layout]
                      ");
            return SqlRequest<AdvMini>(sql, null);
        }
}