修改 WebAPI OData QueryOptions.Filter 的最佳方法

问题描述 投票:0回答:2

我正在使用位于 http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/working-with-entity-relations 的 OData 示例项目。在 Get 中,我希望能够更改 EntitySetController 的 QueryOptions 中的过滤器:

public class ProductsController : EntitySetController<Product, int>
{
    ProductsContext _context = new ProductsContext();

    [Queryable(AllowedQueryOptions=AllowedQueryOptions.All)]
    public override IQueryable<Product> Get()
    {
        var products = QueryOptions.ApplyTo(_context.Products).Cast<Product>();
        return products.AsQueryable();
    }

我希望能够找到特别提到的属性。我可以通过解析

this.QueryOptions.Filter.RawValue
来获取属性名称来完成此操作,但我无法更新
RawValue
,因为它是只读的。不过,我可以从修改后的
FilterQueryOption
创建另一个
RawValue
实例,但我无法将其分配给
this.QueryOptions.Filter
,因为这也是只读的。

我想我可以调用新过滤器的

ApplyTo
传递它
_context.Products
,但随后我需要单独调用
ApplyTo
的其他属性的
QueryOptions
,例如
Skip
OrderBy
。还有比这更好的解决方案吗?

更新

我尝试了以下方法:

    public override IQueryable<Product> Get()
    {
        IQueryable<Product> encryptedProducts = _context.Products;

        var filter = QueryOptions.Filter;
        if (filter != null && filter.RawValue.Contains("Name"))
        {
            var settings = new ODataQuerySettings();
            var originalFilter = filter.RawValue;
            var newFilter = ParseAndEncyptValue(originalFilter);
            filter = new FilterQueryOption(newFilter, QueryOptions.Context);
            encryptedProducts = filter.ApplyTo(encryptedProducts, settings).Cast<Product>();

            if (QueryOptions.OrderBy != null)
            {
                QueryOptions.OrderBy.ApplyTo<Product>(encryptedProducts);
            }
        }
        else
        {
            encryptedProducts = QueryOptions.ApplyTo(encryptedProducts).Cast<Product>();
        }

        var unencryptedProducts = encryptedProducts.Decrypt().ToList();

        return unencryptedProducts.AsQueryable();
    }

而且它似乎在一定程度上发挥了作用。如果我设置断点,我可以在

unencryptedProducts
列表中看到我的产品,但是当该方法返回时,我没有得到任何项目。我尝试重新打开
[Queryable(AllowedQueryOptions=AllowedQueryOptions.All)]
,但没有效果。有什么想法为什么我没有收到物品吗?

更新2

我发现即使我没有使用

Queryable
属性,我的查询也被应用了两次。这意味着即使我有要返回的项目,也会使用未加密的值查询列表,因此不会返回任何值。

我尝试使用

ODataController
代替:

public class ODriversController : ODataController
{

    //[Authorize()]
    //[Queryable(AllowedQueryOptions = AllowedQueryOptions.All)]
    public IQueryable<Products> Get(ODataQueryOptions options)
    {

这成功了!这是否表明

EntitySetController
有错误?

asp.net-web-api odata
2个回答
4
投票

您可能需要重新生成 ODataQueryOptions 才能解决您的问题。假设您想修改以添加 $orderby,您可以这样做:

string url = HttpContext.Current.Request.Url.AbsoluteUri;
url += "&$orderby=name";
var request = new HttpRequestMessage(HttpMethod.Get, url);
ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Product>("Product");
var options = new ODataQueryOptions<Product>(new ODataQueryContext(modelBuilder.GetEdmModel(), typeof(Product)), request);

0
投票

我们可以使用 ActionDescriptor 来访问 EDM,请参阅:

ExtractEdmFromHttpContext

这是来自

ODataQueryParameterBindingAttribute
ODataQueryOptions<T>
enter image description here

public static ODataQueryOptions<TEntity> Create<TEntity>(string url,
    ODataQueryOptions<TEntity>? options = null) where TEntity : class
{
    if (!Uri.TryCreate(url, UriKind.Absolute, out var uri))
        throw new InvalidOperationException("Invalid Absolute URL.");

    var httpContext = new DefaultHttpContext
    {
        Request =
        {
            Scheme = uri.Scheme,
            Path = uri.AbsolutePath,
            Host = new HostString(uri.Host),
            Query = new QueryCollection(QueryHelpers.ParseQuery(uri.Query)),
            QueryString = new QueryString(uri.Query)
        }
    };

    var model = options?.Request.HttpContext.RequestServices is not null
        ? ExtractEdmFromHttpContext(options)
        : CreateDefaultEdmModel<TEntity>();

    var odataPath = httpContext.Request.ODataFeature().Path;
    var oDataQueryContext = new ODataQueryContext(model, typeof(TEntity), odataPath)
    {
        DefaultQueryConfigurations =
        {
            EnableCount = true,
            EnableExpand = true,
            EnableFilter = true,
            EnableSelect = true,
            EnableOrderBy = true,
            EnableSkipToken = true
        }
    };

    return new ODataQueryOptions<TEntity>(oDataQueryContext, httpContext.Request);
}

private static IEdmModel CreateDefaultEdmModel<TEntity>() where TEntity : class
{
    var builder = new ODataConventionModelBuilder();
    builder.AddEntityType(typeof(TEntity));
    return builder.GetEdmModel();
}

private static IEdmModel ExtractEdmFromHttpContext<TEntity>(ODataQueryOptions<TEntity> options)
    where TEntity : class
{
    var actionDescriptor = options.Request.HttpContext.GetEndpoint()!.Metadata
        .OfType<ActionDescriptor>()
        .First();

    var key = $"{ModelKeyPrefix}{typeof(TEntity).FullName}";
    var modelAsObject = actionDescriptor.Properties[key];

    return (modelAsObject as IEdmModel)!;
}
© www.soinside.com 2019 - 2024. All rights reserved.