控制什么用$扩展请求返回 [英] Controlling what is returned with an $expand request

查看:339
本文介绍了控制什么用$扩展请求返回的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此​​,使用 ODataController ,你就会得到控制,如果有人做了什么获取返回 / ODATA / FOOS(42)/酒吧,因为你的 FoosController 像这样被调用:

So, using the ODataController, you get to control what gets returned if somebody does /odata/Foos(42)/Bars, because you'll be called on the FoosController like so:

public IQueryable<Bar> GetBars([FromODataUri] int key) { }



但是,如果你想控制什么获取返回的时候是什么有人的确 / ODATA / FOOS?$ =拓展酒吧?你怎么处理呢?它会触发此方法:

But what if you want to control what gets returned when somebody does /odata/Foos?$expand=Bars? How do you deal with that? It triggers this method:

public IQueryable<Foo> GetFoos() { }

和我想这只是做一个 .INCLUDE( 酒吧)的IQueryable<富> 您返回,所以...我如何获得更多的控制权?特别是,我怎么做,在这种方式的OData不破(即之类的东西$选择,$排序依据,$顶等继续工作。)

And I assume it just does an .Include("Bars") on the IQueryable<Foo> that you return, so... how do I get more control? In particular, how do I do it in such a way that OData doesn't break (i.e. things like $select, $orderby, $top, etc. continue working.)

推荐答案

而不是解决办法,我想(做这样的内置功能,伙计们!),我已经找到了一种方法做我想要的,尽管在一个比较有限的方式(这样到目前为止,我只支持直接其中()过滤)。

While not the solution I wanted (make this a built-in feature, guys!), I have found a way to do what I wanted, albeit in a somewhat limited manner (so far I only support direct Where() filtering).

首先,我做了一个自定义的 ActionFilterAttribute 类。其目的是采取后的 EnableQueryAttribute 已完成了它的东西,因为它修改查询 EnableQueryAttribute 。C $ C>制作了

First, I made a custom ActionFilterAttribute class. Its purpose is to take action after the EnableQueryAttribute has done its thing, as it modifies the query that EnableQueryAttribute has produced.

在你的 GlobalConfiguration.Configure(配置=> {...})打电话,添加以下的之前调用 config.MapODataServiceRoute()

In your GlobalConfiguration.Configure(config => { ... }) call, add the following before the call to config.MapODataServiceRoute():

config.Filters.Add(new NavigationFilterAttribute(typeof(NavigationFilter)));



它必须是之前的,因为 OnActionExecuted()方法被称为以相反的顺序。您也可以装饰特定的控制器,该过滤器,但我发现它很难确保它在正确的顺序运行方式。在 NavigationFilter 是您自己创建一个类,我会发布一个更远了。

It has to be before, because the OnActionExecuted() methods are called in reverse order. You can also decorate specific controllers with this filter, although I've found it harder to ensure that it's run in the correct order that way. The NavigationFilter is a class you create yourself, I'll post an example of one farther down.

<$的例子C $ C> NavigationFilterAttribute ,其内部类,一个 ExpressionVisitor 相对很好地注释文档,所以我就贴上他们没有进一步的评论如下:

NavigationFilterAttribute, and its inner class, an ExpressionVisitor is relatively well documented with comments, so I'll just paste them without further comments below:

public class NavigationFilterAttribute : ActionFilterAttribute
{
    private readonly Type _navigationFilterType;

    class NavigationPropertyFilterExpressionVisitor : ExpressionVisitor
    {
        private Type _navigationFilterType;

        public bool ModifiedExpression { get; private set; }

        public NavigationPropertyFilterExpressionVisitor(Type navigationFilterType)
        {
            _navigationFilterType = navigationFilterType;
        }

