重构DAL代码以支持存储过程

时间:2009-05-10 21:24:32

标签: c# refactoring data-access-layer

    private static readonly string dataProvider = ConfigurationManager.AppSettings.Get("Provider");
    private static readonly DbProviderFactory factory = DbProviderFactories.GetFactory(dataProvider);
    private static readonly string connectionString = ConfigurationManager.ConnectionStrings[dataProvider].ConnectionString;
    /// <summary>
    /// Executes Update statements in the database.
    /// </summary>
    /// <param name="sql">Sql statement.</param>
    /// <returns>Number of rows affected.</returns>
    public static int Update(string sql)
    {
        using (DbConnection connection = factory.CreateConnection())
        {
            connection.ConnectionString = connectionString;

            using (DbCommand command = factory.CreateCommand())
            {
                command.Connection = connection;
                command.CommandText = sql;

                connection.Open();
                return command.ExecuteNonQuery();
            }
        }
    }

我需要帮助重写这个,以便它可以与存储过程一起使用。 (通过sproc名称和params) 有谁知道我应该怎么做呢? 编辑:我遇到问题的领域是试图找出填写参数的方法。

由于

5 个答案:

答案 0 :(得分:10)

您已经需要参数,而不管您是否正在实施存储过程。

现在,您可以使用SELECT * FROM Table WHERE ID = @ID之类的查询调用您的代码,在这种情况下,您需要传递Dictionary<string,object> params。让您的代码填入您已有命令的Parameters集合,并在担心存储过程之前对其进行测试。

一旦有效,你应该简单地创建一个重载,接受一个bool,说这是一个存储过程,然后用它来设置命令的CommandType属性。


编辑:这是我将如何重构

第1步:概括更新

除了名称之外,Update方法没有任何特殊之处可以阻止它用于其他非查询操作。所以:

    /// <summary>
    /// Executes Update statements in the database.
    /// </summary>
    /// <param name="sql">Sql statement.</param>
    /// <returns>Number of rows affected.</returns>
    public static int Update(string sql)
    {
        return NonQuery(sql);
    }

    public static int NonQuery(string sql)
    {
        using (DbConnection connection = factory.CreateConnection())
        {
            connection.ConnectionString = connectionString;

            using (DbCommand command = factory.CreateCommand())
            {
                command.Connection = connection;
                command.CommandText = sql;

                connection.Open();
                return command.ExecuteNonQuery();
            }
        }
    }

第2步:参数怎么样?

您当前的代码甚至无法处理使用参数的UPDATE查询,所以让我们开始修复它。首先,如果没有指定参数,请确保它仍然有效:

    public static int NonQuery(string sql)
    {
        Dictionary<string, object> parameters = null;

        if (parameters == null)
        {
            parameters = new Dictionary<string, object>();
        }

        using (DbConnection connection = factory.CreateConnection())
        {
            connection.ConnectionString = connectionString;

            using (DbCommand command = factory.CreateCommand())
            {
                command.Connection = connection;
                command.CommandText = sql;

                foreach (KeyValuePair<string, object> p in parameters)
                {
                    var parameter = command.CreateParameter();
                    parameter.ParameterName = p.Key;
                    parameter.Value = p.Value;

                    command.Parameters.Add(parameter);
                }

                connection.Open();
                return command.ExecuteNonQuery();
            }
        }
    }

一旦有效,将参数提升为参数。这不会影响Update的任何现有呼叫者:

    /// <summary>
    /// Executes Update statements in the database.
    /// </summary>
    /// <param name="sql">Sql statement.</param>
    /// <returns>Number of rows affected.</returns>
    public static int Update(string sql)
    {
        return NonQuery(sql, null);
    }

    public static int NonQuery(string sql, Dictionary<string, object> parameters)

此时,使用参数化查询测试NonQuery。一旦有效,创建一个接受参数的Update重载:

    /// <summary>
    /// Executes Update statements in the database.
    /// </summary>
    /// <param name="sql">Sql statement.</param>
    /// <returns>Number of rows affected.</returns>
    public static int Update(string sql)
    {
        return NonQuery(sql, null);
    }

    /// <summary>
    /// Executes Update statements in the database.
    /// </summary>
    /// <param name="sql">Sql statement.</param>
    /// <param name="parameters">Name/value dictionary of parameters</param>
    /// <returns>Number of rows affected.</returns>
    public static int Update(string sql, Dictionary<string, object> parameters)
    {
        return NonQuery(sql, parameters);
    }

