gpt4 book ai didi

ravendb - 返回 IQueryable 但需要 Raven 统计信息才能插入 TotalResults header

转载 作者:行者123 更新时间:2023-12-04 15:25:36 25 4
gpt4 key购买 nike

我有一个 WebApi 方法,它返回 RavenDB 文档的 IQueryable。调用者需要知道可能结果的数量(因为实际结果是有限的/分页的)。

所以,我在 WebApi 方法的末尾有这样的东西:

HttpContext.Current.Response.AddHeader("Total-Result-Count", 
resultsStats.TotalResults.ToString())

不幸的是,这不起作用,因为 IQueryable 还没有实际执行 - 所以统计信息将为空。

如何将统计响应 header 的人口推迟到查询执行后?

[更新]

我试图在 Controller 操作执行后应用 ActionFilter 来捕获结果......但似乎在实际枚举 IQueryable 之前调用了 ActionFilter......
public class CountQueryableResultsActionFilter : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext filterContext)
{
var controllerStats = filterContext.ActionContext.ControllerContext.Controller as IControllerStatistics;
System.Web.HttpContext.Current.Response.AddHeader("Total-Result-Count", controllerStats.TotalResults.ToString());
}
}

如果,我在 WebApi 方法的末尾调用了“IQueryable.ToArray()”,然后 Linq 查询立即执行,它生成统计信息,一切正常 - 但这将阻止用户应用他们自己的 OData 过滤器ETC...

最佳答案

好的 - 我想通了。

以下将导致仅发出一个 Raven 查询,该查询返回结果和结果计数。

感谢 David Ruttka因为他在这方面的实验。我已经修改了他的代码以与 RavenDb 一起使用。此代码将按照 RavenDB 的意图通过一个数据库查询返回结果和结果计数。

我在下面附加了我的代码 - 要使用它,您必须返回 IRavenQueryable<T>从您的 WebApi 方法(不是 IQueryable<T> )。然后,将 $inlinecount=allpages 附加到您的 Uri 将调用处理程序。此代码不会破坏其他 OData 查询扩展($take、$skip 等)

注意:此代码使用“内联”技术,因为统计信息会在消息正文中返回 - 如果您愿意,您可以更改代码以在 header 中注入(inject)统计信息 - 我只是选择使用 OData 工作的标准方式.

您可以修改此代码以包含 Raven 生成的所有统计信息。

使用以下代码向 ASP.NET 注册处理程序(在您的 Global.asax.cs 中)

注册码:
GlobalConfiguration.Configuration.MessageHandlers.Add(new WebApi.Extensions.InlineRavenCountHandler());
处理程序代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Reflection;
using System.Net.Http.Headers;
using System.Net;

namespace WebApi.Extensions
{
public class InlineRavenCountHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (!ShouldInlineCount(request))
return base.SendAsync(request, cancellationToken);

// Otherwise, we have a continuation to work our magic...
return base.SendAsync(request, cancellationToken).ContinueWith(
t =>
{
var response = t.Result;

// Is this a response we can work with?
if (!ResponseIsValid(response)) return response;

var pagedResultsValue = this.GetValueFromObjectContent(response.Content);
Type queriedType;

// Can we find the underlying type of the results?
if (pagedResultsValue is IQueryable)
{
queriedType = ((IQueryable)pagedResultsValue).ElementType;

// we need to work with an instance of IRavenQueryable to support statistics
var genericQueryableType = typeof(Raven.Client.Linq.IRavenQueryable<>).MakeGenericType(queriedType);

if (genericQueryableType.IsInstanceOfType(pagedResultsValue))
{
Raven.Client.Linq.RavenQueryStatistics stats = null;

// register our statistics object with the Raven query provider.
// After the query executes, this object will contain the appropriate stats data
dynamic dynamicResults = pagedResultsValue;
dynamicResults.Statistics(out stats);


// Create the return object.
var resultsValueMethod =
this.GetType().GetMethod(
"CreateResultValue", BindingFlags.Instance | BindingFlags.NonPublic).MakeGenericMethod(
new[] { queriedType });

// Create the result value with dynamic type
var resultValue = resultsValueMethod.Invoke(
this, new[] { stats, pagedResultsValue });

// Push the new content and return the response
response.Content = CreateObjectContent(
resultValue, response.Content.Headers.ContentType);
return response;

}
else
return response;
}
else
return response;
});
}

private bool ResponseIsValid(HttpResponseMessage response)
{
// Only do work if the response is OK
if (response == null || response.StatusCode != HttpStatusCode.OK) return false;

// Only do work if we are an ObjectContent
return response.Content is ObjectContent;
}

private bool ShouldInlineCount(HttpRequestMessage request)
{
var queryParams = request.RequestUri.ParseQueryString();
var inlinecount = queryParams["$inlinecount"];
return string.Compare(inlinecount, "allpages", true) == 0;
}

// Dynamically invoked for the T returned by the resulting ApiController
private ResultValue<T> CreateResultValue<T>(Raven.Client.Linq.RavenQueryStatistics stats, IQueryable<T> pagedResults)
{
var genericType = typeof(ResultValue<>);
var constructedType = genericType.MakeGenericType(new[] { typeof(T) });

var ctor = constructedType
.GetConstructors().First();

var instance = ctor.Invoke(null);

var resultsProperty = constructedType.GetProperty("Results");
resultsProperty.SetValue(instance, pagedResults.ToArray(), null);

var countProperty = constructedType.GetProperty("Count");
countProperty.SetValue(instance, stats.TotalResults, null);

return instance as ResultValue<T>;
}

// We need this because ObjectContent's Value property is internal
private object GetValueFromObjectContent(HttpContent content)
{
if (!(content is ObjectContent)) return null;

var valueProperty = typeof(ObjectContent).GetProperty("Value", BindingFlags.Instance | BindingFlags.NonPublic);
if (valueProperty == null) return null;

return valueProperty.GetValue(content, null);
}

// We need this because ObjectContent's constructors are internal
private ObjectContent CreateObjectContent(object value, MediaTypeHeaderValue mthv)
{
if (value == null) return null;

var ctor = typeof(ObjectContent).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault(
ci =>
{
var parameters = ci.GetParameters();
if (parameters.Length != 3) return false;
if (parameters[0].ParameterType != typeof(Type)) return false;
if (parameters[1].ParameterType != typeof(object)) return false;
if (parameters[2].ParameterType != typeof(MediaTypeHeaderValue)) return false;
return true;
});

if (ctor == null) return null;

return ctor.Invoke(new[] { value.GetType(), value, mthv }) as ObjectContent;
}
}

public class ResultValue<T>
{
public int Count { get; set; }
public T[] Results { get; set; }
}
}

关于ravendb - 返回 IQueryable 但需要 Raven 统计信息才能插入 TotalResults header ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10544377/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com