.net SQL Server хранит структуру класса оболочки procs - PullRequest
0 голосов
/ 18 января 2011

Я хочу написать оболочку C # SQL Server для вызова некоторых хранимых процедур. Если бы я писал одну функцию, я бы сделал что-то вроде следующего (что я считаю правильным / правильным):

void RunStoredProc1(object arg1)
{
  using(SqlConnection conn = new SqlConnection(connStr)){
    try{
      SqlCommand cmd = new SqlCommand("storedProc1", conn);
      cmd.CommandType = CommandType.StoredProcedure;
      cmd.Parameters.AddWithValue("@input1", arg1);

      conn.Open();
      cmd.ExecuteNonQuery();
    } catch (Exception ex){
      //handle the exception appropriately.
    }
  }
}

Проблема, с которой я столкнулся, состоит в том, что кажется, что много повторяется код ... каждая функция будет иметь одинаковый код / ​​try (open / execute) / catch, и было бы неплохо иметь все только в одном месте. Есть ли чистый способ сделать это? Как насчет запросов, для которых я хотел бы использовать средство чтения данных?

Ответы [ 5 ]

5 голосов
/ 18 января 2011

Примерно так должно поступить:

void RunStoredProc(string storedProcName, IDictionary<string, object> args)
{
    using (SqlConnection conn = new SqlConnection(connStr))
    using (SqlCommand cmd = conn.CreateCommand())
    {
        cmd.CommandText = storedProcName;
        cmd.CommandType = CommandType.StoredProcedure;

        foreach (KeyValuePair<string, object> kvp in args)
        {
            cmd.Parameters.AddWithValue(kvp.Key, kvp.Value);
        }

        conn.Open();
        cmd.ExecuteNonQuery();
    }
}

Возможно, сам объект соединения был бы лучше использовать в качестве параметра для этого вспомогательного метода, поэтому вы можете сделать его static. Может быть интересно написать его как метод расширения на SqlConnection.

Я бы оставил обработку исключений в вашем методе RunStoredProc1 или, что более вероятно, в методах, которые вызывают RunStoredProc1, поскольку обработка исключений, вероятно, будет отличаться в каждом конкретном случае.

void RunStoredProc1(object input1)
{
    var args = new Dictionary<string, object>()
               {
                   { "input1", input1 }
               };

    try
    {
        RunStoredProc("storedProc1", args);
    }
    catch (Exception ex)
    {
        // Handle exception properly
    }
}
2 голосов
/ 18 января 2011

Просто забавное упражнение для меня, и не обязательно так, как вы хотели бы его реализовать.Я написал быстрый интерфейс для создания и выполнения SqlCommands.

Несколько примеров использования:

    int i = Sql.UsingConnection("sample")
        .GetTextCommandFor("Select Top 1 ActorID From Actor Where FirstName = @fname")
        .AddParameters(new {fname = "Bob"})
        .OnException(e => Console.WriteLine(e.Message))
        .ExecuteScalar<int>();

    var q = Sql.UsingConnection("sample")
        .GetTextCommandFor("Select * From Actor Where FirstName=@fname and ActorID > @id")
        .AddParameters(new {id = 1000, fname = "Bob"});

    using(var reader = q.ExecuteReader())
    {
        while(reader.Read())
        {
            // do something
        }
    }

Фактические классы и интерфейсы приведены ниже:

public class Sql
{
    public static ISqlCommandTypeSelector UsingConnection(string connection)
    {
        return new SqlBuilder(connection);
    }

    private class SqlBuilder : ISqlCommandTypeSelector, ISqlParameterManager, ISqlExecutor
    {
        private string _connection;
        private string _sqltext;
        private CommandType _commandtype;
        private Action<Exception> _exceptionBehavior = DefaultExceptionBehavior;

        private IList<SqlParameter> _inParams;


        public SqlBuilder(string connection)
        {
            _connection = ConfigurationManager.ConnectionStrings[connection].ConnectionString;
            _inParams = new List<SqlParameter>();
        }

        public ISqlParameterManager GetTextCommandFor(string text)
        {
            _sqltext = text;
            _commandtype = CommandType.Text;
            return this;
        }

        public ISqlParameterManager GetProcCommandFor(string proc)
        {
            _sqltext = proc;
            _commandtype = CommandType.StoredProcedure;
            return this;
        }

        public ISqlExecutor OnException(Action<Exception> action)
        {
            _exceptionBehavior = action;
            return this;
        }

        public void ExecuteNonQuery()
        {
            try
            {
                using (var connection = new SqlConnection(_connection))
                using (var cmd = connection.CreateCommand())
                {
                    ConfigureCommand(cmd);
                    PopulateParameters(cmd);
                    connection.Open();
                    cmd.ExecuteNonQuery();
                }
            }
            catch(Exception ex)
            {
                _exceptionBehavior(ex);
            }
        }

        public T ExecuteScalar<T>()
        {
            T result = default(T);
            try
            {
                using (var connection = new SqlConnection(_connection))
                using (var cmd = connection.CreateCommand())
                {
                    ConfigureCommand(cmd);
                    PopulateParameters(cmd);
                    connection.Open();
                    result = (T) cmd.ExecuteScalar();
                    return result;
                }
            }
            catch(InvalidCastException ex)
            {
                // rethrow?
            }
            catch(Exception ex)
            {
                _exceptionBehavior(ex);
            }
            return result;
        }

