我可以用通用方法合并吗?

时间:2018-03-15 20:35:26

标签: c#

我有一个DataAccessBase类,它具有以下2种数据访问方法。一个用于ExecuteScalar,另一个用于ExecuteNonQuery。是否有可能将其合并为一种通用方法,或者甚至值得担心?

    protected static int ExecuteNonQuery(SqlCommand command)
    {
        using (SqlConnection connection = new SqlConnection(_connStr))
        {
            command.Connection = connection;
            SqlDataAdapter da = new SqlDataAdapter(command);
            command.Connection.Open();

            int result = command.ExecuteNonQuery();

            return result;
        }
    }

    protected static string ExecuteScalar(SqlCommand command)
    {
        using (SqlConnection connection = new SqlConnection(_connStr))
        {
            command.Connection = connection;
            SqlDataAdapter da = new SqlDataAdapter(command);
            command.Connection.Open();

            string result = command.ExecuteScalar().ToString();

            return result;
        }
    }

    private static DataTable GetDT(int id)
    {
        using (SqlConnection connection = new SqlConnection(_connStr))
        {
            string query = "select id, userid, name from tasks where id = @id";
            SqlCommand command = new SqlCommand(query, connection);
            SqlDataAdapter da = new SqlDataAdapter(command);
            //Parameterized query to prevent injection attacks
            command.Parameters.AddWithValue("id", id);
            DataTable dt = new DataTable();
            da.Fill(dt);

            return dt;
        }
    }

2 个答案:

答案 0 :(得分:7)

你绝对可以避免使用泛型方法获得的当前重复,但我不会尝试将其减少为单个方法。这是我潜在做的事情:

protected static int ExecuteNonQuery(SqlCommand command) =>
    ExecuteCommand(command, cmd => cmd.ExecuteNonQuery());

protected static string ExecuteScalar(SqlCommand command) =>
    ExecuteCommand(command, cmd => cmd.ExecuteScalar().ToString());

private static T ExecuteCommand<T>(SqlCommand command, Func<SqlCommand, T> resultRetriever)
{
    using (SqlConnection connection = new SqlConnection(_connStr))
    {
        command.Connection = connection;
        command.Connection.Open();
        return resultRetriver(command);
    }
}

对于DataTable,按照相同的模式,首先创建命令:

protected static DataTable GetDataTable(SqlCommand command) =>
    ExecuteCommand(cmd =>
    {
        SqlDataAdapter da = new SqlDataAdapter(cmd)
        DataTable table = new DataTable();
        da.FillTable(table);
        return table;
    });

答案 1 :(得分:1)

您可以将ExecuteScalar转换为通用方法,允许您更改返回类型。

public T ExecuteScalar<T>(SqlCommand command)
{
    using (SqlConnection connection = new SqlConnection(_connStr))
    {
        command.Connection = connection;
        //SqlDataAdapter da = new SqlDataAdapter(command); //not needed...
        command.Connection.Open();
        var result = command.ExecuteScalar();

        //rather than just returning result with an implicit cast, use Max's trick from here: https://stackoverflow.com/a/2976427/361842
        if (Convert.IsDbNull(result))
            return default(T); //handle the scenario where the returned value is null, but the type is not nullable (or remove this to have such scenarios throw an exception)
        if (result is T)
            return (T)result;
        else
            (T)Convert.ChangeType(result, typeof(T));
    }
}

虽然此方法的逻辑与ExecuteNonQuery函数不同,因此您不能同时使用相同的方法。

更新

关于你关于数据表的问题,我已经采用并改编@JonSkeet's answer以允许该类也处理数据表:

public class SqlDatabaseThing //: ISqlDatabaseThing
{

    // ... additional code here ... //

    public int ExecuteNonQuery(SqlCommand command, IEnumerable<SqlParameter> sqlParameters = new[]{}) =>
        ExecuteNonQuery(_connStr, command, sqlParameters);
    public static int ExecuteNonQuery(string connectionString, SqlCommand command, IEnumerable<SqlParameter> sqlParameters = new[]{}) =>
        ExecuteCommand(connectionString, command, cmd => cmd.ExecuteNonQuery());

    public T ExecuteScalar(SqlCommand command, IEnumerable<SqlParameter> sqlParameters = new[]{}) =>
        ExecuteScalar(_connStr, command, sqlParameters);
    public static T ExecuteScalar(string connectionString, SqlCommand command, IEnumerable<SqlParameter> sqlParameters = new[]{}) =>
        ExecuteCommand(connectionString, command, cmd => ConvertSqlCommandResult(cmd.ExecuteScalar()));

    public DataTable ExecuteToDataTable(SqlCommand command, IEnumerable<SqlParameter> sqlParameters = new[]{}) =>
        ExecuteToDataTable(_connStr, command, sqlParameters);
    public static DataTable ExecuteToDataTable(string connectionString, SqlCommand command, IEnumerable<SqlParameter> sqlParameters = new[]{}) =>
        ExecuteCommand(connectionString, command, cmd => PopulateDataTable(cmd));


    private static T ExecuteCommand<T>(string connectionString, SqlCommand command, IEnumerable<SqlParameter> sqlParameters, Func<SqlCommand, T> resultRetriever)
    {
        using (SqlConnection connection = new SqlConnection(connectionString))
        {
            command.Parameters.AddRange(sqlParameters);
            command.Connection = connection;
            command.Connection.Open();
            return resultRetriver(command);
        }
    }

    private static DataTable PopulateDataTable(SqlCommand command)
    {
        var da = SqlDataAdapter(command);
        var dt = new DataTable();
        da.Fill(dt);
        return dt;
    }

    private static T ConvertSqlCommandResult(object result)
    {
        if (Convert.IsDbNull(result))
            return default(T); 
        if (result is T)
            return result as T;
        (T)Convert.ChangeType(result, typeof(T));
    }

}   

注意:在您的代码中,您包含了与获取特定任务相关的逻辑。这应该与您的通用数据库逻辑分开(即,您可能希望为各种查询返回数据表,并且不希望每次都必须重写GetDT代码)。因此,我在下面提供了另外的示例代码,展示了如何将该逻辑分成另一个类......

public class TaskRepository //: IRepository<Task>
{
    ISqlDatabaseThing db;
    public TaskRepository(ISqlDatabaseThing db)
    {
        this.db = db;
    }

    readonly string GetByIdCommand = "select id, userid, name from tasks where id = @id";
    readonly string GetByIdCommandParameterId = "@id"
    readonly SqlDbType GetByIdCommandParameterIdType = SqlDbType.BigInt;
    public Task GetById(long id)
    {
        var command = new SqlCommand(GetByIdCommand);
        var parameters = IEnumerableHelper.ToEnumerable<SqlParameter>(new SqlParameter(GetByIdCommandIdParameter, GetByIdCommandIdParameterType, id));
        var dataTable = db.ExecuteToDataTable(command, parameters);
        return DataTableToTask(dataTable)[0];
    }
    private IEnumerable<Task> DataTableToTask(DataTable dt)
    {
        foreach (var row in dt.Rows)
        {
            yield return DataRowToTask(row);
        }
    }
    private Task DataRowToTask (DataRow dr)
    {
        return new Task()
        {
            Id = dr["Id"]
            ,Name = dr["Name"]
            ,UserId = dr["UserId"]
        };
    }

}

public static class IEnumerableHelper
{
    public static IEnumerable<T> ToEnumerable<T>(params T[] parameters)
    {
        return parameters;
    } 
}

注意:此代码未经测试;有任何问题请告诉我。