ladookie
ladookie

Reputation: 1371

Setting up parent child select elements with eager loading in breeze and knockout

I'm trying to figure out how to set up a parent child select elements with knockout and breeze. This is what I have, but it's not working. What am I doing wrong?

This is the Model

ModelYear.cs (Parent)

public class ModelYear
{
    public ModelYear()
    {
        Vehicles = new List<Vehicle>();
    }
    public int Id { get; set; }
    public string Year { get; set; }
    public virtual ICollection<Vehicle> Vehicles { get; set; }
}

Vehicle.cs (Child)

public class Vehicle
{
    public int Id { get; set; }
    public string Title { get; set; }
}

TestController.cs

[BreezeController]
public class TestController : ApiController
{
    private readonly EFContextProvider<TestContext> contextProvider = new EFContextProvider<TestContext>();

    [HttpGet]
    public string MetaData()
    {
        return contextProvider.Metadata();
    }

    [HttpGet]
    public IQueryable<ModelYear> ModelYears()
    {
        return contextProvider.Context.ModelYears;
    }
}

dataService.js

app.dataService = (function (breeze) {

    var serviceName = 'breeze/test';
    var manager = new breeze.EntityManager(serviceName);

    return {
        getModelYears: getModelYears
    };

    function getModelYears() {
        var query = breeze.EntityQuery.from("ModelYears").expand("Vehicles");
        return manager.executeQuery(query);
    }
})(breeze);

viewModel.js

app.viewModel = (function (dataService) {
    var vm = {
        years: ko.observableArray(),
        selectedYear: ko.observable()
    };

    initVm();

    return vm;

    function initVm() {
        getYears();
    }

    function getYears() {
        dataService.getModelYears()
                   .then(querySucceeded)
                   .fail(queryFailed);
    }

    function querySucceeded(data) {
        vm.years(data.results);
    }

    function queryFailed(error) {
        alert("Query failed. " + error.message);
    }
})(app.dataService);

ko.applyBindings(app.viewModel);

When I put a breakpoint in the querySucceeded method and examine the data parameter, the results variable is an array of objects that only have Id and Year knockout observables, there is no Vehicles array on it. When I examine the network traffic, however, the vehicles array is there. I'm not sure what else to do.

Here is the metadata that gets returned from the server:

{
   "schema":{
      "namespace":"BreezeTesting.Models",
      "alias":"Self",
      "annotation:UseStrongSpatialTypes":"false",
      "xmlns:annotation":"http://schemas.microsoft.com/ado/2009/02/edm/annotation",
      "xmlns":"http://schemas.microsoft.com/ado/2009/11/edm",
      "cSpaceOSpaceMapping":"[[\"BreezeTesting.Models.ModelYear\",\"BreezeTesting.Models.ModelYear\"],[\"BreezeTesting.Models.Vehicle\",\"BreezeTesting.Models.Vehicle\"]]",
      "entityType":[
         {
            "name":"ModelYear",
            "key":{
               "propertyRef":{
                  "name":"Id"
               }
            },
            "property":[
               {
                  "name":"Id",
                  "type":"Edm.Int32",
                  "nullable":"false",
                  "annotation:StoreGeneratedPattern":"Identity"
               },
               {
                  "name":"Year",
                  "type":"Edm.String",
                  "maxLength":"Max",
                  "fixedLength":"false",
                  "unicode":"true"
               }
            ],
            "navigationProperty":{
               "name":"Vehicles",
               "relationship":"Self.ModelYear_Vehicles",
               "fromRole":"ModelYear_Vehicles_Source",
               "toRole":"ModelYear_Vehicles_Target"
            }
         },
         {
            "name":"Vehicle",
            "key":{
               "propertyRef":{
                  "name":"Id"
               }
            },
            "property":[
               {
                  "name":"Id",
                  "type":"Edm.Int32",
                  "nullable":"false",
                  "annotation:StoreGeneratedPattern":"Identity"
               },
               {
                  "name":"Title",
                  "type":"Edm.String",
                  "maxLength":"Max",
                  "fixedLength":"false",
                  "unicode":"true"
               }
            ]
         }
      ],
      "association":{
         "name":"ModelYear_Vehicles",
         "end":[
            {
               "role":"ModelYear_Vehicles_Source",
               "type":"Edm.Self.ModelYear",
               "multiplicity":"0..1"
            },
            {
               "role":"ModelYear_Vehicles_Target",
               "type":"Edm.Self.Vehicle",
               "multiplicity":"*"
            }
         ]
      },
      "entityContainer":{
         "name":"TestContext",
         "entitySet":[
            {
               "name":"ModelYears",
               "entityType":"Self.ModelYear"
            },
            {
               "name":"Vehicles",
               "entityType":"Self.Vehicle"
            }
         ],
         "associationSet":{
            "name":"ModelYear_Vehicles",
            "association":"Self.ModelYear_Vehicles",
            "end":[
               {
                  "role":"ModelYear_Vehicles_Source",
                  "entitySet":"ModelYears"
               },
               {
                  "role":"ModelYear_Vehicles_Target",
                  "entitySet":"Vehicles"
               }
            ]
         }
      }
   }
}

