使用存储过程最优雅的方法是什么?

时间:2009-03-27 14:20:17

标签: c# sql stored-procedures

我正在我的存储库类中重写内联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 个答案:

答案 0 :(得分: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);
    }

希望这会有所帮助:)

答案 1 :(得分: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 :(得分: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;
    }
}

答案 3 :(得分:2)

您可以使用SubSonic作为类和存储过程之间的ORM层。这是一个基本的example。 Phil Haack也有很好的article

this other question中有一些很好的信息。

编辑:由于您已更新了问题以表明您不想使用ORM,因此SubSonic不适合您。但是,我会在这里为其他使用存储过程的人留下答案。 :)您还应该看看是否有可能使用它。

答案 4 :(得分:2)

您可以通过从SqlParameter派生自己的InputSqlParameter并在构造函数中将方向设置为Input来将行数减半。

那会让你写

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

这会显示一个模式,并允许您设置参数名称和项目字段列表,并在 for 循环中添加参数。

答案 5 :(得分:2)

我建议使用Microsoft Application Blocks SqlHelper对象。

对于您上面列出的声明,我可以执行以下操作。

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

基本上SQL Helper需要一些参数。

  1. 连接到数据库的连接字符串
  2. 存储过程的名称
  3. 参数值数组,按照它们在“存储过程”中的显示顺序。
  4. 这种方法在明确创建每个参数方面存在非常轻微的性能缺陷,但节省的时间通常会使它失去平衡,因为它太小了。

答案 6 :(得分:2)

为了使代码更简洁,我总是使用

添加参数
cmd.Parameters.AddWithValue("name", item.Name);
cmd.Parameters.AddWithValue("title", item.Title);
// and so on

答案 7 :(得分: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;

答案 8 :(得分:1)

将给定存储过程的每个参数保存在“数据类”中。使用注释指定诸如“in”或“out”sproc参数之类的内容。

然后,您可以从客户端代码加载该类,然后使用反射为sproc构建所有参数,执行sproc,并将输出参数加载回数据类。

稍微清洁:有一个输入类和另一个输出类(即使有些输入/输出)。这样就很清楚(对于客户端代码)需要在路上填充哪些参数以及返回哪些参数。这也避免了对这些注释的需要。

答案 9 :(得分:0)

这很有趣我实际问了同样的问题。仍在寻找一个好的解决方案。

Which ORM is the best when using Stored Procedures

答案 10 :(得分:-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);
}

您还可以使用Enterprise Library中的SqlHelper方法来简化此语法。

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