2009-02-17 105 views
4

我正在努力定义一个填充和返回实例集合的类方法。我不知道如何解决的问题是我有私有属性来填充。如何编写返回实例集合的类方法

让我们用一个Book类的例子。我不希望代码直接设置(说)一本书的可用性。我希望代码必须在Book实例上使用CheckOut方法。因此,我们有这样的:在我的代码

public class Book 
{ 
    private int ID; 
    private bool pAvailableForCheckout; 

    public string Title { get; set; } 
    public bool AvailableForCheckout { get { return pAvailableForCheckout } } 

    // instance methods 

    public Book(int BookID) 
    { 
    // Load book from DB by ID 
    } 
    public CheckOut() 
    { 
    // perform everything involved with checking a book out 
    } 
    // .. other methods like saving a book, checking out books etc. 

    // class method 

    public static List<Book> FindAll() 
    { 
    // load Dataset of books 
    // foreach record in DB, use the Book(int BookID) constructor and add to List 
    // return list of books 
    } 
} 

所以,我可以做到使用本:

foreach(Book curBook in Book.FindAll()) 
    { /* do something with book */ } 

上述实现的问题是,我必须使用N + 1命中数据库加载所有书籍而不是仅仅一个查询。我如何解决这个问题?

我确定这是编程101,但我需要问。

回答

2

您可以创建一个受保护的构造函数,它直接填充私有属性。

+0

不错,我什至没有想到这一点,但当你说出来时,它似乎很明显。 – 2009-02-17 15:34:49

0

为什么不使用SQL语句在数据库端检查图书的可用性?

2

foreach应该迭代已经实例化的对象列表,它们不需要连接到数据库。

您需要创建一个接受书对象属性的构造函数,以便您可以从现有数据集中实例化书籍,而不是新建一个数据库。

这样:

构造:

public book (String title, String avail) {Title=title...} 

而在方法

public static void FindAll() 
{ 
List<Books> books = new List<books>(); 
using (Sqlconnection conn = new sqlconnection(connstring)) 
using (sqlcommand cmd = new SqlCommand("select title, available from book ", conn) 
{ 
    SqlDatareader dr = cmd.executereader() 
    while (dr.read()) 
    { 
    books.add(new Book(dr["title"], dr["avail"]) 
    } 

} 

foreach(Book curBook in Book.FindAll()) 
    { /* do something with book */ } 

} 
+0

我同意这个答案。域类不应该知道数据库。 – Bertvan 2009-02-17 15:46:38

1

对于在其思想纯度有所极端的例子:

首先,对于类的接口可以从数据库中检索类型为T的对象,其ID为:

interface IAdapter<T> 
{ 
    T Retrieve(int id); 
} 

现在,Book类,它不再是暴露了一个公共的构造,而是一个使用IAdapter<Book>检索书籍从数据库中一个静态方法:

public class Book 
{ 
    public static IAdapter<Book> Adapter { get; set; } 

    public static Book Create(int id) 
    { 
     return Adapter.Retrieve(id); 
    } 

    // constructor is internal so that the Adapter can create Book objects 
    internal Book() { } 

    public int ID { get; internal set; } 
    public string Title { get; internal set; } 
    public bool AvailableForCheckout { get; internal set; } 

} 

你必须写类自己实现IAdapter<Book>,并将Book.Adapter分配给它的一个实例,以便Book.Create()将能够从数据库中提取内容。

我说“思想纯洁”,因为这样的设计强制执行的担忧相当严格区分:有没有在Book类,它知道如何与数据库对话 - 甚至有数据库。

举例来说,这里是一个可能实现的IAdapter<Book>

public class DataTableBookAdapter : IAdapter<Book> 
{ 
    public DataTable Table { get; set; } 
    private List<Book> Books = new List<Book>(); 

    Book Retrieve(int id) 
    { 
     Book b = Books.Where(x => x.ID = id).FirstOrDefault(); 
     if (b != null) 
     { 
     return b; 
     } 

     BookRow r = Table.Find(id); 
     b = new Book(); 

     b.ID = r.Field<int>("ID"); 
     b.Title = r.Field<string>("Title"); 
     b.AvailableForCheckout = r.Field<bool>("AvailableForCheckout"); 

     return b; 
    } 
} 

其他一些类负责创建和填充DataTable这一类使用。你可以编写一个不同的实现,它使用SqlConnection直接与数据库交谈。

你甚至可以这样写:

public IAdapter<Book> TestBookAdapter : IAdapter<Book> 
{ 
    private List<Book> Books = new List<Book>(); 

    public TestBookAdapter() 
    { 
     Books.Add(new Book { ID=1, Title="Test data", AvailableForCheckout=false }; 
     Books.Add(new Book { ID=2, Title="Test data", AvailableForCheckout=true }; 
    } 

    Book Retrieve(int id) 
    { 
     return Books.Where(x => x.ID == id); 
    } 
} 

此实现完全不使用一个数据库 - 为Book类编写单元测试时,你会使用这个。

请注意,这两个类都保留私人List<Book>属性。这保证每次您拨打Book.Create()给定的ID时,您将获得相同的Book实例。有一个参数可以让Book类的功能变为 - 您可以在Book中创建一个静态专用List<Book>属性并编写逻辑以使Create方法维护它。

您使用推送数据到数据库中同样的方法 - UpdateDeleteInsert方法添加到IAdapter<T>和适配器类实现它们,并有Book电话在适当的时候这些方法。

+0

+1让我的大脑爆炸。非常好,我将不得不与IAdapters一起玩。看到另一个实现也是很好的,所以我想我会从这个例子中获得很多。 – 2009-02-17 22:45:53

相关问题