SqlDataReader - заполнить дочерний список всеми элементами, которые соответствуют - PullRequest
0 голосов
/ 18 мая 2019

Я пытаюсь заполнить родительские объекты совпадающими дочерними объектами, но не могу понять, как отобразить только те, которые соответствуют.Мне удается получить дочерние объекты и добавить их в список, но он отображает все дочерние объекты для родительских объектов.

        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 голосов
/ 18 мая 2019

Вы создаете список тегов:

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.Вам не нужно создавать список идентификаторов книг, потому что вы уже создали список книг, и у каждой книги уже есть идентификатор.

Таким образом, вы можете заменить вторую часть метода следующим:

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

Это похоже на перемещение кода, но это приведет к меньшим функциям, которые легче читать.Когда вы перемещаете код, вы можете понять, что используете какую-то переменную, которую, как вы думали, вы не использовали, и это поможет вам уловить проблему.

Таким образом, если часть, которая доставляет вам неприятности, этофункция, которая получает теги для книги, теперь вы можете проверить ее отдельно, вызвав ее с одним идентификатором книги и посмотрев, что вы получите обратно.Если вы получили неправильный результат, вы можете отладить метод, используя только один идентификатор книги.

Отладка становится проще, когда мы можем работать с небольшими фрагментами кода.Если мы знаем, что одна часть работает, мы можем перестать смотреть на нее или запускать ее снова и просто посмотреть на часть, которая не работает.


Обновление 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>()
    };

Объявляя переменную внутри области действия этого цикла, становится невозможным случайноиспользуйте это вне этой области.Полезно хранить переменные в наименьшей возможной области видимости.

...