Including navigation properties from Entity Framew

2020-06-23 07:12发布

I've got an EF hierarchy that (dramatically simplified) looks something like this:

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; }

In other words, a Room can contain multiple sessions; each Session can contain multiple Whiteboards; and each Whiteboard can contain multiple WhiteboardShapes. These shapes can be of various types, including a WhiteboardShapePolyline, which itself can contain multiple PolylinePoints.

When a remote user initially connects to the room, I need to pass the entire object graph to that user, and I'm trying to figure out how to load that graph from the database into memory as efficiently as possible.

Now, of course, the EF allows for you to do eager loading, like so:

      Room room = ctx.Room
            .Include("Sessions.Whiteboards")
            .FirstOrDefault(r => r.OwnerID == ownerUserID && r.Name == roomName);

But Include() doesn't let me load up the PolylinePoints. Specifically, if I try:

        Room room = ctx.Room
            .Include("Sessions.Whiteboards.WhiteboardShape.PolylinePoint")
            .FirstOrDefault(r => r.OwnerID == ownerUserID && r.Name == roomName);

I get the exception "A specified Include path is not valid. The EntityType 'SlideLinc.Model.WhiteboardShape' does not declare a navigation property with the name 'PolylinePoint'."

Nor does this work:

.Include("Sessions.Whiteboards.WhiteboardShapePolyline.PolylinePoint")

Nor does this:

.Include("Sessions.Whiteboards.WhiteboardShape.WhiteboardShapePolyline.PolylinePoint")

Nor any other way of framing the navigation path that I can think of.

The way that I've ended up doing it sure seems like a hack to me:

        // 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(); }
                        }
                    }
                }
            }
        }

It works, but it's a lot more code than I want, and it's a whole bunch more database accesses than I want.

The closest answer I've found is here, but my poor Linq-starved brain can't figure out how to translate the example code into the more complicated hierarchy that I've got; plus, the sample code at that link is damned ugly and difficult to understand. I don't really want my entire object hierarchy depending on an obscure and invisible side-effect of how the EF internally constructs its hierarchies.

Any other suggestions?

2条回答
地球回转人心会变
2楼-- · 2020-06-23 07:35

I was just having a similar problem to yours (easier naviagation path) and I realised that the PropertyName in the Designer.cs file was not what I expected. Once I changed it to the PropertyName in the Designer file it all worked fine - also note I did two different paths which became:

.Include("UnexpectedNameA").Include("UnexpectedNameB")
查看更多
Fickle 薄情
3楼-- · 2020-06-23 07:43

I would probably use projection for this. Instead of returning entity types, project onto lightweight data transfer objects or anonymous types. When you project (e.g., with a LINQ query), the loading happens automatically. You don't need to specify an Include in this case.

查看更多
登录 后发表回答