У меня есть иерархия EF, которая (значительно упрощена) выглядит примерно так:
class Room { EntityCollection<Session> Sessions; }
class Session { EntityCollection<Whiteboard> Whiteboards; EntityReference Room; }
class Whiteboard { EntityCollection<WhiteboardShape> WhiteboardShapes; EntityReference Session; }
abstract class WhiteboardShape { EntityReference Whiteboard; }
class WhiteboardShapeEllipse : WhiteboardShape { }
class WhiteboardShapePolyline { WhiteboardShape { EntityCollection<PolylinePoint> PolylinePoints }
class PolylinePoint { EntityReference<WhiteboardShapePolyline> WhiteboardShapePolylineReference; }
Другими словами, Комната может содержать несколько сеансов; каждая сессия может содержать несколько досок; и каждая доска может содержать несколько форм доски. Эти формы могут быть разных типов, включая WhiteboardShapePolyline, которая сама может содержать несколько PolylinePoints.
Когда удаленный пользователь первоначально подключается к комнате, мне нужно передать этому объекту весь граф объектов, и я пытаюсь выяснить, как максимально эффективно загрузить этот граф из базы данных в память.
Теперь, конечно, EF позволяет вам загружать, например, так:
Room room = ctx.Room
.Include("Sessions.Whiteboards")
.FirstOrDefault(r => r.OwnerID == ownerUserID && r.Name == roomName);
Но Include () не позволяет мне загружать PolylinePoints. В частности, если я попытаюсь:
Room room = ctx.Room
.Include("Sessions.Whiteboards.WhiteboardShape.PolylinePoint")
.FirstOrDefault(r => r.OwnerID == ownerUserID && r.Name == roomName);
Я получаю исключение: «Указанный недопустимый путь включения недействителен. EntityType« SlideLinc.Model.WhiteboardShape »не объявляет свойство навигации с именем« PolylinePoint ».
И это не работает:
.Include("Sessions.Whiteboards.WhiteboardShapePolyline.PolylinePoint")
И при этом это:
.Include("Sessions.Whiteboards.WhiteboardShape.WhiteboardShapePolyline.PolylinePoint")
Ни один другой способ создания навигационного пути, о котором я могу думать.
То, как я это сделал, наверняка кажется мне взломом:
// Make sure we've got everything loaded.
if (room != null)
{
if (!room.Sessions.IsLoaded) { room.Sessions.Load(); }
foreach (Session session in room.Sessions)
{
if (!session.Whiteboards.IsLoaded) { session.Whiteboards.Load(); }
foreach (Whiteboard whiteboard in session.Whiteboards)
{
if (!whiteboard.WhiteboardShape.IsLoaded) { whiteboard.WhiteboardShape.Load(); }
foreach (WhiteboardShape shape in whiteboard.WhiteboardShape)
{
if (shape is WhiteboardShapePolyline)
{
WhiteboardShapePolyline polyline = (WhiteboardShapePolyline)shape;
if (!polyline.PolylinePoints.IsLoaded) { polyline.PolylinePoints.Load(); }
}
}
}
}
}
Это работает, но это намного больше кода, чем я хочу, и это гораздо больше обращений к базе данных, чем я хочу.
Самый близкий ответ, который я нашел, здесь , но мой бедный голодный Linq мозг не может понять, как преобразовать пример кода в более сложную иерархию, которую я получил; Кроме того, пример кода по этой ссылке чертовски уродлив и труден для понимания. Я действительно не хочу, чтобы вся моя иерархия объектов зависела от неясного и невидимого побочного эффекта от того, как EF внутренне строит свои иерархии.
Любые другие предложения?
Я бы, вероятно, использовал проекцию для этого. Вместо того, чтобы возвращать типы сущностей, проектируйте на легкие объекты передачи данных или анонимные типы. Когда вы проецируете (например, с помощью запроса LINQ), загрузка происходит автоматически. Вам не нужно указывать Включить в этом случае.
У меня просто была проблема, похожая на вашу (более простой путь навигации), и я понял, что PropertyName в файле Designer.cs оказался не тем, что я ожидал. Как только я изменил его на PropertyName в файле Designer, все заработало нормально - также обратите внимание, что я сделал два разных пути, которые стали:
.Include("UnexpectedNameA").Include("UnexpectedNameB")