Какой самый элегантный способ использовать хранимые процедуры? - PullRequest
8 голосов
/ 27 марта 2009

Я переписываю встроенный SQL в своем классе репозитория, чтобы вместо него использовать хранимые процедуры (требование безопасности). После использования Fluent NHibernate и Linq2Sql в прошлом я нахожу его чрезвычайно громоздким и не элегантным.

РЕДАКТИРОВАТЬ : Чтобы уточнить, я не ищу решение ORM, которое работает с сохраненными процессами. Я просто хочу посоветовать хороший способ написать код ниже.

Существуют ли стратегии, позволяющие сделать этот код максимально элегантным?

        string commandText = "dbo.Save";

        using (SqlConnection sql = new SqlConnection(_connString.ConnectionString))
        using (SqlCommand cmd = sql.CreateCommand())
        {
            cmd.CommandText = commandText;
            cmd.CommandType = CommandType.StoredProcedure;

            SqlParameter idParam = new SqlParameter("identity", item.Identity);
            idParam.Direction = ParameterDirection.Input;

            SqlParameter nameParam = new SqlParameter("name", item.Name);
            nameParam.Direction = ParameterDirection.Input;

            SqlParameter descParam = new SqlParameter("desc", item.Description);
            descParam.Direction = ParameterDirection.Input;

            SqlParameter titleParam = new SqlParameter("title", item.)
            descParam.Direction = ParameterDirection.Input;

            //SNIP More parameters

            cmd.Parameters.Add(idParam);
            cmd.Parameters.Add(descParam);
            cmd.Parameters.Add(titleParam);
            //SNIP etc

            sql.Open();

            cmd.ExecuteNonQuery();

            //Get out parameters
        }

        return item;

Ответы [ 11 ]

10 голосов
/ 27 марта 2009

В наших внутренних приложениях мы обычно используем класс SqlHelper, который можно найти по следующей ссылке (скачать и описание): http://www.microsoft.com/downloads/details.aspx?familyid=f63d1f0a-9877-4a7b-88ec-0426b48df275&displaylang=en

По сути, класс SqlHelper устраняет необходимость объявлять объекты подключения, команды и т. Д. И позволяет вызывать методы для возврата таких объектов, как DataSet

Вы можете использовать SqlHelper следующим образом:

public static int UpdateItem(int parameter1, int parameter2, string parameter3)
    {
        SqlParameter[] arParam = new SqlParameter[3];
        arParam[0] = new SqlParameter("@Parameter1", lotId);
        arParam[1] = new SqlParameter("@Parameter2", saleId);
        arParam[2] = new SqlParameter("@Parameter3", lotNumber);


        return int.Parse(SqlHelper.ExecuteScalar(connString, CommandType.StoredProcedure, "spName", arParam).ToString(), CultureInfo.InvariantCulture);
    }

Надеюсь, это поможет:)

6 голосов
/ 27 марта 2009

Получить копию Enterprise Library . Это хорошая обертка вокруг ADO. Например:

using System.Data.Common;
using System.Globalization;
using Microsoft.Practices.EnterpriseLibrary.Data;

Database db = DatabaseFactory.CreateDatabase(DatabaseType.MyDatabase.ToString());

using (DbCommand dbCommand = db.GetStoredProcCommand("dbo.MyStoredProc")) {
    db.AddInParameter(dbCommand, "identity", DbType.Int32, item.Identity);
    db.AddInParameter(dbCommand, "name", DbType.String, item.Name);
    db.AddInParameter(dbCommand, "desc", DbType.String, item.Description);
    db.AddInParameter(dbCommand, "title", DbType.String, item.Title);

    db.ExecuteNonQuery(dbCommand);
} // using dbCommand
3 голосов
/ 27 марта 2009

Я обычно использую некоторые варианты следующего примера, в зависимости от среды, конечно:

Мои основные вспомогательные методы, которые я вызываю по всему коду

public static SqlCommand CreateStoredProcCmd(string name, SqlConnection con)
{
    var cmd = new SqlCommand(name, con);
    cmd.CommandType = CommandType.StoredProcedure;
    return cmd;
}

public static void AddParams(this SqlCommand cmdObject, Params SqlParameter[] parameters)
{
  foreach(SqlParameter param in parameters)
  {
    cmdObject.Parameters.add(param);
  }
}

