2009-05-19 85 views
5

我正在维护一些C#2.0代码,程序员通过打开一个DataReader然后将它传递给该对象的构造函数来使用一种读取业务对象集合的模式。我看不出有什么明显的错误,但对我来说感觉很糟糕。这可以吗?将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); 
} 
+0

我的例子并不清楚,但假设MyObject是一个单独的类。 – Sisiutl 2009-05-19 22:59:32

+0

你可以改变你的代码,使其成为一个类:) :) – 2009-05-19 23:02:56

回答

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{} 
+0

这几乎可以肯定我会做什么 - 我相信有人会展示一种方法来使用匿名方法或lambda表达式。我很好奇看到这个解决方案如何“流行”。 – MikeJ 2009-05-19 23:16:01

+0

“使用(SqlDataReader reader ...”并不是真的需要,对吗?由于连接已经被封装,所以配置会清理命令,游标等。 – TheSoftwareJedi 2009-05-20 00:03:21

+2

如果类实现了IDisposable,那么它希望你可以调用Dispose,不要让它失望 – 2009-05-20 01:23:37

1

(编辑:这个答案完全着眼于“什么都在相对较低水平的影响”,而不是整体设计影响它看起来像其他的答案已经得到。那些覆盖,所以我不会评论:)

那么,肯定有错误的代码,因为没有关闭连接,命令或读者。具体来说,您的连接分配的线路通常应该是这样的:

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

你可能认为这只是吹毛求疵,那它只是示例代码和清理不重要 - 但是的“所有权” 需要的资源清理正是将DataReader传递给构造函数的问题。

只要你记录谁拥有读者之后,我认为没关系。例如,在Image.FromStream中,图像拥有之后的流,并且可能不会友好地关闭它(取决于图像格式和其他一些内容)。其他时候,您仍然有责任关闭。这必须非常仔细地记录下来,如果带有构造函数的类型取得所有权,则应该执行IDisposable以使清理更容易使其更明显需要清理。

在你的情况,它看起来像构造函数是而不是取得读者的所有权,这是一个非常好(和更简单)的选择。简单地记录下来,并且调用者仍然必须适当地关闭阅读器。

1

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

1

我称之为“泄漏抽象”。

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

0

我完全duffymo(+1)(贝文和)同意

一个想法我一直在咀嚼最近在我的心里,如果我必须有一个相关性,当然,当我映射读者对象我必须有一个依赖,也许我应该使之完全明确,实际上写的扩展方法来实现它,像...

//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) 
    { 

    } 
} 

这样一来,只要我在这是我的业务对象的参考范围,我的数据搜索器现在将有一种适合我需求的方法......这个想法可能需要更充实,但我认为这将是优雅的。

0

要独立的业务和数据层使用收益

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

传递数据读写器的构造可能是有争议的,但 - 据我所知 - 是一个众所周知的方法。但是,使用它的人应该通过抽象,即不是SqlDataReader而是IDataReader。然而IDataReader并不是最优的。它应该是IDataRecord,而不是允许迭代!

相关问题