我正在使用breezejs v.1.5.4
与OData Web Api控制器(如果它有所不同,则使用AngularJS v.1.4.0
)。
我有以下模型(简化):
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; }
}
当尝试获取Job
信息并展开WorkDoneBy
时,它可以正常工作并获取用户信息(即用户绑定到作业)。当我尝试将Jobs
与用户关联时,我得到一个空数组。我检查了网络,Jobs
与服务器响应一起传输,但未附加到用户实例。
我的JS查询是这样的:
var query = new breeze.EntityQuery()
.from("Users")
.expand("Jobs")
.where(new breeze.Predicate("Id", "eq", "Some long Guid"));
有什么建议吗?
我也在使用datajs v.1.1.3
和odata服务适配器。
以下是元数据:
{
"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"
}
}
这是微风配置:
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 }
);
从服务器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>
答案 0 :(得分:2)
根据您的配置代码,您似乎正在使用本地定义的元数据
虽然它有点令人困惑,因为您问题中前面显示的元数据对象附加到一个显示
"hasServerMetadata": true
的数据服务。我不知道这个内部数据服务实际上很重要。
我不是肯定的,但我怀疑问题是你的关联名称;它们对于两个导航属性是不同的:
// Job
...
"navigationProperties": [
{
"name": "WorkDoneBy",
...
"associationName": "MyApp_Models_Job_WorkDoneBy_MyApp_Models_User_WorkDoneByPartner"
}
]
// User
...
"navigationProperties": [
{
"name": "Jobs",
...
"associationName": "MyApp_Models_User_Jobs_MyApp_Models_Job_JobsPartner"
}
]
如果您希望Breeze配对这些属性,associationName
必须是相同的字符串值。价值本身并不重要;只是关系的两个属性结尾具有相同的associationName
这一事实。这就是Breeze了解这些属性是如何交配的。
尝试一些好的和简短的方法来解释潜在的关系...像"User_Jobs"
或"Job.WorkDoneBy_User.Jobs"
如何获得这些不同的associationName
值,这是一个谜。
在评论中,我要求您查看来自OData源的原始元数据。
以下是“ODataBreezejsSample”中的示例,该示例从OData v.3源获取元数据。
OData nuget包是"Microsoft.AspNet.WebApi.OData" version="5.2.2"。该示例使用EdmBuilder
类as explained in the documentation
有两种类型 - TodoList
和TodoItem
- 具有一对多的关系。相关的原始元数据XML是:
// TodoList
<NavigationProperty Name="TodoItems" Relationship="ODataBreezejsSample.Models.TodoList_TodoItems" ... />
// TodoItem
<NavigationProperty Name="TodoList" Relationship="ODataBreezejsSample.Models.TodoList_TodoItems" ... />
请注意,它们具有相同的Relationship
名称:“ODataBreezejsSample.Models.TodoList_TodoItems”
然后,我检查Breeze从此XML生成的客户端元数据中的相应导航属性。两个属性共享“{TodoList_TodoItems”的associationName
...等于剥离命名空间的Relationship
名称。
您查询的是哪种OData源(以及OData版本)?您使用EdmBuilder
类来生成元数据吗?
所以你正在使用"Microsoft ASP.NET Web API 2.2 for OData v4.0" v.5.6 nuget包!这意味着你正在使用OData v.4。
PITA !
这是对v.5.5.x(最后一个OData v.3 nuget包)的巨大改变。
他们怎么能做出那么大的飞跃并重新使用版本号,特别是主要的版本数字?这令人难以置信。
为了真正混淆事物,现在有两个nuget包名称略有不同:
- v3:https://www.nuget.org/packages/Microsoft.AspNet.WebApi.OData/
- v4:https://www.nuget.org/packages/Microsoft.AspNet.OData/
您是否碰巧注意到v.3包名称中间的“WebApi”?我一开始没有。
坏消息是,他们对v.4的实施打破了一切......再次......包括元数据。而且......再次......他们没有遵循OData规范,尤其是元数据中的w / r / t导航属性。
因此,Breeze尚未使用Web API OData v.4元数据......还有其他问题。
我们正在解决Microsoft OData团队的问题。在那之前,你可以选择等我们或者回到OData v.3。
同样重要:我们用于OData v.1-3的 datajs 第三方客户端JavaScript库不适用于OData v.4。与其他人一样,您必须切换到olingo库。可能是olingo库在合理化导航属性元数据方面发挥了建设性作用。我不知道,目前还没有关于这个问题的专家。
是的......这是一团糟。
答案 1 :(得分:0)
而不是查询你可以在管理器上使用fetchEntityByKey。这将按键获取实体。
function getAJobDetail(){
return manager.fetchEntityByKey(
"Jobs", "Some long Guid", true)
.then(fetchSucceeded)
.fail(queryFailed);
function fetchSucceeded(data) {
var s = data.entity;}
function queryFailed(error) {
var msg = '[datacontext.js] Error retrieving data. ' + error.message;
//logError(msg, error);
throw error;
}
}
注意:只有当您的某个Guid键必须是主键时,此方法才有效,否则您必须使用谓词并比较字段然后查询微风。