【问题标题】:Minimize code repeatednesses when calling Stored Procedures调用存储过程时尽量减少代码重复
【发布时间】:2016-09-21 07:42:09
【问题描述】:

我正在使用某种方法体来调用存储过程,示例代码如下:

     public void StoredProcedureThatIsBeingcalled(int variable_1, int variable_2, out DataSet ds)
 {
     using (SqlConnection con = new SqlConnection(DatabaseConnectionString))
     {
         ds = new DataSet("DsToGoOut");
         using (SqlCommand cmd = new SqlCommand("StoredProcedureThatIsBeingcalled", DbConn.objConn))
         {
             cmd.CommandType = CommandType.StoredProcedure;


             cmd.Parameters.Add(new SqlParameter("@variable_1", variable_1));
             cmd.Parameters.Add(new SqlParameter("@variable_2", variable_2));
             try
             {
                 con.Open();
                 SqlDataAdapter objDataAdapter = new SqlDataAdapter();
                 objDataAdapter.SelectCommand = cmd;

                 objDataAdapter.Fill(ds);

                 con.Close();
             }
             catch (Exception ex)
             {

                 //sql_log_err
             }

         }
     }
 }

让我烦恼的是,对于我调用的每个不同的过程,上述大部分代码都在我的 cs 文件中一次又一次地重复。

显然我可以清除它并使用过程名称作为变量调用一个函数,但是我如何为它提供不同数量的参数(具有不同的数据类型 - int,string bool - 从来没有其他任何东西)我使用的不同程序?

我可以有几个不同数量的参数(0-10)的不同功能,但我觉得有更好的方法吗?

【问题讨论】:

标签: c# asp.net


【解决方案1】:

更新

我知道这是一个非常古老的问题(事实上,我只是在搜索另一个旧答案时才偶然发现它,我给了别人以关闭为重复),但我最近有released a git hub project 回答了这个非常需要的问题。 它通过封装连接、命令、参数和数据适配器,最大限度地减少使用 ADO.Net 时的代码重复。
如果您想尝试一下,我很高兴知道您的想法。

第一版

您可以使用辅助类来封装 sql 参数并创建一个方法来处理所有数据集填充,如下所示:

助手类:

private class SqlParamDefinition
{

    public SqlParamDefinition(string name, SqlDbType dbType, object value)
    {
        this.Name = name;
        this.DbType = dbType;
        this.Value = value;
    }

    public string Name { get; }
    public SqlDbType DbType { get; }

    public object Value { get; }


}

执行方法(根据你发布的方法):

public DataSet ExecuteSelectProcedure(string procedeureName, params SqlParamDefinition[] parameters)
{
    var ds = new DataSet();
    using (var con = new SqlConnection(DatabaseConnectionString))
    {

        using (var cmd = new SqlCommand(procedeureName, DbConn.objConn))
        {
            cmd.CommandType = CommandType.StoredProcedure;

            for(int i = 0; i < parameters.Length; i++)
            {
                var param = parameters[i];
                cmd.Parameters.Add(new SqlParameter(param.Name, param.DbType).Value = param.Value);
            }

            try
            {
                con.Open();
                var objDataAdapter = new SqlDataAdapter();
                objDataAdapter.SelectCommand = cmd;

                objDataAdapter.Fill(ds);

                con.Close();
            }
            catch (Exception ex)
            {

                //sql_log_err
            }

        }
    }
    return ds;
}

调用示例:

var parameters = new SqlParamDefinition[]
{
    new SqlParamDefinition("@Param1", SqlDbType.VarChar, "value1"),
    new SqlParamDefinition("@Param2", SqlDbType.VarChar, "value2"),
    new SqlParamDefinition("@Param3", SqlDbType.Int, 123),
};

var ds = ExecuteSelectProcedure("Strong procedure name", parameters);

【讨论】:

  • 跟我想象的差不多!
  • 你的 SqlParamDefinition 类看起来很像 SqlParameter 的副本
  • @Caleth 是的,它是它的简化版本。可以只使用 SqlParameter 类。
【解决方案2】:

所以,

您可以使用 dapper 来执行存储过程。 https://github.com/StackExchange/dapper-dot-net

您可以声明一个 DTO 模型,该模型将使用您在存储过程中选择的内容进行映射。

public class DogDto
{
    public int? Age { get; set; }
    public int Id { get; set; }
    public string Name { get; set; }
    public float? Weight { get; set; }

    public int IgnoredProperty { get { return 1; } }
}   

