【问题标题】:C# abstraction and database layerC#抽象和数据库层
【发布时间】:2012-11-22 16:00:55
【问题描述】:

我想知道有什么更好的方法可以将其中的一些代码抽象成一个简单的 DAL。目前,我只是在打补丁,没有时间也不需要使用 EF、Linq2Sql 或任何 ORM。

    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;
    }

什么是从 GetMySpecId() 中提取连接、命令等的好方法,因为我将拥有大量这些函数并且不想一遍又一遍地编写 using....

【问题讨论】:

  • 我不确定您要在这里完成什么...我看到您以通常的 ADO.NET 方式从数据库中提取字符串 ID。您想如何以不同的方式编写此代码?将其余数据从MyTable 中提取出来并水合对象的其余部分?摆脱样板 SqlClient 的东西?我真的怀疑使用 ORM 可能值得你花时间。 Code-first EF 可以是一个非常轻量级的解决方案,并且可以快速设置。
  • 遇到了一些基本问题。如果有多个 specID,则返回最后一个。如果没有 specID,则没有默认值。 Heck specID 甚至没有被声明。

标签: c# .net .net-3.5 data-access-layer sqlcommand


【解决方案1】:

好吧,您可以编写自己的自定义数据访问帮助器来封装所有这些内容并返回一个 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();
}

或者,如果您坚持使用 DataReader 的想法,您可以将委托传递给助手,该助手在 using 语句中被调用:

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));
}

您还可以使用像Dapper 这样的轻量级工具来简化一些语法并负责映射到您的数据类型。 (您仍然需要处理打开连接等问题。)

更新

这是一个示例,说明如何编写第二个示例中使用的 ExecuteQuery 方法:

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);
            }
        }
    }
}

【讨论】:

  • 等等这些用途都去哪儿了?
  • 为什么第二个例子中有2条return语句?
  • 是的,我仍然对第二个示例的工作原理有些困惑
  • @chris: using 语句都在 ExecuteQuery 帮助器方法中结束。由于复制/粘贴错误,我不小心留下了第二个返回语句,但现在已修复。如果我的更新有意义,请告诉我。
【解决方案2】:

您可以使用 yield return 语句来将连接、命令和读取器对象保留在 using 语句中。

public class ScalarReader<T>
{
    const string MyConnectionString = "...";

    private string _returnColumn, _table, _whereCond;
    private object[] _condParams;

    public ScalarReader(string returnColumn, string table, string whereCond,
                        params object[] condParams)
    {
        _returnColumn = returnColumn;
        _table = table;
        _whereCond = whereCond;
        _condParams = condParams;
    }

    public IEnumerator<T> GetEnumerator()
    {
        using (SqlConnection conn = new SqlConnection(MyConnectionString)) {
            conn.Open();
            string select = String.Format(@"SELECT ""{0}"" FROM ""{1}"" WHERE {2}",
                                          _returnColumn, _table, _whereCond);
            using (SqlCommand command = new SqlCommand(select, conn)) {
                for (int p = 0; p < _condParams.Length; p++) {
                    command.Parameters.AddWithValue("@" + (p+1), _condParams[p]);
                }
                using (SqlDataReader dr = command.ExecuteReader()) {
                    while (dr.Read()) {
                        if (dr.IsDBNull(0)) {
                            yield return default(T);
                        } else {
                            yield return (T)dr[0];
                        }
                    }
                }
            }
        }
    }
}

你可以这样称呼它

var reader = new ScalarReader<string>("specId", "MyTable", "dataId=@1", "x");
foreach (string id in reader) {
    Console.WriteLine(id);
}

请注意,我使用的是参数名称的约定。他们被命名为@1, @2, @3 ...

var reader =
    new ScalarReader<DateTime>("date", "MyTable", "num=@1 AND name=@2", 77, "joe");

【讨论】:

【解决方案3】:

您需要从 using 语句的中间返回一个 IDataReader,一旦这样做,您将失去连接和数据。你不能真正做你所追求的。

【讨论】:

    【解决方案4】:

    你可以做这样的事情,抱歉没有实际的代码,但它会给你这个想法。当然,必须小心地将 object[] 转换回有用的东西,但是你已经在使用 specId = dr[0].ToString();

    class MyDb
    {
        public MyDb()
        {
        }
    
        public void Initialize()
        {
            // open the connection
        }
    
        public void Finalize()
        {
            // close the connection
        }
    
        public List<object[]> Query(string command, List<SqlParameter> params)
        {
            // prepare command
            // execute reader
            // read all values into List of object[], and return it
        }
    }
    

    【讨论】:

      【解决方案5】:

      您可以创建一个基本抽象类,该类将具有一些基本函数以及所有使用和基本代码,如下所示:

      public abstract class BaseClass
      {
        public abstract void myFunc(SqlConnection conn);
      
        public void BaseFunc()
        {      
              using (SqlConnection conn = new SqlConnection(this.MyConnectionString))
              {
                  conn.Open();
                  myFunc(conn);
                  ..any other base implementation...
              }
        }
      }
      

      每个派生类将继承 BaseClass 并使用特定查询和每个派生类的所有特定内容实现抽象 MyFunc。您将从基础抽象类的 BaseFunc 函数外部调用。

      【讨论】:

      • 将连接作为参数传递给 myFunc
      猜你喜欢
      • 2012-11-22
      • 2010-09-08
      • 2010-12-13
      • 2015-05-15
      • 1970-01-01
      • 2011-02-19
      • 1970-01-01
      • 2019-07-24
      • 1970-01-01
      相关资源
      最近更新 更多