and here is the data that gets returned from the server.

[
   {
      "$id":"1",
      "$type":"BreezeTesting.Models.ModelYear, BreezeTesting",
      "Id":1,
      "Year":"2012",
      "Vehicles":[
         {
            "$id":"2",
            "$type":"BreezeTesting.Models.Vehicle, BreezeTesting",
            "Id":1,
            "Title":"CTS Sport Sedan"
         },
         {
            "$id":"3",
            "$type":"BreezeTesting.Models.Vehicle, BreezeTesting",
            "Id":2,
            "Title":"CTS Coupe"
         },
         {
            "$id":"4",
            "$type":"BreezeTesting.Models.Vehicle, BreezeTesting",
            "Id":3,
            "Title":"Escalade ESV"
         },
         {
            "$id":"5",
            "$type":"BreezeTesting.Models.Vehicle, BreezeTesting",
            "Id":4,
            "Title":"SRX Crossover"
         }
      ]
   },
   {
      "$id":"6",
      "$type":"BreezeTesting.Models.ModelYear, BreezeTesting",
      "Id":2,
      "Year":"2013",
      "Vehicles":[
         {
            "$id":"7",
            "$type":"BreezeTesting.Models.Vehicle, BreezeTesting",
            "Id":5,
            "Title":"XTS Sedan"
         },
         {
            "$id":"8",
            "$type":"BreezeTesting.Models.Vehicle, BreezeTesting",
            "Id":6,
            "Title":"ATS Sedan"
         },
         {
            "$id":"9",
            "$type":"BreezeTesting.Models.Vehicle, BreezeTesting",
            "Id":7,
            "Title":"CTS Sport Sedan"
         }
      ]
   },
   {
      "$id":"10",
      "$type":"BreezeTesting.Models.ModelYear, BreezeTesting",
      "Id":3,
      "Year":"2014",
      "Vehicles":[
         {
            "$id":"11",
            "$type":"BreezeTesting.Models.Vehicle, BreezeTesting",
            "Id":8,
            "Title":"XTS Sedan"
         },
         {
            "$id":"12",
            "$type":"BreezeTesting.Models.Vehicle, BreezeTesting",
            "Id":9,
            "Title":"SRX Crossover"
         }
      ]
   }
]

Upvotes: 0

Views: 515

Answers (2)

Fab
Fab

Reputation: 153

I had the same problem, detail in this thread. Actually i needed to add two properties, one for the FK and one for the parent object. The FK property alone do not work.

Upvotes: 0

Ward
Ward

Reputation: 17863

Please follow the steps in "Query Result Debugging" when exploring this kind of question. It should guide you through to the point where the wheels come off.

As it happens, the reason in this case is obvious (to me :-). Your Vehicle type lacks a FK property such as Vehicle.ModelYearId that points back to the parent ModelYear type. That fact is apparent also in the metadata where there are no FKs for the navigation properties.

Entity Framework tolerates this which is why you see vehicles in the raw data. Behind the scenes it infers and uses the name of the actual FK column in Vehicles table in the database.

You'll have some EF mapping to do as well because this association is unidirectional; you don't have a navigation from the child (Vehicle) back to the parent (ModelYear).

Breeze on the client, however, is unable to maintain the ModelYear.Vehicles collection without having a FK value in the Vehicle data.

While you can send object graphs from the server if you wish. But Breeze can't do anything to assemble those graphs on its own or keep them intact on the client. That is why we say that Breeze requires FKs to maintain navigation properties.

Upvotes: 2

Related Questions