// _databaseConnectionString is your database connection string
using (var conn = new SqlConnection(_databaseConnectionString)){
var dog = cnn.Query<DogDto>("schema.spGetDog", new {Id = 120}, 
        commandType: CommandType.StoredProcedure).SingleOrDefault();
 }

// and let's assume we have schema.spGetDog stored procedure already declared in our database 
// and be aware that the 2nd parameter after the stored procedure name are the stored procedure parameters

您的存储过程必须选择列 Age、Id、Name、Weight(通过属性名称实现映射)。我真的不知道您是否可以更改此操作,因为如果您在使用存储过程的数据库/dto 中具有相同的列/属性,则开发过程会更快。

仅作记录,根据文档:“Dapper 没有特定于 DB 的实现细节,它适用于所有 .NET ADO 提供程序,包括 SQLite、SQL CE、Firebird、Oracle、MySQL、PostgreSQL 和 SQL Server。 "

这就是你需要做的。

【讨论】:

    【解决方案3】:

    您可以创建接受stringDictionary&lt;string,object&gt; 作为参数的方法。现在你可以根据命令文本和参数字典来构造你的命令了。此外,您可以扩展此方法并将其用于选择、插入等查询。

    例子:

    private void ExecCommand(string commandText, Dictionary<string, object> param)
    {
     using (SqlConnection con = new SqlConnection(DatabaseConnectionString))
     {
         ds = new DataSet("DsToGoOut");
         using (SqlCommand cmd = new SqlCommand(commandText, DbConn.objConn))
         {
             cmd.CommandType = CommandType.StoredProcedure;
    
             //***************************************
             // New method
             cmd = AddParametersToCommand(cmd, param);
             //***************************************
    
             try
             {
                 con.Open();
                 SqlDataAdapter objDataAdapter = new SqlDataAdapter();
                 objDataAdapter.SelectCommand = cmd;
    
                 objDataAdapter.Fill(ds);
    
                 con.Close();
             }
             catch (Exception ex)
             {
    
                 //sql_log_err
             }
    
         }
     }
    }
    

    还有AddParametersToCommand:

    private SQLCommand AddParametersToCommand(SqlCommand command, Dictionary<string, object> parameters)
    {
        if (parameters == null || command == null)
        {
            return;
        }
    
    
        SQLCommand tempCommand = command;
    
        foreach (var param in parameters)
        {
            var parameter = tempCommand.CreateParameter();
            parameter.ParameterName = param.Key;
            parameter.Value = param.Value ?? DBNull.Value;
            tempCommand.Parameters.Add(parameter);
        }
    
        return tempCommand;
    }
    

    并像这样使用它:

    Dictionary<string, object> parameters = new Dictionary<string, object>();
    parameters.Add("@variable_1", variable_1);
    parameters.Add("@variable_2", variable_2);
    
    ExecCommand("StoredProcedureThatIsBeingcalled", parameters);
    

    【讨论】:

    • 这种方法的缺点是您最终会猜测参数类型,或者只是将它们全部设为nvarchar(max) 或类似的,这可能会导致执行计划明显不理想。
    【解决方案4】:

    我遇到了这个问题;我在多个数据库上调用存储过程。您可以存储存储过程的详细信息,例如数据库表中的名称、输入参数、输出参数等,然后使用工厂方法填充对象(以下示例中的 clsStoredProcedure)。代码看起来像这样(我没有测试过代码):

    public void StoredProcedureThatIsBeingcalled(clsStoredProcedure objStoredProcedure)
     {
         using (SqlConnection con = new SqlConnection(objStoredProcedure.ConnectionString))
         {
             ds = new DataSet("DsToGoOut");
             using (SqlCommand cmd = new SqlCommand(objStoredProcedure.Name, DbConn.objConn))
             {
                 cmd.CommandType = CommandType.StoredProcedure;
    foreach (Parameter p in clsStoredProcedure.Parameters)
    {
           cmd.Parameters.Add(new SqlParameter(p.name, p.value));
    }
    
                 try
                 {
                     con.Open();
                     SqlDataAdapter objDataAdapter = new SqlDataAdapter();
                     objDataAdapter.SelectCommand = cmd;
    
                     objDataAdapter.Fill(ds);
    
                     con.Close();
                 }
                 catch (Exception ex)
                 {
    
                     //sql_log_err
                 }
    
             }
         }
     }
    

    如果您要连接到 Oracle 数据库和 SQL 数据库,那么您可以使用 dbConnection、dbCommand 等连接到数据库。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多