        public IDataReader ExecuteReader()
        {
            try
            {
                var connection = new SqlConnection(_connection);
                var cmd = connection.CreateCommand();
                ConfigureCommand(cmd);
                PopulateParameters(cmd);
                connection.Open();
                var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
                return reader;
            }
            catch(Exception ex)
            {
                _exceptionBehavior(ex);
            }
            return null;
        }

        public ISqlExecutor AddParameters(object @params)
        {
            var type = @params.GetType();
            var props = type.GetProperties();
            foreach (var propertyInfo in props)
            {
                var param = new SqlParameter("@" + propertyInfo.Name, propertyInfo.GetValue(@params, null));
                param.Direction = ParameterDirection.Input;
                _inParams.Add(param);
            }
            return this;
        }

        public ISqlExecutor WithoutParams()
        {
            return this;
        }

        private void ConfigureCommand(SqlCommand cmd)
        {
            cmd.CommandText = _sqltext;
            cmd.CommandType = _commandtype;
        }

        private void PopulateParameters(SqlCommand cmd)
        {
            cmd.Parameters.AddRange(_inParams.ToArray());
        }

        private static void DefaultExceptionBehavior(Exception e)
        {
            // do something
        }
    }


}

public interface ISqlCommandTypeSelector
{
    ISqlParameterManager GetTextCommandFor(string text);
    ISqlParameterManager GetProcCommandFor(string proc);
}

public interface ISqlExecutor
{
    ISqlExecutor OnException(Action<Exception> action);

    void ExecuteNonQuery();
    T ExecuteScalar<T>();
    IDataReader ExecuteReader();
}

public interface ISqlParameterManager
{
    ISqlExecutor AddParameters(object @params);
    ISqlExecutor WithoutParams();
}

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

1 голос
/ 18 января 2011

Я большой поклонник того, чтобы компьютеры делали рутинную, повторяющуюся работу.Они очень хороши в этом.Я только должен научить их делать это один раз.Поэтому я написал генератор кода, который использует справочную базу данных для генерации строго типизированного кода доступа.Преимущество этого метода заключается в том, что если вы изменяете сигнатуры хранимой процедуры, все, что вам нужно сделать, - это повторно создать уровень доступа к данным.Любые критические изменения приведут к ошибкам компиляции.

Код, который я написал, читает XML-файл, идентифицирующий хранимые процедуры, представляющие интерес, и получает их метаданные из указанных справочных баз данных.

XMLфайл содержит флаги, определяющие, возвращает ли каждая хранимая процедура

  • несколько наборов результатов (набор данных)
  • один набор результатов (данные)
  • одна строка (дата)
  • одна строка с одним столбцом (скалярное значение)
  • DataReader
  • XmlReader
  • или ничего (без запроса)

Исходя из этого, он генерирует соответствующий код, 1 класс на хранимую процедуру.Сгенерированный код обеспечивает доступ к коду возврата хранимой процедуры, а также к возвращаемому значению для любых выходных параметров.

Он также анализирует объявление для хранимой процедуры в исходном коде хранимой процедуры, чтобы определить любые необязательные аргументы (те,со значениями по умолчанию): сгенерированный код позволяет исключить их при вызове для выполнения хранимой процедуры.

Вызов сгенерированного кода происходит следующим образом:

    public DataTable GetRiskFactorsForPatient( int patientID )
    {
        dbo_GetRiskbyPatient sp = new dbo_GetRiskbyPatient( CONNECT_STRING_ID ) ;
        int                  rc = sp.Exec( patientID ) ;
        DataTable            dt = sp.ResultSet ;

        if ( dt == null ) throw new InvalidOperationException( "nothing returned from stored procedure" ) ;
        return dt ;
    }
1 голос
/ 18 января 2011

Блок приложения для доступа к данным Microsoft Enterprise Library может помочь уменьшить избыточный код, если вы используете чистый ADO.NET для своего уровня данных.См. http://msdn.microsoft.com/en-us/library/ff664408(v=PandP.50).aspx.. Есть много примеров кода онлайн, а также в загружаемом файле, например http://msdn.microsoft.com/en-us/library/ff664702(v=PandP.50).aspx.

.
0 голосов
/ 18 января 2011

Лично я предпочитаю

void RunStoredProc1(object arg1)
{
    try
    {
        using(SqlConnection conn = new SqlConnection(connStr))
        {
            using SqlCommand cmd = new SqlCommand("storedProc1", conn))
            {
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.AddWithValue("@input1", arg1);

                conn.Open();
                cmd.ExecuteNonQuery();
            }
        }
    } 
    catch (Exception ex)
    {
      //handle the exception appropriately.
    }
}

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

Но в целом, мне нравится делать это с отдельными методамитак что вы можете индивидуально адаптировать ваши блоки улова для sproc.

Кроме того, вам может понадобиться более одного параметра в будущем, и вы просто запутаетесь в довольно простой функции

...