Рефакторинг кода DAL для поддержки хранимых процедур - PullRequest
0 голосов
/ 11 мая 2009
    private static readonly string dataProvider = ConfigurationManager.AppSettings.Get("Provider");
    private static readonly DbProviderFactory factory = DbProviderFactories.GetFactory(dataProvider);
    private static readonly string connectionString = ConfigurationManager.ConnectionStrings[dataProvider].ConnectionString;
    /// <summary>
    /// Executes Update statements in the database.
    /// </summary>
    /// <param name="sql">Sql statement.</param>
    /// <returns>Number of rows affected.</returns>
    public static int Update(string sql)
    {
        using (DbConnection connection = factory.CreateConnection())
        {
            connection.ConnectionString = connectionString;

            using (DbCommand command = factory.CreateCommand())
            {
                command.Connection = connection;
                command.CommandText = sql;

                connection.Open();
                return command.ExecuteNonQuery();
            }
        }
    }

Мне нужна помощь в переписывании этого, чтобы оно работало с хранимыми процедурами. (Передать имя и параметры sproc) У кого-нибудь есть идеи, как мне поступить? Изменить: область, с которой у меня проблемы, пытается выяснить способы заполнения параметров.

Спасибо

Ответы [ 5 ]

10 голосов
/ 11 мая 2009

Вам уже нужны параметры независимо от того, внедряете ли вы хранимые процедуры.

Прямо сейчас ваш код может быть вызван с запросом, подобным SELECT * FROM Table WHERE ID = @ID, в этом случае вам уже нужно передать Dictionary<string,object> params. Заставьте свой код заполнить коллекцию параметров уже имеющейся у вас команды и проверьте ее, прежде чем беспокоиться о хранимых процедурах.

Как только это сработает, вы должны просто создать перегрузку, которая принимает bool, который говорит, что это хранимая процедура, а затем использовать ее для установки свойства CommandType команды.


Редактировать: Вот как я бы это изобразил

Шаг 1: обобщить обновление

В методе Update нет ничего особенного, что не позволяет использовать его для других операций, не связанных с запросом, кроме имени. Итак:

    /// <summary>
    /// Executes Update statements in the database.
    /// </summary>
    /// <param name="sql">Sql statement.</param>
    /// <returns>Number of rows affected.</returns>
    public static int Update(string sql)
    {
        return NonQuery(sql);
    }

    public static int NonQuery(string sql)
    {
        using (DbConnection connection = factory.CreateConnection())
        {
            connection.ConnectionString = connectionString;

            using (DbCommand command = factory.CreateCommand())
            {
                command.Connection = connection;
                command.CommandText = sql;

                connection.Open();
                return command.ExecuteNonQuery();
            }
        }
    }

Шаг 2: А как насчет параметров?

Ваш текущий код не может даже обрабатывать запросы UPDATE, которые используют параметры, поэтому давайте начнем исправлять это. Во-первых, убедитесь, что он все еще работает, если параметры не указаны:

    public static int NonQuery(string sql)
    {
        Dictionary<string, object> parameters = null;

        if (parameters == null)
        {
            parameters = new Dictionary<string, object>();
        }

        using (DbConnection connection = factory.CreateConnection())
        {
            connection.ConnectionString = connectionString;

            using (DbCommand command = factory.CreateCommand())
            {
                command.Connection = connection;
                command.CommandText = sql;

                foreach (KeyValuePair<string, object> p in parameters)
                {
                    var parameter = command.CreateParameter();
                    parameter.ParameterName = p.Key;
                    parameter.Value = p.Value;

                    command.Parameters.Add(parameter);
                }

                connection.Open();
                return command.ExecuteNonQuery();
            }
        }
    }

Как только это сработает, выдвиньте параметров в качестве параметра. Это не влияет на существующие абоненты Update:

    /// <summary>
    /// Executes Update statements in the database.
    /// </summary>
    /// <param name="sql">Sql statement.</param>
    /// <returns>Number of rows affected.</returns>
    public static int Update(string sql)
    {
        return NonQuery(sql, null);
    }

    public static int NonQuery(string sql, Dictionary<string, object> parameters)

На этом этапе протестируйте NonQuery с параметризованным запросом. Как только это сработает, создайте перегрузку Update, которая принимает параметры:

    /// <summary>
    /// Executes Update statements in the database.
    /// </summary>
    /// <param name="sql">Sql statement.</param>
    /// <returns>Number of rows affected.</returns>
    public static int Update(string sql)
    {
        return NonQuery(sql, null);
    }

    /// <summary>
    /// Executes Update statements in the database.
    /// </summary>
    /// <param name="sql">Sql statement.</param>
    /// <param name="parameters">Name/value dictionary of parameters</param>
    /// <returns>Number of rows affected.</returns>
    public static int Update(string sql, Dictionary<string, object> parameters)
    {
        return NonQuery(sql, parameters);
    }

Шаг 3: Принять хранимые процедуры на счет

