Модель, которую вы описываете, почти идентична модели Post
/ Tag
из Пример "многие ко многим" в документации EF Core.
Таким образом, у вас будет 3 класса, представляющих записи таблицы
public class Report
{
public int Id { get; set; }
public string ReportName { get; set; }
public ICollection<ReportMapping> Mappings { get; set; } // navigation
}
public class ReportCategory
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<ReportMapping> Mappings { get; set; } // navigation
}
public class ReportMapping
{
public int Id { get; set; }
public int ReportId { get; set; }
public int CategoryId { get; set; }
public Report Report { get; set; } // navigation
public ReportCategory Category { get; set; } // navigation
}
и 3 DbSet
s, представляющие ваши таблицы:
public DbSet<Report> Reports { get; set; }
public DbSet<ReportCategory> ReportCategories { get; set; }
public DbSet<ReportMapping> ReportMappings { get; set; }
Обратите внимание, что свойство Id
(столбец) в объекте соединения (таблице) является избыточным, поэтому, если вы не ограничены существующей базой данных, рассмотрите возможность ее удаления и настройте составной PK, как в примере
modelBuilder.Entity<ReportMapping>()
.HasKey(e => new { e.ReportId, e.CategoryId });
Также обратите внимание на свойства, помеченные // navigation
. Это так называемые свойства навигации (см. Определение терминов ), которые представляют собой концы отношений и позволяют получить доступ к связанным данным внутри запросов LINQ.
без использования join
конструкций - см. Не используйте Linq Join. Перейдите! и это EF (Core) рекомендуемый / предпочтительный способ написания запросов LINQ.
Итак, это ваша модель базы данных. Поскольку вы хотите запрос, возвращающий конкретный тип результата, начните с определения класса, который представляет этот результат (например, DTO, ViewModel и т. Д.), Например:
public class ReportInfo
{
public int ReportId { get; set; }
public string ReportName { get; set; }
public IEnumerable<string> ReportCategories { get; set; }
}
Обратите внимание, что я определяю ReportCategories
как последовательность строк, а не как одну строку. Это связано с тем, что, во-первых, конкатенация строк набора результатов изначально не поддерживается базами данных, а во-вторых, конкатенация через запятую является лишь одним из многих способов представления этих данных. В целом форматирование данных является обязанностью клиентов. Таким образом, вы возвращаете данные в их исходном формате (список строк) и позволяете клиенту форматировать их (в этом случае это легко сделать с помощью string.Join(",", info.ReportCategories)
).
Наконец, фактический запрос. При наличии свойств навигации это довольно просто - в основном просто Select
s:
var result = db.Reports
.Select(r => new ReportInfo
{
ReportId = r.Id,
ReportName = r.ReportName,
ReportCategories = r.Mappings
.Select(m => m.Category.Name)
.ToList() // <-- to avoid N + 1 subquery in EF Core 2.1+
})
.ToList();