我目前正在使用Web API v2与OData v3连接到剑道网格。我有问题,使网格正确序列化模型到AsyncEntitySetController<TEntity, TKey>
类上的PatchEntityAsync
方法。传递给PatchEntityAsync
方法的Delta<TEntity>
是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
,并在GameSeries
属性上发出$expand
时,我得到以下内容,这是预期的/正确的:
{
"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)端点到剑道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。
然而,我认为部分问题来自于我如何定义自定义编辑器,特别是剑道需要的data
属性:
$('<input data-text-field="Description" data-value-field="GameSeriesId" data-bind="value:GameSeriesId"/>')
我得到的感觉我应该使用点符号来引用Game
实例上的GameSeries
属性,但我不确定如何。
另外,我认为这里的绑定导致了create命令失败。应该有一些方法来设置数据绑定属性,以允许新的创建,以及编辑现有的。
但是,当我让编辑器弹出一个现有实例时,它会正确地使用填充了所有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>
实例为空。
我试过了:
我注意到扩展的GameSeries
属性上的GameSeriesId
属性没有字符串化。我已经将值更改为"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
。
EF工作得很好。
所以让OData指向包括GameSeries
,但在进行更新时排除它。您可以使用parameterMap来拦截更新操作。
parameterMap: function (data, type) {
if (type === "update") {
delete data.GameSeries;
return JSON.stringify(data);
}
// Returns as it is.
return data;
}
要使编辑器与网格同步,您需要绑定更改事件并手动更改网格中模型的属性。
gameSeriesIdAutoCompleteEditor = function (container, options) {
/* omitted code */
.kendoDropDownList({
/* omitted code */
change: function (e) {
options.model.GameSeries = this.dataItem();
}