【问题标题】:automatically expand the result of an odata function自动展开 odata 函数的结果
【发布时间】:2019-03-20 01:02:26
【问题描述】:

我定义了一个 odata 函数来模拟最近的核心版本中尚不支持的 $search。我想返回核心实体加上一个扩展实体,该实体将在返回的 json 值数组中的每个 Person 上转换为一个 js 对象。

我尝试了 odata/People/MyNS.Find(text='john', orderby='CreatedOn')?$expand=CurrentWork,其中 CurrentWork 在 People 上,但没有奏效。

关于如何做到这一点的想法?

  // my controller code for the function
  [HttpGet]
  public ActionResult<ICollection<People>> Find([FromODataUri] string text,
        [FromODataUri] string orderBy)
        {
            if (text == null || text.Length == 0)
                return Get().ToList();
            if (orderBy == null || orderBy.Length == 0)
                orderBy = "CreatedOn";
            return _db.People
                .Where(p => p.FirstName.Contains(text)
                    || p.LastName.Contains(text)
                    || p.Nickname.Contains(text))
                .OrderBy(orderBy)
                .Take(5000)
                .ToList();
        }

CurrentWork 在非函数中的常规扩展工作正常,例如odata/People?$expand=CurrentWork.

【问题讨论】:

    标签: asp.net-web-api asp.net-core odata


    【解决方案1】:

    这是我最后想出的。我注意到包含的实体正在从数据库中提取大量数据,因此我通过具体化减少了拉动。包括刚刚拉出的所有内容,我无法直接减少Include,所以我不得不使用Select

        [HttpGet]
        public IOrderedQueryable Find2([FromODataUri] string text,
        [FromODataUri] string orderBy)
        {
            if (orderBy == null || orderBy.Length == 0)
                orderBy = "CreatedOn DESC";
            if (text == null || text.Length == 0)
                return Get().OrderBy(orderBy);
            var r = LikeToRegular(text);
            return _db.People
            .AsNoTracking() // can't use if using lazy loading
            .Select(p => new
            {
                p.FirstName,
                p.LastName,
                p.Nickname,
                p.CreatedOn,
                p.CurrentWork.Title,
                p.CurrentWork.Company.CompanyName
            })
            // Forces local computation, so pulls entire people dataset :-(
            .Where(x => Regex.IsMatch(x.LastName ?? "", r)
            || Regex.IsMatch(x.FirstName ?? "", r, RegexOptions.IgnoreCase)
            || Regex.IsMatch(x.Nickname ?? "", r, RegexOptions.IgnoreCase)
            || Regex.IsMatch($"{x.FirstName} {x.LastName}", r,
                    RegexOptions.IgnoreCase))
            .OrderBy(orderBy);
        }
        // Allow some wildcards in the search...
        public static String LikeToRegular(String value)
        {
            return "^" + Regex.Escape(value)
            .Replace("_", ".")
            .Replace("%", ".*") + "$";
        }
    

    【讨论】:

    • 请注意,似乎取决于您声明函数的方式,即使您不会收到错误,您也可能会或可能不会返回有效的 odata 响应。上面的函数返回一个纯 json 数组作为无效 odata 的响应。 Vinit 的评论似乎是正确的,您需要包含 $expand 查询参数,以便 Include 不仅可以从数据库中提取数据,而且如果您正在生成有效的 odata 响应,还可以将其包含在 odata 格式的响应中开始。
    【解决方案2】:

    通过查看 Linq 查询,它只获取 People 数据,而不是它的任何子集合。您应该使用Include 来获取子集合的数据以及父实体,如下所示。阅读更多关于加载相关实体here

    // my controller code for the function
    [HttpGet]
    public ActionResult<ICollection<People>> Find([FromODataUri] string text,
        [FromODataUri] string orderBy)
    {
        if (text == null || text.Length == 0)
            return Get().ToList();
        if (orderBy == null || orderBy.Length == 0)
            orderBy = "CreatedOn";
        return _db.People
            .Where(p => p.FirstName.Contains(text)
                || p.LastName.Contains(text)
                || p.Nickname.Contains(text))
            .Include(p => p.CurrentWork) // I have added this line
            .OrderBy(orderBy)
            .Take(5000)
            .ToList();
    }
    

    注意:您仍然需要使用 $expand=CurrentWork 作为查询字符串。如果没有此查询字符串,服务器将在向客户端发送响应之前删除子集合。

    【讨论】:

    • 看起来确实可以。在阅读了更多文档后,我还决定让 aspnetcore 通过返回 IOrderedQueryable、在控制器上设置 maxtop、丢弃跟踪和使用正则表达式来调用查询,这会在初始获取后强制在客户端进行评估,但允许我更轻松地使用通配符。对于大型人物数据集来说不是很好,但效果很好。我的“包含”仍然提取了太多数据,但这是另一天的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-20
    • 2017-09-01
    • 1970-01-01
    • 2017-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多