SqlDataReader-用匹配的所有项目填充子列表

时间:2019-05-18 15:51:26

标签: c# wpf many-to-many sqldatareader sqlcommand

我试图用匹配的子对象填充父对象,但无法弄清楚如何仅显示匹配的子对象。 我设法获取了子对象并将它们添加到列表中,但是它显示了父对象的所有子对象。

        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;
    }

1 个答案:

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

通过在该循环范围内声明变量,就不可能在该范围之外意外使用它。将变量保持在尽可能小的范围内很有帮助。