我目前正在将Web API v2与OData v3链接到Kendo Grid。我在获取网格以将模型正确序列化到 PatchEntityAsync
method上的 AsyncEntitySetController<TEntity, TKey>
class时遇到问题。传递给Delta<TEntity>
方法的PatchEntityAsync
是null
,这显然是不正确的。
首先,实体框架模型。我有一个GameSeries
定义:
[Table("stats.GameSeries")]
public class GameSeries
{
[Key]
public int GameSeriesId { get; set; }
[MaxLength(500)]
[Required]
public string Description { get; set; }
public string Notes { get; set; }
}
然后是
Game
定义,每个Game
实例都有一个GameSeries
实例的引用:[Table("stats.Game")]
public class Game
{
[Key]
public int GameId { get; set; }
[MaxLength(500)]
[Required]
public string Description { get; set; }
public int GameSeriesId { get; set; }
[ForeignKey("GameSeriesId")]
public virtual GameSeries GameSeries { get; set; }
public int Revision { get; set; }
[MaxLength(100)]
public string Tag { get; set; }
public string Notes { get; set; }
}
当使用JSON查询
Game
并在$expand
属性上发布GameSeries
时,我得到以下期望/正确的信息:{
"odata.metadata":
"http://localhost:7566/odata/$metadata#Games",
"odata.count":"58",
"value":[
{
"GameSeries": {
"GameSeriesId": 1,
"Description":"Street Fighter IV",
"Notes":null
},
"GameId": 1,
"Description": "Street Fighter IV",
"GameSeriesId": 1,
"Revision": 1,
"Tag": null,
"Notes": null
}, {
"GameSeries": {
"GameSeriesId":1,
"Description": "Street Fighter IV",
"Notes": null
},
"GameId": 2,
"Description": "Super Street Fighter IV",
"GameSeriesId": 1,
"Revision": 2,
"Tag": null,
"Notes": null
},
// And so on...
]
}
我通过OData Web API(Microsoft.AspNet.WebApi.OData 5.2.0)端点向Kendo UI网格公开这些内容。这是网格的配置:
function initializeGrid(selector, entitySet, key, modelFields, columns, expand) {
// Edit and destroy commands.
columns.push({ command: ["edit", "destroy"], title: "Operations" });
// The main key is not editable.
modelFields[key].editable = false;
modelFields[key].defaultValue = 0;
var baseODataUrl = "/odata/" + entitySet,
options = {
dataSource: {
type: "odata",
pageSize: 50,
//autoSync: true,
transport: {
read: {
url: baseODataUrl,
dataType: "json",
data: {
$expand: expand
}
},
update: {
url: function(data) {
return baseODataUrl + "(" + data[key] + ")";
},
type: "patch",
dataType: "json"
},
destroy: {
url: function (data) {
return baseODataUrl + "(" + data[key] + ")";
},
dataType: "json"
},
create: {
url: baseODataUrl,
dataType: "json",
contentType: "application/json;odata=verbose"
}
},
batch: false,
serverPaging: true,
serverSorting: true,
serverFiltering: true,
schema: {
data: function (data) {
return data.value;
},
total: function (data) {
return data["odata.count"];
},
model: {
id: key,
fields: modelFields
}
}
},
height: 550,
toolbar: ["create"],
filterable: true,
sortable: true,
pageable: true,
editable: "popup",
navigatable: true,
columns: columns
};
selector.kendoGrid(options);
}
$(function () {
var baseODataUrl = "/odata/",
gameSeriesIdDataSource = new kendo.data.DataSource({
type: "odata",
schema: {
data: function (data) {
return data.value;
},
total: function (data) {
return data["odata.count"];
}
},
transport: {
read: {
url: baseODataUrl + "GameSeries",
dataType: "json"
}
}
}),
gameSeriesIdAutoCompleteEditor = function(container, options) {
$('<input data-text-field="Description" data-value-field="GameSeriesId" data-bind="value:GameSeriesId"/>')
.appendTo(container)
.kendoDropDownList({
autoBind: false,
dataSource: gameSeriesIdDataSource,
dataTextField: "Description",
dataValueField: "GameSeriesId"
});
};
initializeGrid($("#grid"), "Games", "GameId", {
GameId: {
title: "Game ID",
editable: false
},
Description: { type: "string" },
GameSeriesId: { type: "integer" },
Revision: { type: "integer" },
Tag: { type: "string" },
Notes: { type: "string" }
}, [
{ field: "GameId", title: "Game ID" },
"Description",
{ field: "GameSeries.Description", title: "Game Series", editor: gameSeriesIdAutoCompleteEditor },
"Revision",
"Tag",
"Notes"
], "GameSeries");
});
}(jQuery));
这将正确渲染网格,在这里我将显示
GameSeries.Description
而不是GameSeries
的数字ID。但是,我认为部分问题来自于我如何定义自定义编辑器,特别是Kendo要求的
data
属性:$('<input data-text-field="Description" data-value-field="GameSeriesId" data-bind="value:GameSeriesId"/>')
我觉得我应该使用点表示法来引用
GameSeries
实例上的Game
属性,但是我不确定如何使用。另外,我相信这里的绑定(bind)会导致create命令失败。应该有一些方法可以设置数据绑定(bind)属性,以允许新创建以及编辑现有属性。
但是,当我让编辑器弹出一个现有实例时,它会正确地执行所有包含
GameSeries
实例的下拉列表。我可以进行更改,并且当我这样做时,我会通过Fiddler注意到身体正在通过,尽管我注意到一些差异:
{
"GameSeries": {
"GameSeriesId": 1,
"Description": "Street Fighter IV",
"Notes": null
},
"GameId": "1",
"Description":
"Street Fighter IV",
"GameSeriesId": "4",
"Revision": "1",
"Tag": "Test",
"Notes": null
}
在这种情况下,
GameSeriesId
属性正确地填充了更改(我想要4),但是扩展后的GameSeries
属性的“GameSeriesId”为1。进行此调用时,传入的
Delta<Game>
实例为null。我试过的
我注意到扩展后的
GameSeriesId
属性上的GameSeries
属性没有被字符串化。我已将值更改为"1"
,并且Delta<Game>
实例仍然为空。我已经将调用复制到OData点,以不包括扩展的
GameSeries
属性,因此有效负载如下所示:{
"GameId": "1",
"Description":
"Street Fighter IV",
"GameSeriesId": "4",
"Revision": "1",
"Tag": "Test",
"Notes": null
}
并填充
Delta<Game>
。我不确定从有效负载中删除扩展的GameSeries
属性是否正确,还是不确定是否应该在服务器端或Kendo网格中解决它。 最佳答案
由于外键ID已成功更改,因此您可以在进行更新时仅排除导航属性GameSeries
。
仅通过使用外键ID更新关系时,EF效果很好。
因此,让OData指向包括GameSeries
,但在进行更新时将其排除。您可以使用parameterMap拦截更新操作。
parameterMap: function (data, type) {
if (type === "update") {
delete data.GameSeries;
return JSON.stringify(data);
}
// Returns as it is.
return data;
}
更新
要将编辑器与网格同步,您需要绑定(bind)更改事件并在网格中手动更改模型的属性。
gameSeriesIdAutoCompleteEditor = function (container, options) {
/* omitted code */
.kendoDropDownList({
/* omitted code */
change: function (e) {
options.model.GameSeries = this.dataItem();
}