У меня 2 модели:
public class Document
{
public Guid Id { get; set; }
[Required(ErrorMessage = "Please enter new documents name . . .")]
[Display(Name = "Document Name:")]
public string Name { get; set; }
[Required]
[Display(Name = "Type:")]
[EnumDataType(typeof(DocumentTypeEnum))]
public DocumentTypeEnum DocType { get; set; }
public DateTime? CreatedDate { get; set; }
public string? Description { get; set; }
public virtual ICollection<DocumentVersion>? Versions { get; set; }
[Required]
[Display(Name = "Document Category")]
public virtual DocumentCategory Category { get; set; }
}
И
public class DocumentCategory
{
public int Id { get; set; }
public string? Name { get; set; }
public int? ParentId { get; set; }
public virtual DocumentCategory? Parent { get; set; }
public virtual ICollection<DocumentCategory>? Children { get; set; }
}
Категории документов являются иерархическими и представлены в базе данных, как показано ниже:
ID | Name | Parent ID
1 | Test1 | 'null'
2 | Test2 | 'null'
3 | Test3 | 'null'
4 | Test1.1 | 1
5 | Test1.2 | 1
6 | Test1.1.1 | 4
7 | Test1.1.2 | 4
В контроллере у меня есть действие, которое возвращает частичное представление, которое выбирает документы на основе категории ID, используя приведенный ниже код.
public async Task<ActionResult> DocsByCatAsync(int id)
{
var documents = new List<Document>();
if (id == 0)
{
documents = await _documentDbContext
.Documents
.AsNoTracking()
.Include(d => d.Category)
.ToListAsync();
}
else
{
documents = await _documentDbContext
.Documents
.Where(d => d.Category.Id == id)
.AsNoTracking()
.Include(d => d.Category)
.ToListAsync();
}
return PartialView("_DocumentTable", documents);
}
Что я пытаюсь сделать, так это то, что если я выберу CategoryId, у которого есть потомки, я могу вернуть все документы в выбранном ID и потомках категории.
Например, если категория ID была 1, то будут возвращены все документы с категорией ID из: 1, 4, 6, 7 (используя пример таблицы выше).
Что-то вроде
documents = await _documentDbContext
.Documents
.Where(d => d.Category.Id IN (1,4,6,7));
Проблема в том, что не существует фиксированного количества уровней, и я не могу понять, как использовать EF для включения оператора IN.
Любой совет?
Часть IN может быть достигнута с помощью .Where(d => new int[] { 1, 4, 6, 7 }.Contains(d.Category.Id))
Какую СУБД вы используете? В Sql Server есть тип данных иерархии . Посмотреть образец
Вы используете так называемый список смежности. Для эффективной работы с ним нужны общие табличные выражения. У EF нет их поддержки. Фреймворк linq2db имеет такую поддержку. По крайней мере, используйте расширение linq2db.EntityFrameworkCore . Он указан в официальном списке EF Core Tools & Extensions





Спасибо за совет.
Ошибка, которую я совершал, заключалась в предложении .where(). Я пытался написать это .where(d => d.category.Id.Contains(), но это не сработало.
Комментарий Ивана - то, что мне было нужно.
По сути, у меня есть рекурсивный процесс, который генерирует массив идентификаторов, которые я передаю в «запрос» EF:
var catIDs = GetChildrenCategories(id);
documents = await _documentDbContext.Documents.Where(d => catIDs.Contains(d.Category.Id))
.AsNoTracking()
.Include(d => d.Category)
.ToListAsync();
Ваш ActionResult представляет собой массив. Смотрите следующую публикацию с ответом 157: stackoverflow.com/questions/9508265/…