        protected override Expression VisitMember(MemberExpression node)
        {
            // Check properties that are of type ICollection<T>.
            if (node.Member.MemberType == System.Reflection.MemberTypes.Property
                && node.Type.IsGenericType
                && node.Type.GetGenericTypeDefinition() == typeof(ICollection<>))
            {
                var collectionType = node.Type.GenericTypeArguments[0];

                // See if there is a static, public method on the _navigationFilterType
                // which has a return type of Expression<Func<T, bool>>, as that can be
                // handed to a .Where(...) call on the ICollection<T>.
                var filterMethod = (from m in _navigationFilterType.GetMethods()
                                    where m.IsStatic
                                    let rt = m.ReturnType
                                    where rt.IsGenericType && rt.GetGenericTypeDefinition() == typeof(Expression<>)
                                    let et = rt.GenericTypeArguments[0]
                                    where et.IsGenericType && et.GetGenericTypeDefinition() == typeof(Func<,>)
                                        && et.GenericTypeArguments[0] == collectionType
                                        && et.GenericTypeArguments[1] == typeof(bool)

                                    // Make sure method either has a matching PropertyDeclaringTypeAttribute or no such attribute
                                    let pda = m.GetCustomAttributes<PropertyDeclaringTypeAttribute>()
                                    where pda.Count() == 0 || pda.Any(p => p.DeclaringType == node.Member.DeclaringType)

                                    // Make sure method either has a matching PropertyNameAttribute or no such attribute
                                    let pna = m.GetCustomAttributes<PropertyNameAttribute>()
                                    where pna.Count() == 0 || pna.Any(p => p.Name == node.Member.Name)
                                    select m).SingleOrDefault();

                if (filterMethod != null)
                {
                    // <node>.Where(<expression>)
                    var expression = filterMethod.Invoke(null, new object[0]) as Expression;
                    var whereCall = Expression.Call(typeof(Enumerable), "Where", new Type[] { collectionType }, node, expression);
                    ModifiedExpression = true;
                    return whereCall;
                }
            }
            return base.VisitMember(node);
        }
    }

    public NavigationFilterAttribute(Type navigationFilterType)
    {
        _navigationFilterType = navigationFilterType;
    }

    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        HttpResponseMessage response = actionExecutedContext.Response;

        if (response != null && response.IsSuccessStatusCode && response.Content != null)
        {
            ObjectContent responseContent = response.Content as ObjectContent;
            if (responseContent == null)
            {
                throw new ArgumentException("HttpRequestMessage's Content must be of type ObjectContent", "actionExecutedContext");
            }

            // Take the query returned to us by the EnableQueryAttribute and run it through out
            // NavigationPropertyFilterExpressionVisitor.
            IQueryable query = responseContent.Value as IQueryable;
            if (query != null)
            {
                var visitor = new NavigationPropertyFilterExpressionVisitor(_navigationFilterType);
                var expressionWithFilter = visitor.Visit(query.Expression);
                if (visitor.ModifiedExpression)
                    responseContent.Value = query.Provider.CreateQuery(expressionWithFilter);
            }
        }
    }
}



下一页,有几个简单的属性类,为缩小筛选的目的。

Next, there are a few simple attribute classes, for the purpose of narrowing down the filtering.

如果你把 PropertyDeclaringTypeAttribute 在对你的 NavigationFilter 的方法之一,如果属性是基于该类型只会调用该方法。例如,给定一个类与类型的属性的ICollection<酒吧GT; ,如果你有一个过滤法 [PropertyDeclaringType(typeof运算(富))] ,那么它只会被要求的ICollection<酒吧GT; 的属性,但不能用于任何其它类。

If you put PropertyDeclaringTypeAttribute on one of the methods on your NavigationFilter, it will only call that method if the property is on that type. For instance, given a class Foo with a property of type ICollection<Bar>, if you have a filter method with [PropertyDeclaringType(typeof(Foo))], then it will only be called for ICollection<Bar> properties on Foo, but not for any other class.

PropertyNameAttribute 做类似的东西,但对于属性的名称,而不是类型。如果你有相同的多个属性实体类型也可以是有用的的ICollection< T> 要过滤的不同取决于属性名

PropertyNameAttribute does something similar, but for the property's name rather than type. It can be useful if you have an entity type with multiple properties of the same ICollection<T> where you want to filter differently depending on the property name.

下面,他们是:

[AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class PropertyDeclaringTypeAttribute : Attribute
{
    public PropertyDeclaringTypeAttribute(Type declaringType)
    {
        DeclaringType = declaringType;
    }

    public Type DeclaringType { get; private set; }
}

[AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class PropertyNameAttribute : Attribute
{
    public PropertyNameAttribute(string name)
    {
        Name = name;
    }

    public string Name { get; private set; }
}



最后,这里有一个例子 NavigationFilter 类:

class NavigationFilter
{
    [PropertyDeclaringType(typeof(Foo))]
    [PropertyName("Bars")]
    public static Expression<Func<Bar,bool>> OnlyReturnBarsWithSpecificSomeValue()
    {
        var someValue = SomeClass.GetAValue();
        return b => b.SomeValue == someValue;
    }
}

这篇关于控制什么用$扩展请求返回的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