Привет, у меня есть самоссылающийся объект, который фактически является отражением модели задачи › подзадачи в планах проекта, где в плане проекта может быть много слоев задач:
public class ProjectPlanItem
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public Guid Reference { get; set; } = Guid.NewGuid();
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public DateTime BaselineStartDate { get; set; }
public DateTime BaselineEndDate { get; set; }
public int Duration { get; set; }
public string DurationUnit { get; set; }
public int Progress { get; set; }
public string Predecessor { get; set; }
public virtual ICollection<ProjectPlanItem> ProjectPlanSubItems { get; set; } = new List<ProjectPlanItem>();
}
DBContext правильно создает таблицу базы данных, имея FK для вложенного ProjectPlanItemID
У меня есть следующий код в методе get моего контроллера для быстрой загрузки:
var plans = await _context.ProjectPlans
.Include(p => p.ProjectPlanEvents)
.Include(p => p.ProjectPlanHolidays)
.Include(p => p.ProjectPlanResources)
.Include(p => p.ProjectPlanItems)
.ThenInclude(pi => pi.ProjectPlanSubItems)
.ToListAsync();
Это помогает расширять мой объект по мере необходимости для всех связанных ICollection
, кроме вложенных элементов плана проекта, где вложенные элементы появляются (неправильно) в корне и (правильно) во вложенной коллекции. Согласно приведенному ниже:
- Group 1
- Group 1 Sub Item 1
- Группа 1 Подпункт 2
- Группа 1 Подпункт 1
- Группа 1 Подпункт 2
- Group 2
- Group 2 Sub Item 1
- Группа 2 Подпункт 2
- Группа 2 Подпункт 1
- Группа 2 Подпункт 2
Только что попробовал с еще одним третьим уровнем вложенности, и та же ситуация повторяется, но на этот раз
- G1
- G1 I1
- G2
- G2 I1
- G1 I1
- G2
- G2 I1
Я просмотрел множество сообщений о проблемах с efcore самоссылками здесь и в других местах, но ни один из них не отражает эту проблему (или я недостаточно четко формулирую свои условия поиска).
Я только что перенес проект на .net 5 rc2 с .net core 3 на случай, если в новой версии будет что-то полезное.
есть идеи?
ОБНОВЛЕНИЕ: Запрос на модель ProjectPlan
:
public class ProjectPlan
{
[Key]
public int ID { get; set; }
public Guid Reference { get; set; } = Guid.NewGuid();
public string Name { get; set; }
public string Version { get; set; }
public DateTime VersionDate { get; set; }
public ProjectPlanStatus Status { get; set; }
public string WorkWeek { get; set; }
public ICollection<ProjectPlanItem> ProjectPlanItems { get; set; } = new List<ProjectPlanItem>();
public ICollection<ProjectPlanEventMarker> ProjectPlanEvents { get; set; } = new List<ProjectPlanEventMarker>();
public ICollection<ProjectPlanResource> ProjectPlanResources { get; set; } = new List<ProjectPlanResource>();
public ICollection<ProjectPlanHoliday> ProjectPlanHolidays { get; set; } = new List<ProjectPlanHoliday>();
}
ОБНОВЛЕНИЕ 2 - Запрос вывода с тестовыми данными (я не заполнил другие коллекции в родительском элементе ProjectPlan, а только элементы ProjectPlanItems, которые он имеет в коллекции).
{
"id": 1,
"reference": "1735cc99-81d4-42aa-b5f5-62c012e1cd6a",
"name": "My Project Plan",
"version": "1.0",
"versionDate": "2020-10-26T00:00:00",
"status": 1,
"workWeek": "Monday",
"projectPlanItems": [
{
"id": 1,
"name": "Group 1",
"reference": "69e9fe1e-8219-4178-b465-6074cd1e08e4",
"startDate": "2020-10-01T00:00:00",
"endDate": "2020-10-30T00:00:00",
"baselineStartDate": "2020-10-01T00:00:00",
"baselineEndDate": "2020-10-30T00:00:00",
"duration": 30,
"durationUnit": "d",
"progress": 75,
"predecessor": null,
"isSubItem": false,
"projectPlanSubItems": [
{
"id": 2,
"name": "Group 1 Item 1",
"reference": "f038d10f-862a-468d-b664-d4e848b73cac",
"startDate": "2020-10-01T00:00:00",
"endDate": "2020-10-15T00:00:00",
"baselineStartDate": "2020-10-01T00:00:00",
"baselineEndDate": "2020-10-15T00:00:00",
"duration": 15,
"durationUnit": "d",
"progress": 75,
"predecessor": null,
"isSubItem": true,
"projectPlanSubItems": []
},
{
"id": 3,
"name": "Group 1 Item 2",
"reference": "3d32845b-3a9e-42bf-8a38-414d8822a8c5",
"startDate": "2020-10-16T00:00:00",
"endDate": "2020-10-30T00:00:00",
"baselineStartDate": "2020-10-16T00:00:00",
"baselineEndDate": "2020-10-30T00:00:00",
"duration": 15,
"durationUnit": "d",
"progress": 75,
"predecessor": null,
"isSubItem": true,
"projectPlanSubItems": []
}
]
},
{
"id": 2,
"name": "Group 1 Item 1",
"reference": "f038d10f-862a-468d-b664-d4e848b73cac",
"startDate": "2020-10-01T00:00:00",
"endDate": "2020-10-15T00:00:00",
"baselineStartDate": "2020-10-01T00:00:00",
"baselineEndDate": "2020-10-15T00:00:00",
"duration": 15,
"durationUnit": "d",
"progress": 75,
"predecessor": null,
"isSubItem": true,
"projectPlanSubItems": []
},
{
"id": 3,
"name": "Group 1 Item 2",
"reference": "3d32845b-3a9e-42bf-8a38-414d8822a8c5",
"startDate": "2020-10-16T00:00:00",
"endDate": "2020-10-30T00:00:00",
"baselineStartDate": "2020-10-16T00:00:00",
"baselineEndDate": "2020-10-30T00:00:00",
"duration": 15,
"durationUnit": "d",
"progress": 75,
"predecessor": null,
"isSubItem": true,
"projectPlanSubItems": []
}
],
"projectPlanEvents": [],
"projectPlanResources": [],
"projectPlanHolidays": []
}
Вы можете видеть, что в JSON ProjectPlanItem ID=2 и ID=3 правильно вложены в коллекцию в ProjectPlanItem ID=1.
Затем они неправильно повторяются в коллекции родительской корневой коллекции.
ОБНОВЛЕНИЕ 4. Отделите загрузку ProjectPlanItems от .Include в родительском плане проекта:
Я поиграл и решил лениво загрузить все остальные коллекции в родительский объект ProjectPlan, а затем отдельно загрузить ProjectPlanItems в качестве отдельного шага с помощью .Where, выбрав только элементы ProjectPlanItems верхнего уровня.
var projectPlan = await _context.ProjectPlans
.Where(p=> p.ID==id)
.Include(p => p.ProjectPlanEvents)
.Include(p => p.ProjectPlanHolidays)
.Include(p => p.ProjectPlanResources)
.FirstOrDefaultAsync();
var projectplanitems = await _context.ProjectPlanItems
.Where(ppi => ppi.IsSubItem == false)
.Include(ppi => ppi.ProjectPlanSubItems)
.ToListAsync();
projectPlan.ProjectPlanItems = projectplanitems;
Это сработало и дало мне только один элемент в ProjectPlan.ProjectPlanItems (с идентификатором = 1), как и ожидалось, а затем 2 элемента в ProjectPlanItem.ProjectPlanSubItems с идентификатором 2 и 3 снова, как и ожидалось.
Итак, учитывая, что я обновил решение до .net5 RC2, я попытался добавить .Where в исходную ленивую загрузку, чтобы (теоретически) дать мне тот же запрос, что и выше, но только в одном туда и обратно:
var plans = await _context.ProjectPlans
.Include(p => p.ProjectPlanEvents)
.Include(p => p.ProjectPlanHolidays)
.Include(p => p.ProjectPlanResources)
.Include(p => p.ProjectPlanItems.Where(p => p.IsSubItem==false))
.ThenInclude(pi => pi.ProjectPlanSubItems)
.ToListAsync();
Но это не имеет никакого эффекта и по-прежнему дает неверный вывод.
Заботится ли EF, если я сделаю два запроса, чтобы заполнить объект в контроллере
Я предполагаю, что если я внесу изменения в клиент, а затем отправлю его обратно, EF не будет знать или не заботиться ни в малейшей степени?