我试图用匹配的子对象填充父对象,但无法弄清楚如何仅显示匹配的子对象。 我设法获取了子对象并将它们添加到列表中,但是它显示了父对象的所有子对象。
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;
}
答案 0 :(得分:0)
您正在创建标签列表:
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>()
};
通过在该循环范围内声明变量,就不可能在该范围之外意外使用它。将变量保持在尽可能小的范围内很有帮助。