使用$ expand请求控制返回的内容
因此,使用ODataController
,您可以控制返回的内容,如果有人执行/odata/Foos(42)/Bars
,因为您将在FoosController
上调用, FoosController
所示:
public IQueryable GetBars([FromODataUri] int key) { }
但是如果你想控制什么时候有什么东西/odata/Foos?$expand=Bars
? 你怎么处理那件事呢? 它会触发此方法:
public IQueryable GetFoos() { }
而且我认为它只会在您返回的IQueryable
上执行.Include("Bars")
,所以…我如何获得更多控制权? 特别是,我如何以OData不破坏的方式进行操作(例如$ select,$ orderby,$ top等继续工作。)
虽然不是我想要的解决方案(让这个内置function,伙计!),我找到了一种方法来做我想要的,虽然方式有点限制(到目前为止我只支持直接Where()
过滤)。
首先,我创建了一个自定义的ActionFilterAttribute
类。 它的目的是在EnableQueryAttribute
完成它之后采取行动,因为它修改了EnableQueryAttribute
生成的查询。
在您的GlobalConfiguration.Configure(config => { ... })
调用中,在调用config.MapODataServiceRoute()
之前添加以下内容:
config.Filters.Add(new NavigationFilterAttribute(typeof(NavigationFilter)));
它必须在之前,因为OnActionExecuted()
方法以相反的顺序调用。 你也可以使用这个filter来装饰特定的控制器,虽然我发现很难确保它以正确的顺序运行。 NavigationFilter
是你自己创建的一个类,我将发布一个更远的例子。
NavigationFilterAttribute
及其内部类, ExpressionVisitor
与注释相对较好,所以我只需粘贴它们,不再进一步评论如下:
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. 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>, as that can be // handed to a .Where(...) call on the ICollection . 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() 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() where pna.Count() == 0 || pna.Any(p => p.Name == node.Member.Name) select m).SingleOrDefault(); if (filterMethod != null) { // .Where() 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); } } } }
接下来,为了缩小过滤范围,有一些简单的属性类。
如果将PropertyDeclaringTypeAttribute
放在NavigationFilter
上的某个方法上,则只有在该属性属于该类型时才会调用该方法。 例如,给定一个具有ICollection
类型属性的类Foo
,如果你有一个带[PropertyDeclaringType(typeof(Foo))]
的filter方法,那么它只会在Foo
上调用ICollection
属性,但是不适用于任何其他课程。
PropertyNameAttribute
执行类似的操作,但是对于属性的名称而不是类型。 如果您的实体类型具有相同ICollection
多个属性,并且您希望根据属性名称进行不同的过滤,则此选项非常有用。
他们来了:
[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> OnlyReturnBarsWithSpecificSomeValue() { var someValue = SomeClass.GetAValue(); return b => b.SomeValue == someValue; } }
@Alex
1)您可以在GetBars(… int key)中添加一个参数,并使用该参数为查询选项执行更多控制器。 例如,
public IQueryable GetBars(ODataQueryOptions options, [FromODataUri] int key) { }
2)或者,您可以在动作GetBars
上添加[ GetBars
]以允许Web API OData执行查询选项。
上述就是C#学习教程:使用$ expand请求控制返回的内容分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
[EnableQuery] public IQueryable GetBars([FromODataUri] int key) { }
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/991664.html