我正在努力定义一个填充并返回实例集合的类方法。我不知道如何解决的问题是我有私人属性来填充。
让我们使用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命中数据库来加载所有书籍,而不是只加载1个查询。我该如何解决这个问题?
我确定这是编程101,但我需要问。
答案 0 :(得分:2)
您可以创建一个受保护的构造函数,直接填充私有属性。
答案 1 :(得分:2)
foreach应该迭代已经实例化的对象列表,它们不需要连接到DB。
您需要创建一个接受书籍对象属性的构造函数,以便您可以从现有数据集中实例化一本书,而不是对数据库进行新命中。
这样:
构造
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 */ }
}
答案 2 :(得分: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>
属性。这可以保证每次使用给定ID调用Book.Create()
时,都会返回相同的Book
实例。相反,有一个论点是将此作为Book
类的一个特性 - 你在List<Book>
中创建一个静态私有Book
属性并编写逻辑来生成Create
方法维护它。
使用相同的方法将数据推送回数据库 - 将Update
,Delete
和Insert
方法添加到IAdapter<T>
并在适配器类中实现它们,并Book
在适当的时候调用这些方法。
答案 3 :(得分:0)
为什么不使用SQL where语句检查数据库端书籍的可用性?