I am using breezejs v.1.5.4
with OData Web Api controllers (and AngularJS v.1.4.0
if it makes a difference).
I have the following models (simplified):
public partial class Job
{
...
[Required]
[StringLength(128)]
[Index("IX_WorkDoneById")]
[Display(Name = "Work Done By Id")]
public string WorkDoneById { get; set; }
[ForeignKey("WorkDoneById")]
public virtual User WorkDoneBy { get; set; }
}
[DataContract]
public partial class User : IdentityUser
{
...
[Key]
[StringLength(128)]
[Display(Name = "Id")]
[DataMember]
public override string Id
{
get
{
return base.Id;
}
set
{
base.Id = value;
}
}
[InverseProperty("WorkDoneBy")]
[DataMember]
public virtual ICollection<Job> Jobs { get; set; }
}
When trying to get a Job
information and expanding the WorkDoneBy
, it works and I get the user information (i.e. the user is binded to the job). While when I try to get the Jobs
associated with the user, I get an empty array. I inspected the network and the Jobs
are transmitted with the server response but not attached to the user instance.
My JS query is like this:
var query = new breeze.EntityQuery()
.from("Users")
.expand("Jobs")
.where(new breeze.Predicate("Id", "eq", "Some long Guid"));
Any suggestions ??
Update 1
Also I am using datajs v.1.1.3
and odata service adapter.
Below is the metadata:
{
"metadataVersion": "1.0.5",
"namingConvention": "noChange",
"localQueryComparisonOptions": "caseInsensitiveSQL",
"dataServices": [
{
"serviceName": "odata/",
"adapterName": "odata",
"uriBuilderName": "odata",
"hasServerMetadata": true,
"jsonResultsAdapter": "OData_default",
"useJsonp": false
}
],
"structuralTypes": [
{
"shortName": "Job",
"namespace": "MyApp.Models",
"autoGeneratedKeyType": "None",
"defaultResourceName": "Jobs",
"dataProperties": [
{
"name": "JobId",
"dataType": "Guid",
"isNullable": false,
"defaultValue": "00000000-0000-0000-0000-000000000000",
"isPartOfKey": true,
"validators": [{ "name": "required" }, { "name": "guid" }]
},
{
"name": "WorkDoneById",
"dataType": "String",
"isNullable": false,
"defaultValue": "",
"validators": [{ "name": "required" }, { "name": "string" }]
}
],
"navigationProperties": [
{
"name": "WorkDoneBy",
"entityTypeName": "User:#MyApp.Models",
"isScalar": true,
"associationName": "MyApp_Models_Job_WorkDoneBy_MyApp_Models_User_WorkDoneByPartner"
}
]
},
{
"shortName": "User",
"namespace": "MyApp.Models",
"autoGeneratedKeyType": "None",
"defaultResourceName": "Users",
"dataProperties": [
{
"name": "Id",
"dataType": "String",
"isNullable": false,
"defaultValue": "",
"isPartOfKey": true,
"validators": [{ "name": "required" }, { "name": "string" }]
}
],
"navigationProperties": [
{
"name": "Jobs",
"entityTypeName": "Job:#MyApp.Models",
"isScalar": false,
"associationName": "MyApp_Models_User_Jobs_MyApp_Models_Job_JobsPartner"
}
]
}
],
"resourceEntityTypeMap":
{
"Jobs": "Job:#MyApp.Models",
"Users": "User:#MyApp.Models"
}
}
and this is breeze configuration:
var dataService = new breeze.DataService({
adapterName: "odata",
hasServerMetadata: false, // don't ask the server for metadata
serviceName: "odata",
uriBuilderName: "odata",
});
// create the metadataStore
var metadataStore = new breeze.MetadataStore();
// initialize the store from the application's metadata variable
metadataStore.importMetadata(Models.metaData);
// Apply additional functions and properties to the models
metadataStore.registerEntityTypeCtor("Job", Models.Job);
metadataStore.registerEntityTypeCtor("User", Models.User);
// Initializes entity manager.
this.entityManager = new breeze.EntityManager(
{ dataService: dataService, metadataStore: metadataStore }
);
Update 2
Metadata generated from the server odata/$metadata
:
<edmx:Edmx Version="1.0">
<edmx:DataServices m:DataServiceVersion="3.0" m:MaxDataServiceVersion="3.0">
<Schema Namespace="MyApp.Models">
<EntityType Name="Job">
<Key>
<PropertyRef Name="JobId"/>
</Key>
<Property Name="JobId" Type="Edm.Guid" Nullable="false"/>
<Property Name="WorkDoneById" Type="Edm.String" Nullable="false"/>
<NavigationProperty Name="WorkDoneBy" Relationship="MyApp.Models.MyApp_Models_Job_WorkDoneBy_MyApp_Models_User_WorkDoneByPartner" ToRole="WorkDoneBy" FromRole="WorkDoneByPartner"/>
</EntityType>
<EntityType Name="User">
<Key>
<PropertyRef Name="Id"/>
</Key>
<Property Name="Id" Type="Edm.String" Nullable="false"/>
<NavigationProperty Name="Jobs" Relationship="MyApp.Models.MyApp_Models_User_Jobs_MyApp_Models_Job_JobsPartner" ToRole="Jobs" FromRole="JobsPartner"/>
</EntityType>
<Association Name="MyApp_Models_Job_WorkDoneBy_MyApp_Models_User_WorkDoneByPartner">
<End Type="MyApp.Models.User" Role="WorkDoneBy" Multiplicity="0..1"/>
<End Type="MyApp.Models.Job" Role="WorkDoneByPartner" Multiplicity="0..1"/>
</Association>
<Association Name="MyApp_Models_User_Jobs_MyApp_Models_Job_JobsPartner">
<End Type="MyApp.Models.Job" Role="Jobs" Multiplicity="*"/>
<End Type="MyApp.Models.User" Role="JobsPartner" Multiplicity="0..1"/>
</Association>
</Schema>
<Schema Namespace="Default">
<EntityContainer Name="Container" m:IsDefaultEntityContainer="true">
<EntitySet Name="Jobs" EntityType="MyApp.Models.Job"/>
<EntitySet Name="Users" EntityType="MyApp.Models.User"/>
<AssociationSet Name="MyApp_Models_Job_WorkDoneBy_MyApp_Models_User_WorkDoneByPartnerSet" Association="MyApp.Models.MyApp_Models_Job_WorkDoneBy_MyApp_Models_User_WorkDoneByPartner">
<End Role="WorkDoneByPartner" EntitySet="Jobs"/>
<End Role="WorkDoneBy" EntitySet="Users"/>
</AssociationSet>
<AssociationSet Name="MyApp_Models_User_Jobs_MyApp_Models_Job_JobsPartnerSet" Association="MyApp.Models.MyApp_Models_User_Jobs_MyApp_Models_Job_JobsPartner">
<End Role="JobsPartner" EntitySet="Users"/>
<End Role="Jobs" EntitySet="Jobs"/>
</AssociationSet>
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
According to your configuration code, you appear to be using locally defined metadata
I'm not positive but I suspect that the problem is your association names; they are different for the two navigation properties:
If you want Breeze to pair up these properties, the
associationName
must be the same string value. The value itself doesn't matter; just the fact that both property ends of the relationship have the sameassociationName
. That's how Breeze learns that these properties are mated.Try something nice and short that spells out the underlying relationship ... something like
"User_Jobs"
or"Job.WorkDoneBy_User.Jobs"
Update #1
It is a mystery how you got those different
associationName
values.In a comment I asked that you look at the raw metadata coming from your OData source.
Here is an example from the "ODataBreezejsSample" that gets metadata from an OData v.3 source.
The OData nuget package is "Microsoft.AspNet.WebApi.OData" version="5.2.2". The sample uses the
EdmBuilder
class as explained in the documentationThere are two types -
TodoList
andTodoItem
- that have a one-to-many relationship. The pertinent raw metadata XML is:Note they have the same
Relationship
name: "ODataBreezejsSample.Models.TodoList_TodoItems"Then I inspect the corresponding navigation properties in the client-side metadata that Breeze produces from this XML. Both properties share the
associationName
of "TodoList_TodoItems" ... equal to theRelationship
name stripped of the namespace.What kind of OData source (and what OData version) are you querying? Are you using the
EdmBuilder
class to generate the metadata?Update #2
So you're using the "Microsoft ASP.NET Web API 2.2 for OData v4.0" v.5.6 nuget package! That means you're using OData v.4.
What a PITA!
This is a huge breaking change from v.5.5.x, the last of the OData v.3 nuget packages.
The bad news is that their implementation of v.4 broke everything ... again ... including metadata. And ... again ... they failed to follow the OData spec, especially w/r/t navigation properties in metadata.
Consequently, Breeze does not yet work with Web API OData v.4 metadata ... and there are other problems as well.
We are in the process of working through the issues with the Microsoft OData team. Until then, you're options are to wait for us or go back to OData v.3.
Also critical: the datajs 3rd party client-side JavaScript library that we have all used for OData v.1-3 does not work with OData v.4. Like everyone else, you'll have to switch to the olingo library. It may be that the olingo library plays a constructive role in rationalizing navigation property metadata. I don't know and our expert on the subject is not available at the moment.
Yes ... this is a mess.