1
votes

I have three entities: Districts, Sites, and Repayments (Actually there are about a dozen, but these are the important ones). THe Relavent Code is:

public class Site 
{
    [Key, Column(Order = 0)]
    public int DistrictId { get; set; }
    [Key, Column(Order = 1)]
    public int SiteId { get; set; }

    public string Name { get; set; }

    public virtual District District { get; set; }
}

public class District 
{
    public int DistrictId { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Repayment> Repayments { get; set; }
}

public class Repayment 
{
    [Key, Column(Order = 0)]
    public int DistrictId { get; set; }
    [Key, Column(Order = 1)]
    public int RepaymentId { get; set; }

    public string Name { get; set; }

    [InverseProperty("Repayments")]
    [ForeignKey("DistrictId")]
    public virtual District District { get; set; }
}

Then, in the FluentAPI I have:

        modelBuilder.Entity<Repayment>().HasRequired(r => r.District)
                    .WithMany(d => d.Repayments)
                    .HasForeignKey(r => new { r.DistrictId});

The model performs correctly on the C# side, making repayments a collection in a District when I use the appropriate Include clause. Breeze, however, cannot find the repayments collection under the district (the repayments are in cache under a couple of other entities). I have tried every combination of Fluent API and Data Annotation possible (using only data annotation, using partial data annotation with and without the fluent api, etc) and there is no difference.

An interesting aside, the client side code works perfectly (and populates the repayments under districts) when I run it against a 1.5.1 version of Context Provider and 2.1.5.1 version of BreezeWebApi (the version that is not working is 1.5.2 and 2.1.5.2). However, I'm porting from a less complex model to a more complex model, so the backend is not exactly the same (those these classes are byte for byte the same).

I'd be grateful for any ideas.

EDIT: I have just installed the more recent versions (2.1.5.2 and 1.5.2) on the working site, and it caused no problems. So the version change doesn't seem to be the culprit. Are there any other things I should be checking?

TIA

EDIT: Even more information There are, within a district, 10 total one-to-many navigation propertys, of which Repayments is only one. 8 of them are being populated correctly (and are defined in exactly the same way) and two (including the Repayments property) are not. On the server side, all 10 properties are recognized and populated, while on the client side Breeze is only "wiring up" 8 of the properties to District (all the related entities are in cache and wired to at least one other entity, just not the District entity). Very strange. Help?

EDIT: Curiouser and curiouser: The breeze metadata seems to have the appropriate navigation property definitiions. Below is the NavProp definitions for Repayments (not working) and SeasonClients (working) in the District Entity:

        {
            "name": "Repayments",
            "relationship": "Self.Repayment_District",
            "fromRole": "Repayment_District_Target",
            "toRole": "Repayment_District_Source"
        },
        {
            "name": "SeasonClients",
            "relationship": "Self.SeasonClient_District",
            "fromRole": "SeasonClient_District_Target",
            "toRole": "SeasonClient_District_Source"
        },

And here is how the Nav Property for District is defined in both entities:

REPAYMENT Nav Properties:

        {
            "name": "District",
            "relationship": "Self.Repayment_District",
            "fromRole": "Repayment_District_Source",
            "toRole": "Repayment_District_Target"
        },

SEASON CLIENT Nav Properties:

        {
            "name": "District",
            "relationship": "Self.SeasonClient_District",
            "fromRole": "SeasonClient_District_Source",
            "toRole": "SeasonClient_District_Target"
        },

And here is the SeasonClient_District relation definition:

        {
        "name": "SeasonClient_District",
        "end": [{
            "role": "SeasonClient_District_Source",
            "type": "Edm.Self.SeasonClient",
            "multiplicity": "*"
        },
        {
            "role": "SeasonClient_District_Target",
            "type": "Edm.Self.District",
            "multiplicity": "1",
            "onDelete": {
                "action": "Cascade"
            }
        }],
        "referentialConstraint": {
            "principal": {
                "role": "SeasonClient_District_Target",
                "propertyRef": {
                    "name": "DistrictId"
                }
            },
            "dependent": {
                "role": "SeasonClient_District_Source",
                "propertyRef": {
                    "name": "DistrictId"
                }
            }
        }
    },

Which is byte-for-byte the same (if you replace 'SeasonClient' with 'Repayment') as the Repayment_District relationship definition.

Any thoughts?

1
Unless I missed it, you have not shown us the pertinent Breeze metadata on the client. You may think that you have. But you're actually showing us server-side EF model metadata. There is no "fromRole" in a Breeze metadata navigation property descriptor. Please review the query debugging topic in the documentation. If that doesn't help, please come back with the relevant Breeze client metadata. - Ward
Thanks for the pointers, @Ward, you are correct that I thought I was giving you the breeze metadata. Sorry I missed that page on debugging. I will play around with that and get back to you. My first pass at it, the data.query.entityManager object is null (data.query is not null), so I can't seem to get at the metadatastore, but I'll see what I can come up with. THanks again. - Beartums
@ward, following your suggestion, I worked on debugging the Breeze metadata. I can post it if that will help, but the navigation property that I'm looking for is clearly not included in the District entity (the inverse property IS included in the Repayment Entity). So, I know that: 1) the relationships are correct on the server, the relationships are NOT correct on the client. I infer that means the metadata being sent from the server is not properly formatted for Breeze, though it 'appears' correct to me. I have tried... - Beartums
...using both the FluentAPI and data annotations and it doesn't seem to make a difference. The metadata that Breeze gets from the server doesn't change, nor do the relationships get propagated to the client-side entities. Is there possibly a hard limit on the number of entities/relationships on the breeze side? My shop wants to use a single dbContext for all their operations, so the metadata includes dozens of entities and relationships that are not being used by this project. Thanks in advance for any suggestions. - Beartums
There is no limit. But you (and your API) may benefit from a simple gambit that reduces the size of your model (and the metadata) without compromising your server team's desire for a single, comprehensive DbContext. The gambit? Create a subclass of that DbContext that hides the stuff you don't want to expose. You can use that for Metadata generation or even for your own api if you wish. It has no logic in it and won't encumber your team in any way. - Ward

1 Answers

1
votes

J had similar problem. In June 2014 (all the latest libs at that time) the following client side code worked:

        return EntityQuery.from('FlowCharts')
        .withParameters({ flowChartId: 1 })
        .expand('Nodes.NodeFunction.NodeFunctionParameters, Connections')
        .using(manager).execute()
        .then(querySucceeded)
        .catch(_queryFailed);

but not now (Feb 2015 - after updating to the latest libs).Changing related entities names to lowercase has helped (please read @Ward debugging tips):

        return EntityQuery.from('flowCharts')
        .withParameters({ flowChartId: 1 })
        .expand('nodes.nodeFunction.nodeFunctionParameters, connections')
        .using(manager).execute()
        .then(querySucceeded)
        .catch(_queryFailed);

Hope it helps. Grzech