romatthe
romatthe

Reputation: 1477

Modeling nested data structures in Entity Framework

I'm trying out Entity Framework 7 RC1 (every major version, I check back to see if it's worth the hassle), but I'm having some trouble trying to understand how I'm supposed to model certain entities.

As an example I went back to a simple real-world application I have lying around. It was made to manage Windows print servers (ugh).

Here's an example of some of the database tables:

Here's the DDL:

CREATE TABLE dbo.PrintServer
(
    ID          INT IDENTITY NOT NULL,
    Name        VARCHAR(MAX) NOT NULL,
    Description VARCHAR(MAX) NULL,
    CONSTRAINT [PK_PrintServer_ID] PRIMARY KEY (ID),
)

CREATE TABLE dbo.PrintServerSupport
(
    ID              INT IDENTITY NOT NULL,
    ServerID        INT NOT NULL,
    TypeID          INT NOT NULL,
    ManufacturerID  INT NOT NULL,
    CONSTRAINT [PK_PrintServerSupport_ID] PRIMARY KEY (ID),
    CONSTRAINT [FK_PrintServerSupport_ServerID] FOREIGN KEY (ServerID) REFERENCES PrintServer (ID) ON DELETE CASCADE,
    CONSTRAINT [FK_PrintServerSupport_TypeID] FOREIGN KEY (TypeID) REFERENCES PrinterType (ID) ON DELETE CASCADE,
    CONSTRAINT [FK_PrintServerSupport_ManufacturerID] FOREIGN KEY (ManufacturerID) REFERENCES PrinterManufacturer (ID) ON DELETE CASCADE
)

CREATE TABLE dbo.PrinterType
(
    ID          INT IDENTITY NOT NULL,
    Type        VARCHAR(MAX) NOT NULL,
    CONSTRAINT [PK_PrinterType_ID] PRIMARY KEY (ID),
)

CREATE TABLE dbo.PrinterManufacturer
(
    ID              INT IDENTITY NOT NULL,
    Manufacturer    VARCHAR(MAX) NOT NULL,
    CONSTRAINT [PK_PrinterManufacturer_ID] PRIMARY KEY (ID)
)

Now, turning this into C# POCO entities would apparently amount to something along these lines:

public partial class PrinterManufacturer
{
    public PrinterManufacturer()
    {
        PrintServerSupport = new HashSet<PrintServerSupport>();
    }

    public int ID { get; set; }
    public string Manufacturer { get; set; }

    public virtual ICollection<PrintServerSupport> PrintServerSupport { get; set; }
}

public partial class PrinterType
{
    public PrinterType()
    {
        PrintServerSupport = new HashSet<PrintServerSupport>();
    }

    public int ID { get; set; }
    public string Type { get; set; }

    public virtual ICollection<PrintServerSupport> PrintServerSupport { get; set; }
}

public partial class PrintServer
{
    public PrintServer()
    {
        PrintServerSupport = new HashSet<PrintServerSupport>();
    }

    public int ID { get; set; }
    public string Description { get; set; }
    public string Name { get; set; }

    public virtual ICollection<PrintServerSupport> PrintServerSupport { get; set; }
}

public partial class PrintServerSupport
{
    public int ID { get; set; }
    public int ManufacturerID { get; set; }
    public int ServerID { get; set; }
    public int TypeID { get; set; }

    public virtual PrinterManufacturer Manufacturer { get; set; }
    public virtual PrintServer Server { get; set; }
    public virtual PrinterType Type { get; set; }
}

Now, image I would want to select all print servers, I would merely have to do the following? (Please keep in mind my EF experience with EF is very limited)

using (var db = new DbContext())
{
   var query = db.PrintServer.Include(s => s.PrintServerSupport);
}

However, when debugging, this returns the following rather strange resultset:

Result view

As you can see, the Manufacturer and Type fields are not populated. Curiously enough, the nested Server fields are...

To make things even more annoying, I'm also receiving JSON payloads with nested data. Here's an example:

[
    {
        "Name":"REDACTED",
        "Description":"Xerox MFP TEST",
        "SupportedPrinters": [
            {
                "Type":"Printer",
                "Manufacturer":"XEROX"
            },
            {
                "Type":"Plotter",
                "Manufacturer":"XEROX"
            },
            {
                "Type":"MFP",
                "Manufacturer":"XEROX"
            }
        ]
    },
    {
        "Name":"REDACTED-2",
        "Description":"Xerox MFP TEST 2",
        "SupportedPrinters": [
            {
                "Type":"Printer",
                "Manufacturer":"SAMSUNG"
            },
            {
                "Type":"Plotter",
                "Manufacturer":"SAMSUNG"
            }
        ]
    }
]

Marshaling and unmarshaling this data is a piece of cake, but what about unmarshaling data, and then updating the database? I always found it to be pretty difficult problem, and I'm curious as to how EF is supposed to help out here.

What is the correct way of both modeling the data and querying it?

Upvotes: 0

Views: 211

Answers (1)

SWilko
SWilko

Reputation: 3612

I don't think lazy loading is enabled by default in EF 7 when you mark your navigation properties virtual (like in EF6). This is to reduce unnecessary trips to the database.

You can load your related entities by using ThenInclude

using (var db = new DbContext())
{
   var query = db.PrintServer.Include(s => s.PrintServerSupport)
                             .ThenInclude(p => p.Manufacturer);
}

Upvotes: 1

Related Questions