【问题标题】:SqlDataReader - Populate child list with all the items that matchSqlDataReader - 使用所有匹配项填充子列表
【发布时间】:2019-05-18 15:51:25
【问题描述】:

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

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

【问题讨论】:

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


    【解决方案1】:

    您正在创建标签列表:

    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)
    

    并移动在那里查找每本书的标签的代码。这样你的 main 函数将如下所示:

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

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

    【讨论】:

    • 我试过了,现在它显示了上一本书的所有标签
    • 检查您的第二个 SQL 查询,并确保在遍历所有图书时,您使用查询中每本不同图书的 ID。并单独运行该查询以确保它是正确的。我查看了代码,但没有仔细检查查询。
    • 它循环通过它们似乎很好我会尝试看看我是否能弄明白
    • 查询似乎很好我不知道我还能怎么写,我似乎无法弄清楚
    • 我添加了一些更新。我看不到您的代码中发生了什么,但是如果您将其分解为一些较小的部分,则将可以工作的部分与不工作的部分分开会更容易。用更小的方法更容易发现错误。
    猜你喜欢
    • 2011-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-24
    • 1970-01-01
    • 1970-01-01
    • 2021-04-24
    相关资源
    最近更新 更多