使用 Kendo 网格和 Odata 数据源对数字列进行字符串过滤的最简单方法是什么?

What's the simplest way to do string contains filtering on a numeric column with a Kendo Grid and Odata data source?

我有一个带有数据源的 kendo 网格:

{
    type: 'odata-v4',
    transport: {
        read: '/odata/Books'
    },
    schema: {
        model: {
            id: 'id',
            fields: {
                id: {
                    type: 'number'
                }
            }
        }
    },
    serverPaging: true,
    serverFiltering: true,
    serverSorting: true
}

和一个 C# odata 控制器:

namespace What.Ever
{
    [System.Web.Http.Authorize]
    [ValidateAntiForgeryToken]
    [EnableQuery]
    public class BooksController : ODataController
    {
        private readonly IDataService _dataService;

        public BooksController(IDataService dataService)
        {
            _dataService = dataService;
        }

        public IHttpActionResult Get()
        {
            var displayData = _dataService.RetrieveData().AsQueryable();

            return Ok(displayData);
        }
    }
}

但我想使用一个包含字符串的过滤器而不是那种笨拙的数字过滤器,它对数字 ID 的效果不如它可能适用于数字统计的效果。用户将能够仅输入 Id 的一部分并取回包含该 id 的任何结果。 kendo 网格需要一个具有字符串 'type' 的模式模型才能使用包含过滤器,而不是数字。如果我将 Id 设置为字符串类型,那么当网格尝试在该字段上执行本机字符串函数时,我会收到错误消息。

我们的一个解决方案是创建一个 IdAsString 字段,然后在 odata url 上进行字符串替换,以便在 C#/服务器端将其设置回 'Id'。这样它保留了数字排序,但它似乎是一个非常不优雅的解决方案。对于内存中有集合的无数据解决方案,我们通常为每个 id 字段创建一个字符串版本,然后对其进行自定义排序。否则,如果它不是 odata 并且它使用服务器端排序,我们只使用 linq 逻辑来实现自定义过滤。

也许我可以将 id 设置为字符串并将其转换为字符串,然后在服务器端实现自定义排序?

这似乎是一个并不少见的用例,所以我想知道是否有很好的解决方案。通过修改 kendo 配置的某些客户端部分或使用 [EnableQuery] 属性的服务器端 C# odata 支持。

在使用 Odata 和 Kendo grids/datasources 保持数字排序的同时,对数字 ID 进行字符串包含过滤的最佳方法是什么?

最终,我制作了 id 字段的字符串版本,但将 odata 排序服务器端短路以使用原始数字字段。因此我们将获得包含过滤以及数字排序而不是基于字符串的词法排序。

为了替换服务器端的字符串排序,我从 controller/action 中删除了 odata [EnableQuery] 属性,而是添加了一个模板化的 ODataQueryOptions 参数。使用 ODataQueryOptions 并调用其 ApplyTo 方法几乎可以替代 [EnableQuery] 属性所做的工作。所以属性和参数在很大程度上是可以互换的,但也是相互排斥的。如果您要添加两者,odata 系统会将两组 odata 选项应用于同一结果集,这可能会导致错误或奇怪的行为。

举个控制器动作的例子:

[EnableQuery]
public IHttpActionResult Get()
{
    var displayData = _dataService.RetrieveData().AsQueryable();

    return Ok(displayData);
}

变成了:

public IQueryable<Book> Get(ODataQueryOptions<Book> options)
{
    var displayData = _dataService.RetrieveData().AsQueryable();

    _oDataSortingService.ReplaceSingleSortAsStringArguments(options);

    var results = options.ApplyTo(displayData);

    return results as IQueryable<Book>;
}

ReplaceSingleSortAsStringArguments 查找任何带有 'AsString' 后缀的排序参数,然后操作 ODataQueryOptions 的排序节点以将 AsString 字段替换为其非后缀同行。

options.OrderBy.OrderByNodes.Clear(); 将清除当前。然后您可以在 ODataQueryOptions 对象的 OrderBy 属性 上的 OrderByNodes 列表中创建一个 OrderByNode 添加。像这样:

var entityType = options.Context.ElementType as IEdmEntityType;
var edmProperty = entityType.FindProperty(newSortText);
var nodeDirection = options.OrderBy.OrderByNodes.First().Direction;
options.OrderBy.OrderByNodes.Clear();
options.OrderBy.OrderByNodes.Add(new OrderByPropertyNode(
    edmProperty,
    nodeDirection));

在我查找当前排序参数的地方缺少一些代码,但你明白了。这应该允许一个人同时进行字符串过滤和数字排序。日期可能也是如此,但您可能需要传递时区以确保字段的字符串版本与用户处于同一时区。