Как достать список из таблицы БД?

235
03 марта 2019, 22:00

Я получил список категорий из БД. Далее мне нужно получить из соседней таблицы все топики, которые относятся к данной категории. После создания 2х и более записей в таблице с топиками - у меня почему-то не к категории топики прибавляются, а прибавляется категория с новым топиком. Хотя по идее Topic_Name 4 должен быть рядом с Topic_Name 3 и само собой не должно быть 2х Test Category 3.

Controller:

public async Task<IActionResult> Index()
{
    var source =
    from category in _context.Web_Forum_Category
    join topic in _context.Web_Forum_Topic on category.Id equals topic.Category_Id
    orderby topic.Id
    select new IndexViewModel { Web_Forum_Category = category, Web_Forum_Topic = topic };
    var model = await source.ToListAsync();
    return View(model);
}

View:

@model IEnumerable<Web.Models.Web.IndexViewModel>
@foreach (var item in Model)
{
<table class="table table-condensed table-responsive">
    <tbody>
        <tr>
            <td>
                @item.Web_Forum_Category.Name_Ru @item.Web_Forum_Category.Name_En
                <table>
                    <tbody>
                        <tr>
                            <td>@item.Web_Forum_Topic.Name</td>
                        </tr>
                    </tbody>
                </table>
            </td>
        </tr>
    </tbody>
</table>
}

Web_Forum_Category и Web_Forum_Topic:

public class Web_Forum_Category
{
    public int Id { get; set; }
    public string Name_Ru { get; set; }
    public string Name_En { get; set; }
}
public class Web_Forum_Topic
{
    [Key]
    public int Id { get; set; }
    public int Category_Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}

IndexViewModel

public class IndexViewModel
{
    public Web_Forum_Category Web_Forum_Category { get; set; }
    public Web_Forum_Topic Web_Forum_Topic { get; set; }
}

Я сначала думал что что-то с <table>...</table>. Позже понял что увы, нет...

Answer 1

Оператор join за основу берет ту коллекцию, которая указана ему с помощью in. По этой коллекции он грубо говоря пройдется и сопоставит все значения с другой коллекцией.

Скажем если у вас в первой коллекции 10 элементов, а во второй коллекции всего два, то он выведет 10 результатов с видом который вы устанавливаете в select. Вы же как я понял хотите сделать коллекцию категорий, которая будет содержать в себе коллекцию топиков. Для таких целей нужно еще дополнительно сгруппировать элементы, делается это с помощью group by.

Давайте для начала я сделаю тестовые данные в своем консольном приложение и попробуем поиграться с ними:

var cat = new List<Web_Forum_Category>
{
    new Web_Forum_Category(1, "Тестовая категория 1", "Test category 1"),
    new Web_Forum_Category(2, "Тестовая категория 2", "Test category 2"),
    new Web_Forum_Category(3, "Тестовая категория 3", "Test category 3")
};
var top = new List<Web_Forum_Topic>
{
    new Web_Forum_Topic(1, 1, "Тестовый топик 1", "Описание"),
    new Web_Forum_Topic(2, 2, "Тестовый топик 2", "Описание"),
    new Web_Forum_Topic(3, 1, "Тестовый топик 3", "Описание"),
    new Web_Forum_Topic(4, 3, "Тестовый топик 4", "Описание")
};
var source =
    from category in cat
    join topic in top on category.Id equals topic.Category_Id
    orderby topic.Id
    select new { Web_Forum_Category = category, Web_Forum_Topic = topic };
var model = source.ToList();
foreach (var item in model)
{
    Console.WriteLine($"{item.Web_Forum_Category.Name_Ru} | {item.Web_Forum_Category.Name_En}");
    Console.WriteLine(item.Web_Forum_Topic.Name);
}

Результат как и у вас:

Тестовая категория 1 | Test category 1
Тестовый топик 1
Тестовая категория 2 | Test category 2
Тестовый топик 2
Тестовая категория 1 | Test category 1
Тестовый топик 3
Тестовая категория 3 | Test category 3
Тестовый топик 4

Теперь сгруппируем это:

var source =
    from topic in top
    group topic by topic.Category_Id into g
    join category in cat on g.Key equals category.Id
    select new {Web_Forum_Category = category,  Web_Forum_Topic = g.ToList()};
var model = source.ToList();

Теперь Web_Forum_Topic будет содержать не один элемент, а коллекцию топиков и для вывода нам потребуется пройтись и по ней циклом тоже:

foreach (var item in model)
{
    Console.WriteLine($"{item.Web_Forum_Category.Name_Ru} | {item.Web_Forum_Category.Name_En}");
    item.Web_Forum_Topic.ForEach(x=>Console.WriteLine(x.Name));
}

Вывод:

Тестовая категория 1 | Test category 1
Тестовый топик 1
Тестовый топик 3
Тестовая категория 2 | Test category 2
Тестовый топик 2
Тестовая категория 3 | Test category 3
Тестовый топик 4

Вообще, я очень сильно не люблю такой вид LINQ, ведь написать что то по типу:

var source = cat.GroupJoin(top, c => c.Id, t => t.Category_Id,
    (category, topics) => new {Web_Forum_Category = category, Web_Forum_Topic = topics.ToList()});

куда приятней... Но это пожалуй решать каждому...

В общем удачи в изучении!

READ ALSO
символ " в запросе (C#)

символ " в запросе (C#)

Как вставить код символа в запрос С#? В месте, где я указываю на формат даты:

167
Примитивы синхроинзации

Примитивы синхроинзации

Вопрос теоретический, делал я задачу тестовую для VeeamЗнаменитая задача про архиватор многопоточный

193