【问题标题】:oData $count doesn't work with EntitySetController<TEntity, TKey> in web api 4oData $count 不适用于 Web api 4 中的 EntitySetController<TEntity, TKey>
【发布时间】:2013-05-15 00:36:24
【问题描述】:

我正在使用 EntitySetController 创建一个 oData web api 控制器,一切正常,除了获取总记录数。

控制器定义如下:

public class MyODataController : EntitySetController<Entity1, int> where TEntity : class
{
    public override IQueryable<Entity1> Get()
    {
        return EntityDatabase.Get();
    }
}

当我调用计数时:

http://localhost:44789/oData/MyOData/$count

我得到错误: 检测到无效操作。 '$count' 不是可以绑定到 'Collection([Entity1 Nullable=False])' 的操作。

【问题讨论】:

    标签: asp.net-web-api odata


    【解决方案1】:

    一种选择是将 $top=0 与 $inlinecount=allpages 结合使用。有点解决方法,但我发现它很好用,我宁愿返回对象而不是单个整数值。这也适用于过滤器:

    http://localhost:44789/oData/MyOData?$filter=MyFilter&$top=1&$inlinecount=allpages
    

    【讨论】:

      【解决方案2】:

      localhost:..../?$inlinecount=allpages&$skip=0&$top=0 我认为这是击球手。您无需添加更多代码。

      【讨论】:

      • 您可以使用更少的代码:不需要 $skip=0。顺便说一句,过滤仍然有效。
      【解决方案3】:

      OData v4.0 的最新包 Web API 2.2 支持 $count。

      【讨论】:

        【解决方案4】:

        不幸的是,Web API 还不支持 $count 开箱即用,尽管它应该在未来的版本中。同时,您仍然可以通过定义这些类来添加支持:

        public class CountODataRoutingConvention : EntitySetRoutingConvention
        {
            public override string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, ILookup<string, HttpActionDescriptor> actionMap)
            {
                if (controllerContext.Request.Method == HttpMethod.Get && odataPath.PathTemplate == "~/entityset/$count")
                {
                    if (actionMap.Contains("GetCount"))
                    {
                        return "GetCount";
                    }
                }
                return null;
            }
        }
        
        public class CountODataPathHandler : DefaultODataPathHandler
        {
            protected override ODataPathSegment ParseAtEntityCollection(IEdmModel model, ODataPathSegment previous, IEdmType previousEdmType, string segment)
            {
                if (segment == "$count")
                {
                    return new CountPathSegment();
                }
                return base.ParseAtEntityCollection(model, previous, previousEdmType, segment);
            }
        }
        
        public class CountPathSegment : ODataPathSegment
        {
            public override string SegmentKind
            {
                get
                {
                    return "$count";
                }
            }
        
            public override IEdmType GetEdmType(IEdmType previousEdmType)
            {
                return EdmCoreModel.Instance.FindDeclaredType("Edm.Int32");
            }
        
            public override IEdmEntitySet GetEntitySet(IEdmEntitySet previousEntitySet)
            {
                return previousEntitySet;
            }
        
            public override string ToString()
            {
                return "$count";
            }
        }
        

        在 MapODataRoute 中注册它们:

        IList<IODataRoutingConvention> routingConventions = ODataRoutingConventions.CreateDefault();
        routingConventions.Insert(0, new CountODataRoutingConvention());
        config.Routes.MapODataRoute("OData", "odata", GetModel(), new CountODataPathHandler(), routingConventions);
        

        在你的控制器中,添加这个方法:

        public HttpResponseMessage GetCount(ODataQueryOptions<TEntity> queryOptions)
        {
            IQueryable<TEntity> queryResults = queryOptions.ApplyTo(Get()) as IQueryable<TEntity>;
            int count = queryResults.Count();
            HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
            response.Content = new StringContent(count.ToString(), Encoding.UTF8, "text/plain");
            return response;
        }
        

        为避免将 GetCount() 复制到每个控制器,您可以定义一个派生自定义 GetCount 的 EntitySetController 的基类。

        【讨论】:

        • 谢谢,但是如果我想通过过滤器获取总计数,例如只获取与 url 中的过滤器匹配的实体的总记录数,我该怎么办?
        • 好点坦率。我已经更新了控制器代码,因此它也可以与过滤器等查询选项一起使用。
        • $count 必须返回集合中的项目总数(参见 [MS-ODATA],第 58 页,URI 模式 #15:必须标识基本 EntityType 的所有实例的计数或在最后一个 URI 段中指定的指定 EntitySet 中的任何 EntityType 子类型)。如果您需要计算应用了过滤器的项目,请使用 $inlinecount。
        • Anton,inlinecount 的问题在于它也返回了所有数据,这在我的情况下非常重要。我想在没有实际数据的情况下简单地计算满足过滤器的实体的数量,以便我可以在客户端有效地管理传输。我认为这与 [MS-ODATA] 的上述声明不符...
        • 内联计数应该在过滤器之后,计数应该在查询过滤器之前(但可能的总数(所以在你所有的身份验证过滤等之后))
        【解决方案5】:

        如果您使用以下内容:

        http://localhost:44789/oData/MyOData?$inlinecount=allpages
        

        那么总计数将包含在您的 get return 中。

        【讨论】:

        • 谢谢,不过我试过了,allpages返回不加filter的count,比如我用**/oData/MyOData$inlinecount=allpages&filter=age ge 20,还是返回所有没有过滤器的实体
        • 对不起,我的错,我忘记在过滤器前添加“$”,这种方式有效
        • 忘记“$”是我存在的祸根。我已经到了这样的地步,如果 odata 调用没有按预期工作,我会检查是否缺少“$”。
        • 该方法执行带有top 0子句的SQL查询,即使没有返回任何内容,查询仍然发送到SQL Server,SQL Server解析并执行。
        • 你有什么解决办法吗?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-12-28
        • 1970-01-01
        • 2012-10-09
        • 2013-01-12
        • 2017-04-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多