2009-03-27 2 views
8

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

EDIT: Чтобы уточнить, я не ищу решение 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; 

ответ

10

В наших внутренних приложений, которые мы обычно используем класс 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); 
    } 

Надеется, что это помогает :)

+0

Ох спасибо за головы! Я этого не видел. –

+0

Ходят слухи, что EntLib будет обновлен для .NET 3.5 для поддержки LINQ. –

+0

SqlHelper также будет кэшировать параметры в _some_ перегрузки, что позволит сэкономить поездки на db. В приведенном выше примере .NET будет проходить в оба конца до db, чтобы обнаружить тип данных для параметра @ Parameter1 и т. Д. Обязательно ознакомьтесь с его использованием. – Daniel

2

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

В this other question есть хорошая информация.

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

+0

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

+0

Так что не говорите им, что это «сторонняя технология». Вы также должны обновить свой вопрос с помощью этой информации. –

+0

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

2

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

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

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

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

1

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

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

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

+0

Интересно ... У меня будет игра с этим –

-1
using (var conn = new SqlConnection(ConnectionString)) 
using (var cmd = conn.CreateCommand()) 
{ 
    cmd.CommandText = "[dbo].[Save]"; 
    cmd.CommandType = CommandType.StoredProcedure; 

    cmd.Parameters.Add(new SqlParameter(
     "Identity", SqlDbType.Int) { Value = item.Identity }); 

    cmd.Parameters.Add(new SqlParameter(
     "Name", SqlDbType.NVarChar, 50) { Value = item.Name }); 

    cmd.Parameters.Add(new SqlParameter(
     "Title", SqlDbType.NVarChar, 100) { Value = item.Title }); 

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

Вот как это может выглядеть с Ent Lib:

// Note, that you don't need to specify connection string here, 
// it will be automatically taken from a configuration file 
var db = DatabaseFactory.CreateDatabase(); 

using (var cmd = db.GetStoredProcCommand("[dbo].[Save]")) 
{ 
    db.AddInParameter(cmd, "Identity", DbType.Int32, item.Identity); 
    db.AddInParameter(cmd, "Name", DbType.String, item.Name); 
    db.AddInParameter(cmd, "Title", DbType.String, item.Title); 
    db.ExecuteNonQuery(cmd); 
} 

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

SqlHelper.ExecuteNonQuery(connectinString, 
    CommandType.StoredProcedure, "[dbo].[Save]", new SqlParameter[] 
     { 
      new SqlParameter("Identity", item.Identity), 
      new SqlParameter("Name", item.Name), 
      new SqlParameter("Title", item.Title) 
     }); 
+0

Не нужно пытаться, наконец, использовать «использование», который он вызывает, автоматически, который сам вызывает закрытие. –

0

Thats funny, я остро спросил этот же вопрос. Все еще ищут хорошее решение.

Which ORM is the best when using Stored Procedures

+0

На самом деле это противоположные вопросы. –

+0

@joe: это не тот же вопрос. –

6

захватить копию 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 
2

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

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

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

В основном SQL-помощник выполняет несколько параметров.

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

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

+0

Есть ли недостаток производительности? Разве компилятор не позаботится об этом? –

+0

Я на самом деле только что закончил, куча тестов. при сохраненной процедуре 5 параметров существует разница в 7% при выдаче от 1 до 1000 операторов. После 1000 он падает примерно до 4% или около того. Как только я ударил около 20000 вставок, не было никакой разницы. Он, похоже, связан с кешированием. –

+0

Но на самом деле, учитывая разное время, разница в производительности - это не то, что вы заметили бы разницу в 0,0003 секунды для одиночных записей. –

3

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

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

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

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

cmd.Parameters.AddWithValue("name", item.Name); 
cmd.Parameters.AddWithValue("title", item.Title); 
// and so on 
2

входа является направлением по умолчанию, и вы можете сократить параметр добавления и, вероятно, хочет объявить 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; 
Смежные вопросы