将DataReaders传递给构造函数是否可以?

时间:2009-05-19 22:49:32

标签: c# constructor sqldatareader

我正在维护一些C#2.0代码,程序员通过打开DataReader然后将其传递给对象的构造函数来使用读取业务对象集合的模式。我看不出任何明显的错误,但对我来说感觉很糟糕。这样可以吗?

private static void GetObjects()
{
    List<MyObject> objects = new List<MyObject>();
    string sql = "Select ...";
    SqlConnection connection = GetConnection();
    SqlCommand command = new SqlCommand(sql, connection);
    connection.Open();
    SqlDataReader reader = command.ExecuteReader(CommandBehavior.CloseConnection);
    while (reader.Read())
        objects.Add(new MyObject(reader));
    reader.Close();
}

public MyObject(SqlDataReader reader)
{
    field0 = reader.GetString(0);
    field1 = reader.GetString(1);
    field2 = reader.GetString(2);
}

9 个答案:

答案 0 :(得分:5)

通过将DataReader传递给对象的构造函数,您可以在业务对象和您选择的持久性技术之间建立非常紧密的耦合。

至少,这种紧密耦合将使重用和测试变得困难;在最坏的情况下,它可能导致业务对象对数据库了解太多。

解决这个问题并不太困难 - 您只需将对象初始化移出构造函数并进入不同的工厂类。

答案 1 :(得分:3)

传阅读者会让我感到畏缩,除非这是一种处理复制行的非公共帮助方法。绝对不是一个构造函数。

传递一个阅读器连接你的构造函数(你没有把MyObject放在一个类中,但是你把它调用new MyObject())到你的数据存储器中,我认为你的对象不是这样编写的?

如果是我:

private static void GetObjects()
{
    List<MyObject> objects = new List<MyObject>();
    string sql = "Select ...";
    using (SqlConnection connection = GetConnection())
    {
        SqlCommand command = new SqlCommand(sql, connection);
        connection.Open();
        using(SqlDataReader reader = command.ExecuteReader(CommandBehavior.CloseConnection);)
        {
            while (reader.Read())
                objects.Add(_ReadRow(reader));
        }
    }
}

private static MyObject _ReadRow(SqlDataReader reader)
{
    MyObject o = new MyObject();
    o.field0 = reader.GetString(0);
    o.field1 = reader.GetString(1);
    o.field2 = reader.GetString(2);

    // Do other manipulation to object before returning

    return o;
}

class MyObject{}

答案 2 :(得分:1)

我不会这样做,但我没有看到任何重大错误。

答案 3 :(得分:1)

(编辑:这个答案仅关注“相对较低级别的影响是什么”,而不是整体设计含义。看起来其他答案已经涵盖了那些,所以我不会评论:)) p>

嗯,你给出的代码肯定有问题,因为没有关闭连接,命令或阅读器。具体来说,您的连接分配行通常应如下所示:

using (SqlConnection connection = GetConnection())
{
    ...
}

您可能认为这只是挑剔,而且它只是示例代码而清理并不重要 - 但需要清理资源的“所有权”正是问题所在将DataReader传递给构造函数。

我认为只要您记录之后“拥有”读者的人就可以了。例如,在Image.FromStream中,图像之后拥有该流,并且可能不会亲自关闭它(取决于图像格式和其他一些东西)。其他时候,你仍有责任关闭。必须非常仔细地记录这一点,并且如果具有构造函数的类型具有所有权,则应该实现IDisposable以使清理更容易以使清除更加明显。

在您的情况下,看起来构造函数取得读者的所有权,这是一个非常好(和更简单)的替代方案。只需记录下来,调用者仍然需要适当地关闭阅读器。

答案 4 :(得分:1)

我会在IDataReader中传递实体,因为这有助于测试MyObject。

答案 5 :(得分:1)

我称之为“漏洞抽象”。

我喜欢在尽可能最窄的范围内使用持久性对象:获取它们,使用它们,清理它们。如果存在定义明确的持久性对象,则可以要求它将查询结果映射到对象或集合中,在方法范围内关闭阅读器,并将对象或集合返回给客户端。

答案 6 :(得分:0)

我完全同意duffymo(+1)(和Bevan)

我最近在脑海里想到的一个想法是,如果我必须有一个依赖,当然当我将一个阅读器映射到一个对象时我必须有一个依赖,也许我应该让它完全明确事实上,写一个扩展方法来实现它,就像......

//This Static extension class in the same namespace (but not necesarrily assembly) as my BO

public static class DataReaderExtensions
{
  public static List<MyObject> GetMyObjects(this DataReader reader)
  {

  }
}

这样,只要我在业务对象的参考范围内,我的datareader现在将有一个根据我的需求量身定制的方法......这个想法可能需要更多地充实,但我认为这将是优雅。

答案 7 :(得分:0)

要分隔业务层和数据层,请使用YIELD

  public IEnumerable<IDataReader> getIDataReader(string sql)
{

    using (SqlConnection conn = new SqlConnection("cadena de conexion"))
    {
        using (SqlCommand da = new SqlCommand(sql, conn))
        {
            conn.Open();
            using (SqlDataReader dr = da.ExecuteReader)
            {
                if (dr.HasRows)
                {
                    while (dr.Read)
                    {
                        yield return dr;
                    }
                }
            }
            conn.Close();
        }
    }
}

答案 8 :(得分:0)

将数据读取器传递给构造函数可能存在争议,但据我所知 - 这是一种众所周知的方法。但无论谁使用它,都应该传递一个抽象,即不是SqlDataReader而是IDataReader。然而,IDataReader不是最佳的;它应该是IDataRecord,它包含相应记录的结果,而不是允许迭代!!