Существует небольшая разница в том, как будут обрабатываться хранимые процедуры. То, что вы уже получили, подразумевается следующим образом:

            using (DbCommand command = factory.CreateCommand())
            {
                command.Connection = connection;
                command.CommandText = sql;
                command.CommandType = CommandType.Text;

Итак, возьмите CommandType.Text и сделайте его параметром перегрузки:

    public static int NonQuery(string sql, Dictionary<string, object> parameters)
    {
        return NonQuery(sql, CommandType.Text, parameters);
    }

    public static int NonQuery(string sql, CommandType commandType, Dictionary<string, object> parameters)

Наконец, если хотите, обновите Update:

    /// <summary>
    /// Executes Update statements in the database.
    /// </summary>
    /// <param name="sql">Sql statement.</param>
    /// <param name="parameters">Name/value dictionary of parameters</param>
    /// <returns>Number of rows affected.</returns>
    public static int Update(string sql, Dictionary<string, object> parameters)
    {
        return Update(sql, CommandType.Text, parameters);
    }

    /// <summary>
    /// Executes Update statements in the database.
    /// </summary>
    /// <param name="sql">Sql statement.</param>
    /// <param name="commandType">CommandType.Text or CommandType.StoredProcedure</param>
    /// <param name="parameters">Name/value dictionary of parameters</param>
    /// <returns>Number of rows affected.</returns>
    public static int Update(string sql, CommandType commandType, Dictionary<string, object> parameters)
    {
        return NonQuery(sql, parameters);
    }

Конечно, в качестве заключительного упражнения для читателя вы можете заменить все ваши вызовы на обновление вызовами NonQuery и полностью избавиться от обновления.


Конечно, этот простой метод не обрабатывает выходные параметры или ситуации, когда необходимо указать DbType параметра. Для этого вам нужно принять коллекцию ParameterCollection некоторого вида.

0 голосов
/ 11 мая 2009

Вам может быть трудно с этим, в зависимости от среды, в которой вы планируете это разместить.

Если я правильно понимаю, вы хотели бы сохранить сигнатуру функции (единственный строковый параметр), но обрабатывать ее иначе, если первое слово в строке не «ОБНОВЛЕНИЕ». Например, эта строка должна выполнять сохраненный процесс с именем UpdateTable1 с четырьмя аргументами:

UpdateTable1 NULL, A5KMI, "New description", #5/10/2009#

или что-то в этом роде.

Проблема в том, что вам нужен разделитель между аргументами в остальной части строки. Для этого вам придется разбить строку на токены, и это может стать настоящим трюком.

Таким образом, альтернативой является добавление команды EXEC перед всей строкой, установка всего этого свойства в свойстве CommandText и затем вызов одного из методов Execute.

Что мне не нравится в этом, так это то, что он действительно открывает для вас уязвимость SQL-инъекций. Если это вообще возможно, используйте перегруженные методы, подобные тем, с которыми ответили другие, и сделайте так, чтобы вызывающая сторона разделяла аргументы на какую-то коллекцию.

0 голосов
/ 11 мая 2009

Я делаю нечто похожее на это, используя интерфейс и дженерики.

Пример

    using System.Data.SqlClient;

public interface IParameter<T> where T : IEntity<T>
{
    void Populate(SqlParameterCollection parameters, T entity);
}

Класс, который реализует интерфейс используя System.Data; using System.Data.SqlClient;

public class UserParameter : IParameter<User>
{
    public void Populate(SqlParameterCollection parameters, User entity)
    {
        parameters.Add("@ID", SqlDbType.UniqueIdentifier).Value = entity.Id;
        parameters.Add("@Name", SqlDbType.NVarChar, 255).Value = entity.Name;
        parameters.Add("@EmailAddress", SqlDbType.NVarChar, 255).Value = entity.EmailAddress;
    }
}

Тогда у нас есть метод, который выполняет обновление

            public void Update<T>(string prefix, IParameter<T> parameters, T entity)
        where T : class, IEntity<T>
    {
        using (var connection = this.Connection())
        {
            using (var command = new SqlCommand(string.Format("dbo.{0}_Update", prefix), connection))
            {
                command.CommandType = CommandType.StoredProcedure;

                parameters.Populate(command.Parameters, entity);

                connection.Open();

                command.ExecuteNonQuery();

                connection.Close();
            }
        }
    }

Затем я называю это чем-то вроде

Обновление («Пользователи», новый UserParameter (), значение);

Я также делаю то же самое для заполнения сущности из значений считывателя.

0 голосов
/ 11 мая 2009

Используйте перегрузку, такую ​​как

public static int Update(string storedProc, Dictionary<string,string> params)
{
    ...
}
0 голосов
/ 11 мая 2009

Вы можете добавить 3 параметра:

  1. string SchemaName - сохранить имя схемы базы данных
  2. string StoredProcName - сохранить имя хранимой процедуры
  3. List<Parameter> - список параметров вашей хранимой процедуры
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...