Search code examples
c#.net.net-3.5data-access-layersqlcommand

C# abstraction and database layer


I am wondering what's a better way to abstract some of this code, into a simple DAL. At this time, I'm just patching the code and don't have time or need yet to use EF, Linq2Sql or any ORM right now.

    public string GetMySpecId(string dataId)
    {
        using (SqlConnection conn = new SqlConnection(this.MyConnectionString))
        {

            conn.Open();

            // Declare the parameter in the query string
            using (SqlCommand command = new SqlCommand(@"select ""specId"" from ""MyTable"" where ""dataId"" = :dataId", conn))
            {
                // Now add the parameter to the parameter collection of the command specifying its type.
                command.Parameters.Add(new SqlParameter("dataId", SqlDbType.Text));

                command.Prepare();

                // Now, add a value to it and later execute the command as usual.
                command.Parameters[0].Value = dataId;


                using (SqlDataReader dr = command.ExecuteReader())
                {
                    while (dr.Read())
                    {
                        specId = dr[0].ToString();
                    }
                }
            }
        }

        return specId;
    }

What's a good clean way to pull the connection, commands, and such out of the GetMySpecId() as I will have tons of these functions and don't want to write the using.... over and over again.


Solution

  • Well, you could write your own custom data-access helper that encapsulates all that stuff and returns a DataTable:

    public string GetMySpecId(string dataId)
    {
        DataTable result = _dbHelper.ExecuteQuery(
            @"select ""specId"" from ""MyTable"" where ""dataId"" = :dataId",
            new SqlParameter("dataId", dataId);
        return result.Rows[0][0].ToString();
    }
    

    Or if you are stuck on the idea of using a DataReader, you could pass a delegate to the helper, which gets invoked inside of the using statements:

    public string GetMySpecId(string dataId)
    {
        return _dbHelper.ExecuteQuery(
            dr => 
               {
                   if(dr.Read())
                   {
                       return dr[0].ToString();
                   }
                   // do whatever makes sense here.
               },
            @"select ""specId"" from ""MyTable"" where ""dataId"" = :dataId",
            new SqlParameter("dataId", dataId));
    }
    

    You could also use a lightweight tool like Dapper to simplify some of the syntax and take care of mapping to your data types. (You'd still need to deal with opening a connection and such.)

    Update

    Here's an example of how you could write the ExecuteQuery method used in the second example:

    public T ExecuteQuery<T>(
        Func<IDataReader, T> getResult,
        string query,
        params IDataParameter[] parameters)
    {
        using (SqlConnection conn = new SqlConnection(this.MyConnectionString))
        {
            conn.Open();
            // Declare the parameter in the query string
            using (SqlCommand command = new SqlCommand(query, conn))
            {
                foreach(var parameter in parameters)
                {
                    command.Parameters.Add(parameter);
                }
                command.Prepare();
                using (SqlDataReader dr = command.ExecuteReader())
                {
                    return getResult(dr);
                }
            }
        }
    }