SqlDataReader - 使用匹配的所有项目填充子列表
SqlDataReader - Populate child list with all the items that match
我试图用匹配的子对象填充父对象,但不知道如何只显示匹配的子对象。
我设法获取子对象并将它们添加到列表中,但它显示了父对象的所有子对象。
public ObservableCollection<Book> GetBooks()
{
con.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = con.con;
Book book = new Book();
List<int> IdBooks= new List<int>();
List<Tag> tags = new List<Tag>();
cmd.CommandText = "SELECT b.idBook,b.Title,b.Writer,t.IdTypeOfBook,t.Type FROM tblBook b LEFT OUTER JOIN tblTypeOfBook t ON b.idTypeOfBook = t.idTypeOfBook WHERE b.Active = 1 ORDER BY b.idBook";
SqlDataReader rd = cmd.ExecuteReader();
if (rd.HasRows)
{
while (rd.Read())
{
var idBook = Convert.ToInt32(rd["IdBook"]);
var title = rd["Title"].ToString();
var writer = rd["Writer"].ToString();
var idType = Convert.ToInt32(rd["IdTypeOfBook"]);
var type = rd["Type"].ToString();
book = new Book()
{
IdBook = idBook,
Title = title
};
book.Tags = tags;
Books.Add(book);
IdBooks.Add(idBook);
}
rd.Close();
}
foreach (var idBook in IdBooks)
{
SqlCommand tagCommand = new SqlCommand();
tagCommand.Connection = con.con;
tagCommand.CommandText = "SELECT t.IdTag,t.Name FROM tblTag t LEFT OUTER JOIN tblBookTag bt ON bt.idTag = t.idTag WHERE bt.idBook = @ID";
tagCommand.Parameters.AddWithValue("@ID", idBook);
SqlDataReader tagReader = tagCommand.ExecuteReader();
if (tagReader.HasRows)
{
while (tagReader.Read())
{
Tag tag = new Tag();
var idTag = Convert.ToInt32(tagReader["IdTag"]);
var name = tagReader["Name"].ToString();
tag.IdTag = idTag;
tag.Name = name;
tags.Add(tag);
}
tagReader.Close();
}
}
con.Close();
return Books;
}
现在我只能显示所有标签,而不是与书相匹配的标签。
编辑
这是现在的样子
public ObservableCollection<Book> GetBooks()
{
con.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = con.con;
Book book = new Book();
cmd.CommandText = "SELECT b.idBook,b.Title,b.Writer,t.IdTypeOfBook,t.Type FROM tblBook b LEFT OUTER JOIN tblTypeOfBook t ON b.idTypeOfBook = t.idTypeOfBook WHERE b.Active = 1 ORDER BY b.idBook";
SqlDataReader rd = cmd.ExecuteReader();
if (rd.HasRows)
{
while (rd.Read())
{
var idBook = Convert.ToInt32(rd["IdBook"]);
var title = rd["Title"].ToString();
var writer = rd["Writer"].ToString();
var idType = Convert.ToInt32(rd["IdTypeOfBook"]);
var type = rd["Type"].ToString();
book = new Book()
{
IdBook = idBook,
Title = title,
Tags = new List<Tag>()
};
Books.Add(book);
}
rd.Close();
}
foreach (var bk in Books)
{
book.Tags = GetTagsThatMatch(bk.IdBook);
}
con.Close();
return Books;
}
public List<Tag> GetTagsThatMatch(int idBook)
{
SqlCommand tagCommand = new SqlCommand();
tagCommand.Connection = con.con;
tagCommand.CommandText = "SELECT t.IdTag,t.Name FROM tblTag t LEFT OUTER JOIN tblBookTag bt ON bt.idTag = t.idTag WHERE bt.idBook = @ID";
tagCommand.Parameters.AddWithValue("@ID", idBook);
SqlDataReader tagReader = tagCommand.ExecuteReader();
if (tagReader.HasRows)
{
while (tagReader.Read())
{
Tag tag = new Tag();
var idTag = Convert.ToInt32(tagReader["IdTag"]);
var name = tagReader["Name"].ToString();
tag.IdTag = idTag;
tag.Name = name;
Console.WriteLine(tag.Name);
Tags.Add(tag);
}
tagReader.Close();
}
return Tags;
}
您正在创建标签列表:
List<Tag> tags = new List<Tag>();
然后,对于每本书,您都将 Tags
属性 设置到该列表。
book.Tags = tags;
Books.Add(book);
这很重要,因为这意味着无论您有多少本书,都只有一个标签列表。每本书的 Tags
属性 指的是同一个标签列表。他们都在分享它。
这意味着在第二个查询中,当您将标签添加到此列表时:
tags.Add(tag);
...您正在为每本书添加该标签。结果是每本书都会有相同的标签,因为只有一个列表。
您可能想要的只是将标签添加到它们附带的书籍中。为此,不要创建一个标签列表,而是这样做:
book = new Book()
{
IdBook = idBook,
Title = title,
Tags = new List<Tag>()
};
现在每本书都有自己的标签列表。您可以将标签添加到一本书,而不是将它们添加到所有书。
您可以丢弃 tags
变量和 IdBooks
。您不需要构建图书 ID 列表,因为您已经创建了图书列表,并且每本书都有一个 ID。
所以你可以用这个替换方法的第二部分:
foreach (var book in Books) // loop through the books, not the IDs
{
SqlCommand tagCommand = new SqlCommand();
tagCommand.Connection = con.con;
// in your query, get the ID from the book
tagCommand.CommandText = "SELECT t.IdTag,t.Name FROM tblTag t LEFT OUTER JOIN tblBookTag bt ON bt.idTag = t.idTag WHERE bt.idBook = @ID";
tagCommand.Parameters.AddWithValue("@ID", book.IdBook);
SqlDataReader tagReader = tagCommand.ExecuteReader();
if (tagReader.HasRows)
{
while (tagReader.Read())
{
Tag tag = new Tag();
var idTag = Convert.ToInt32(tagReader["IdTag"]);
var name = tagReader["Name"].ToString();
tag.IdTag = idTag;
tag.Name = name;
// Add the tag to the collection of tags for just that one book.
book.Tags.Add(tag);
}
tagReader.Close();
}
}
为了更容易调试,我还考虑将获取标签的部分放在一个单独的函数中,如下所示:
List<Tag> GetTags(int bookId)
并将查找每本书标签的代码移到那里。这样您的主要功能将如下所示:
foreach(var book in Books)
{
book.Tags = GetTags(book.IdBook);
}
看起来只是四处移动代码,但它会产生更小的函数,更易于阅读。当您移动代码时,您可能会意识到您正在使用一些您认为自己没有使用的变量,这将帮助您发现问题。
这样的话,如果给您带来麻烦的部分是获取一本书标签的函数,现在您可以单独测试它,只需使用一个图书 ID 调用它并查看返回的内容。如果您得到错误的结果,那么您可以仅使用一个图书 ID 来调试该方法。
当我们可以使用更小的代码片段时,调试会更容易。如果我们知道一个部分可以工作,我们可以停止查看它或 运行 再看一遍,只查看不工作的部分。
更新 2(抱歉,这已经超出了对阅读本文的任何其他人有用的程度,但现在没有回头路了。)
改变这个:
foreach (var bk in Books)
{
book.Tags = GetTagsThatMatch(bk.IdBook);
}
对此:
foreach (var bk in Books)
{
bk.Tags = GetTagsThatMatch(bk.IdBook);
}
如果您将标签添加到 book
,那么您是将它们添加到最后创建的图书中,而不是添加到 foreach
循环中的当前图书中。
为了避免这种混乱。删除此行:
Book book = new Book();
并在您使用它的地方声明变量:
Book book = new Book()
{
IdBook = idBook,
Title = title,
Tags = new List<Tag>()
};
通过在该循环范围内声明变量,就不可能在该范围外意外使用它。将变量保持在尽可能小的范围内很有帮助。
我试图用匹配的子对象填充父对象,但不知道如何只显示匹配的子对象。 我设法获取子对象并将它们添加到列表中,但它显示了父对象的所有子对象。
public ObservableCollection<Book> GetBooks()
{
con.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = con.con;
Book book = new Book();
List<int> IdBooks= new List<int>();
List<Tag> tags = new List<Tag>();
cmd.CommandText = "SELECT b.idBook,b.Title,b.Writer,t.IdTypeOfBook,t.Type FROM tblBook b LEFT OUTER JOIN tblTypeOfBook t ON b.idTypeOfBook = t.idTypeOfBook WHERE b.Active = 1 ORDER BY b.idBook";
SqlDataReader rd = cmd.ExecuteReader();
if (rd.HasRows)
{
while (rd.Read())
{
var idBook = Convert.ToInt32(rd["IdBook"]);
var title = rd["Title"].ToString();
var writer = rd["Writer"].ToString();
var idType = Convert.ToInt32(rd["IdTypeOfBook"]);
var type = rd["Type"].ToString();
book = new Book()
{
IdBook = idBook,
Title = title
};
book.Tags = tags;
Books.Add(book);
IdBooks.Add(idBook);
}
rd.Close();
}
foreach (var idBook in IdBooks)
{
SqlCommand tagCommand = new SqlCommand();
tagCommand.Connection = con.con;
tagCommand.CommandText = "SELECT t.IdTag,t.Name FROM tblTag t LEFT OUTER JOIN tblBookTag bt ON bt.idTag = t.idTag WHERE bt.idBook = @ID";
tagCommand.Parameters.AddWithValue("@ID", idBook);
SqlDataReader tagReader = tagCommand.ExecuteReader();
if (tagReader.HasRows)
{
while (tagReader.Read())
{
Tag tag = new Tag();
var idTag = Convert.ToInt32(tagReader["IdTag"]);
var name = tagReader["Name"].ToString();
tag.IdTag = idTag;
tag.Name = name;
tags.Add(tag);
}
tagReader.Close();
}
}
con.Close();
return Books;
}
现在我只能显示所有标签,而不是与书相匹配的标签。
编辑
这是现在的样子
public ObservableCollection<Book> GetBooks()
{
con.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = con.con;
Book book = new Book();
cmd.CommandText = "SELECT b.idBook,b.Title,b.Writer,t.IdTypeOfBook,t.Type FROM tblBook b LEFT OUTER JOIN tblTypeOfBook t ON b.idTypeOfBook = t.idTypeOfBook WHERE b.Active = 1 ORDER BY b.idBook";
SqlDataReader rd = cmd.ExecuteReader();
if (rd.HasRows)
{
while (rd.Read())
{
var idBook = Convert.ToInt32(rd["IdBook"]);
var title = rd["Title"].ToString();
var writer = rd["Writer"].ToString();
var idType = Convert.ToInt32(rd["IdTypeOfBook"]);
var type = rd["Type"].ToString();
book = new Book()
{
IdBook = idBook,
Title = title,
Tags = new List<Tag>()
};
Books.Add(book);
}
rd.Close();
}
foreach (var bk in Books)
{
book.Tags = GetTagsThatMatch(bk.IdBook);
}
con.Close();
return Books;
}
public List<Tag> GetTagsThatMatch(int idBook)
{
SqlCommand tagCommand = new SqlCommand();
tagCommand.Connection = con.con;
tagCommand.CommandText = "SELECT t.IdTag,t.Name FROM tblTag t LEFT OUTER JOIN tblBookTag bt ON bt.idTag = t.idTag WHERE bt.idBook = @ID";
tagCommand.Parameters.AddWithValue("@ID", idBook);
SqlDataReader tagReader = tagCommand.ExecuteReader();
if (tagReader.HasRows)
{
while (tagReader.Read())
{
Tag tag = new Tag();
var idTag = Convert.ToInt32(tagReader["IdTag"]);
var name = tagReader["Name"].ToString();
tag.IdTag = idTag;
tag.Name = name;
Console.WriteLine(tag.Name);
Tags.Add(tag);
}
tagReader.Close();
}
return Tags;
}
您正在创建标签列表:
List<Tag> tags = new List<Tag>();
然后,对于每本书,您都将 Tags
属性 设置到该列表。
book.Tags = tags;
Books.Add(book);
这很重要,因为这意味着无论您有多少本书,都只有一个标签列表。每本书的 Tags
属性 指的是同一个标签列表。他们都在分享它。
这意味着在第二个查询中,当您将标签添加到此列表时:
tags.Add(tag);
...您正在为每本书添加该标签。结果是每本书都会有相同的标签,因为只有一个列表。
您可能想要的只是将标签添加到它们附带的书籍中。为此,不要创建一个标签列表,而是这样做:
book = new Book()
{
IdBook = idBook,
Title = title,
Tags = new List<Tag>()
};
现在每本书都有自己的标签列表。您可以将标签添加到一本书,而不是将它们添加到所有书。
您可以丢弃 tags
变量和 IdBooks
。您不需要构建图书 ID 列表,因为您已经创建了图书列表,并且每本书都有一个 ID。
所以你可以用这个替换方法的第二部分:
foreach (var book in Books) // loop through the books, not the IDs
{
SqlCommand tagCommand = new SqlCommand();
tagCommand.Connection = con.con;
// in your query, get the ID from the book
tagCommand.CommandText = "SELECT t.IdTag,t.Name FROM tblTag t LEFT OUTER JOIN tblBookTag bt ON bt.idTag = t.idTag WHERE bt.idBook = @ID";
tagCommand.Parameters.AddWithValue("@ID", book.IdBook);
SqlDataReader tagReader = tagCommand.ExecuteReader();
if (tagReader.HasRows)
{
while (tagReader.Read())
{
Tag tag = new Tag();
var idTag = Convert.ToInt32(tagReader["IdTag"]);
var name = tagReader["Name"].ToString();
tag.IdTag = idTag;
tag.Name = name;
// Add the tag to the collection of tags for just that one book.
book.Tags.Add(tag);
}
tagReader.Close();
}
}
为了更容易调试,我还考虑将获取标签的部分放在一个单独的函数中,如下所示:
List<Tag> GetTags(int bookId)
并将查找每本书标签的代码移到那里。这样您的主要功能将如下所示:
foreach(var book in Books)
{
book.Tags = GetTags(book.IdBook);
}
看起来只是四处移动代码,但它会产生更小的函数,更易于阅读。当您移动代码时,您可能会意识到您正在使用一些您认为自己没有使用的变量,这将帮助您发现问题。
这样的话,如果给您带来麻烦的部分是获取一本书标签的函数,现在您可以单独测试它,只需使用一个图书 ID 调用它并查看返回的内容。如果您得到错误的结果,那么您可以仅使用一个图书 ID 来调试该方法。
当我们可以使用更小的代码片段时,调试会更容易。如果我们知道一个部分可以工作,我们可以停止查看它或 运行 再看一遍,只查看不工作的部分。
更新 2(抱歉,这已经超出了对阅读本文的任何其他人有用的程度,但现在没有回头路了。)
改变这个:
foreach (var bk in Books)
{
book.Tags = GetTagsThatMatch(bk.IdBook);
}
对此:
foreach (var bk in Books)
{
bk.Tags = GetTagsThatMatch(bk.IdBook);
}
如果您将标签添加到 book
,那么您是将它们添加到最后创建的图书中,而不是添加到 foreach
循环中的当前图书中。
为了避免这种混乱。删除此行:
Book book = new Book();
并在您使用它的地方声明变量:
Book book = new Book()
{
IdBook = idBook,
Title = title,
Tags = new List<Tag>()
};
通过在该循环范围内声明变量,就不可能在该范围外意外使用它。将变量保持在尽可能小的范围内很有帮助。