第3步:将存储过程纳入考虑

存储过程的处理方式几乎没有什么区别。您已经得到的内容隐含如下:

            using (DbCommand command = factory.CreateCommand())
            {
                command.Connection = connection;
                command.CommandText = sql;
                command.CommandType = CommandType.Text;

因此,请使用CommandType.Text并将其提升为重载中的参数:

    public static int NonQuery(string sql, Dictionary<string, object> parameters)
    {
        return NonQuery(sql, CommandType.Text, parameters);
    }

    public static int NonQuery(string sql, CommandType commandType, Dictionary<string, object> parameters)

最后,如果您愿意,请更新Update

    /// <summary>
    /// Executes Update statements in the database.
    /// </summary>
    /// <param name="sql">Sql statement.</param>
    /// <param name="parameters">Name/value dictionary of parameters</param>
    /// <returns>Number of rows affected.</returns>
    public static int Update(string sql, Dictionary<string, object> parameters)
    {
        return Update(sql, CommandType.Text, parameters);
    }

    /// <summary>
    /// Executes Update statements in the database.
    /// </summary>
    /// <param name="sql">Sql statement.</param>
    /// <param name="commandType">CommandType.Text or CommandType.StoredProcedure</param>
    /// <param name="parameters">Name/value dictionary of parameters</param>
    /// <returns>Number of rows affected.</returns>
    public static int Update(string sql, CommandType commandType, Dictionary<string, object> parameters)
    {
        return NonQuery(sql, parameters);
    }

当然,作为读者的最后练习,您可以使用对NonQuery的调用替换所有Update调用,并完全摆脱更新。


当然,这种简单的技术不处理输出参数,或者需要指定参数的DbType的情况。为此,您需要接受某种ParameterCollection。

答案 1 :(得分:0)

您可以添加3个参数:

  1. string SchemaName - 存储数据库架构的名称
  2. string StoredProcName - 存储存储过程的名称
  3. List<Parameter> - 存储过程参数列表

答案 2 :(得分:0)

使用重载,例如

public static int Update(string storedProc, Dictionary<string,string> params)
{
    ...
}

答案 3 :(得分:0)

我使用界面和泛型做类似的事情。

实施例

    using System.Data.SqlClient;

public interface IParameter<T> where T : IEntity<T>
{
    void Populate(SqlParameterCollection parameters, T entity);
}

实现接口的类         使用System.Data;     使用System.Data.SqlClient;

public class UserParameter : IParameter<User>
{
    public void Populate(SqlParameterCollection parameters, User entity)
    {
        parameters.Add("@ID", SqlDbType.UniqueIdentifier).Value = entity.Id;
        parameters.Add("@Name", SqlDbType.NVarChar, 255).Value = entity.Name;
        parameters.Add("@EmailAddress", SqlDbType.NVarChar, 255).Value = entity.EmailAddress;
    }
}

然后我们有执行更新的方法

            public void Update<T>(string prefix, IParameter<T> parameters, T entity)
        where T : class, IEntity<T>
    {
        using (var connection = this.Connection())
        {
            using (var command = new SqlCommand(string.Format("dbo.{0}_Update", prefix), connection))
            {
                command.CommandType = CommandType.StoredProcedure;

                parameters.Populate(command.Parameters, entity);

                connection.Open();

                command.ExecuteNonQuery();

                connection.Close();
            }
        }
    }

然后我做了一些像

这样的事情

更新(“用户”,新的UserParameter(),值);

我也从读者值中填充实体。

答案 4 :(得分:0)

您可能会遇到困难,具体取决于您计划放置此环境的环境。

如果我理解正确,你想保留函数签名(单个字符串参数),但如果字符串中的第一个单词不是“UPDATE”,则处理它不同。例如,此字符串应执行名为UpdateTable1的存储过程,其中包含四个参数:

UpdateTable1 NULL, A5KMI, "New description", #5/10/2009#

或类似的东西。

问题是你需要在字符串其余部分的参数之间使用分隔符。为此,你必须将字符串分解为标记,这可能会变得非常棘手。

所以替代方法是在整个字符串前添加命令EXEC,将整个事物设置为CommandText属性,然后调用其中一个Execute方法。

我不喜欢这个,它确实为您打开了SQL注入漏洞。如果可能的话,使用其他人已经回答的重载方法,并让调用者将参数拆分成某种集合。