3

我目前正在使用 Web API v2 和 OData v3 链接到 Kendo Grid。我在让网格正确地将模型序列化为上的PatchEntityAsync方法时遇到问题。传递给方法的显然是不正确的。AsyncEntitySetController<TEntity, TKey>Delta<TEntity>PatchEntityAsyncnull

首先,实体框架模型。我有一个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发出一个时,我得到以下内容,这是预期/正确的:$expandGameSeries

{
    "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 Grid。这是网格的配置:

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.

但是,我相信部分问题来自我如何定义自定义编辑器,特别data是 Kendo 所需的属性:

$('<input data-text-field="Description" data-value-field="GameSeriesId" data-bind="value:GameSeriesId"/>')

我觉得我应该使用点符号来引用实例GameSeries上的属性Game,但我不确定如何。

此外,我相信这里的绑定导致 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>传入的实例为空。

我试过的:

我注意到GameSeriesId扩展GameSeries属性上的属性没有被字符串化。我已将值更改为"1"" 并且Delta<Game>实例仍然为空。

我已复制对 OData 点的调用以不包含扩展GameSeries属性,因此有效负载如下所示:

{
    "GameId": "1",
    "Description":
    "Street Fighter IV",
    "GameSeriesId": "4",
    "Revision": "1",
    "Tag": "Test",
    "Notes": null
}

并且Delta<Game>人口众多。我不确定GameSeries从有效负载中删除扩展属性是否是正确的方法,或者是否应该在服务器端或剑道网格中解决它。

4

1 回答 1

2

由于外键 ID 已成功更改,因此您可以GameSeries在更新时排除导航属性。

EF 在仅使用外键 id 更新关系时效果很好。

因此,让 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();
        }
于 2014-08-23T15:43:33.657 回答