/* Any overloaded methods to create params receiving my param definitions 
in any manner that the usual new SqlParameter() constructor doesn't handle */
public static SqlParameter CreateSqlParam(string ParamName,
                                          SqlDbType ParamType,
                                          object value)
{
    return CreateSqlParam(ParamName, ParamType, ParameterDirection.Input, value);
}

public static SqlParameter CreateSqlParam(string ParamName, 
                                          SqlDbType ParamType, 
                                          ParameterDirection ParamDir)
{
    return CreateSqlParam(ParamName, ParamType, ParamDir, null;
}                          

public static SqlParameter CreateSqlParam(string ParamName, 
                                          SqlDbType ParamType, 
                                          ParameterDirection ParamDir,
                                          object value)
{
    var parm = new SqlParameter(ParamName, ParamType);
    parm.Direction = ParamDir;
    parm.Value = value;
    return parm;
}

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

public static string DoStuff()
{
    using (var oCon = new SqlConnection("MyConnectionString"))
    {
        oCon.Open();
        var oCmd = CreateStoredProcCmd("sp_Name", oCon).AddParams(
            CreateSqlParam("Param1", SqlDBType.Int, 3),
            CreateSqlParam("Param2", SqlDBType.VarChar, "Hello World"),
            CreateSqlParam("Param3", SqlDBType.VarChar, ParameterDirection.Output)
        );
        oCmd.Prepare();
        oCmd.ExecuteNonQuery();
        object outVal = oCmd.Parameters["Param3"];
        return null != outVal ? outVal.ToString() : String.Empty;
    }
}
2 голосов
/ 27 марта 2009

Ввод является направлением по умолчанию, и вы можете сократить добавление параметров и, возможно, захотите также объявить SqlDBTypes ...

cmd.Parameters.Add("identity", SqlDBType.???).Value = item.Identity;
cmd.Parameters.Add("desc", SqlDbType.???, ?size?).Value = item.Description;
cmd.Parameters.Add("title", SqlDBType.???, ?size?).Value = item.Title;

//Output params generally don't need a value so...
cmd.Parameters.Add("someOut", SqlDBType.???).Direction = ParameterDirection.Output;
2 голосов
/ 27 марта 2009

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

cmd.Parameters.AddWithValue("name", item.Name);
cmd.Parameters.AddWithValue("title", item.Title);
// and so on
2 голосов
/ 27 марта 2009

Я бы порекомендовал использовать объект SqlHelper блоков приложений Microsoft.

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

SqlHelper.ExecuteNonQuery(_connectionString, "MyProcName", 1, "NameValue", "Description", "Title");

В основном SQL Helper принимает несколько параметров.

  1. Строка подключения для подключения к БД
  2. Имя хранимой процедуры
  3. Массив значений параметров в порядке их появления в хранимой процедуре.

У этого ОЧЕНЬ небольшого недостатка производительности по сравнению с явным созданием каждого параметра, но экономия времени обычно перевешивает его, поскольку он очень мал.

2 голосов
/ 27 марта 2009

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

Это позволит вам написать

    cmd.Parameters.Add(new InputSqlParameter("title", item.title));
    cmd.Parameters.Add(new InputSqlParameter("property", item.property));

Здесь показан шаблон, и вы можете настроить список имен параметров и полей элементов и выполнить добавление параметров в цикле для .

2 голосов
/ 27 марта 2009

Вы можете использовать SubSonic в качестве слоя ORM между вашим классом и хранимыми процедурами. Вот простой пример . У Фила Хаака тоже есть хорошая статья .

В этом другом вопросе есть хорошая информация .

РЕДАКТИРОВАТЬ: Поскольку вы обновили свой вопрос, чтобы указать, что вы не хотите использовать ORM, SubSonic не для вас. Однако я оставлю здесь ответ для других людей, которые используют хранимые процедуры. :) Вы также должны взглянуть, есть ли возможность использовать его.

1 голос
/ 27 марта 2009

Храните каждый параметр для данной хранимой процедуры в «классе данных». Используйте аннотации для указания таких параметров, как "in" или "out" sproc.

Затем вы можете загрузить класс из клиентского кода, затем использовать отражение, чтобы построить все параметры для sproc, выполнить sproc и загрузить выходные параметры обратно в класс данных.

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

0 голосов
/ 27 марта 2009

Это смешно, но я задал этот же вопрос. Все еще ищу хорошее решение.

Какой ORM лучше при использовании хранимых процедур

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...