public void BuildQuery_ReturnsNull_IfPathHasUnsupportedSegmen(string path) { var model = ODataPathQueryModel.GetModel(); var pathHandler = new DefaultODataPathHandler(); var odataPath = pathHandler.Parse(model, "http://any/", path); IQueryable source = Array.CreateInstance(typeof(ODataPathQuery_Customer), 0).AsQueryable(); var queryBuilder = new ODataPathQueryBuilder(source, odataPath); ODataPathQueryResult result = queryBuilder.BuildQuery(); Assert.Null(result); }
public void BuildQuery_SetValueFlagToTrue_IfPathHasValueSegment(string path, string expectedQuery) { var model = ODataPathQueryModel.GetModel(); var pathHandler = new DefaultODataPathHandler(); var odataPath = pathHandler.Parse(model, "http://any/", path); IQueryable source = Array.CreateInstance(typeof(ODataPathQuery_Customer), 0).AsQueryable(); var queryBuilder = new ODataPathQueryBuilder(source, odataPath); ODataPathQueryResult result = queryBuilder.BuildQuery(); string queryExpression = ExpressionStringBuilder.ToString(result.Result.Expression); queryExpression = RemoveNameSpace(queryExpression); Assert.Equal(expectedQuery, queryExpression); Assert.True(result.HasValueSegment); Assert.False(result.HasCountSegment); }
/// <summary> /// Transforms the result of the action based on the sequence of property accesses in the odata path /// after the action is executed. /// It first tries to retrieve the IQueryable from the /// returning response message. It then uses the <see cref="ODataPathQueryBuilder"/> to transform /// the query and sets the result back to the response message. /// </summary> /// <param name="actionExecutedContext">The context related to this action, including the response message, /// request message and HttpConfiguration etc.</param> public override void OnActionExecuted(ActionExecutedContext actionExecutedContext) { if (actionExecutedContext == null) { throw Error.ArgumentNull("actionExecutedContext"); } HttpRequest request = actionExecutedContext.HttpContext.Request; if (request == null) { throw Error.Argument("actionExecutedContext", SRResources.ActionExecutedContextMustHaveRequest); } ControllerActionDescriptor actionDescriptor = actionExecutedContext.ActionDescriptor as ControllerActionDescriptor; if (actionDescriptor == null) { throw Error.Argument("actionExecutedContext", SRResources.ActionContextMustHaveDescriptor); } HttpResponse response = actionExecutedContext.HttpContext.Response; ObjectResult responseContent = actionExecutedContext.Result as ObjectResult; IQueryable result = responseContent.Value as IQueryable; SingleResult singleResult = responseContent.Value as SingleResult; if (singleResult != null) { // This could be a SingleResult, which has the property Queryable. // But it could be a SingleResult() or SingleResult<T>. Sort by number of parameters // on the property and get the one with the most parameters. PropertyInfo propInfo = singleResult.GetType().GetProperties() .Where(p => p.Name.Equals("Queryable")) .OrderByDescending(p => p.GetIndexParameters().Count()) .FirstOrDefault(); result = propInfo.GetValue(singleResult) as IQueryable; } if (result != null) { ODataPath path = actionExecutedContext.HttpContext.ODataFeature().Path; IEdmModel model = actionExecutedContext.HttpContext.Request.GetModel(); var queryBuilder = new ODataPathQueryBuilder(result, path); ODataPathQueryResult transformedResult = queryBuilder.BuildQuery(); if (transformedResult == null) { actionExecutedContext.Result = new NotFoundObjectResult(null); } else if (path.EdmType.TypeKind == EdmTypeKind.Collection || transformedResult.HasCountSegment) { responseContent.Value = transformedResult.Result; } else { Type elementType = transformedResult.Result.ElementType; // If we return the IQueryable as the result, then the response will be returned as a collection // so we have to return a single result. We can either return the materialized single result using SingleOrDefault, // which will run the query against the underlying data source. Or we can wrap the IQueryable result with // a SingleResult<T>. // If the action has [EnableQuery], then we return a SingleResult<T> which allows query options to be // applied to the IQueryable. If we returned the materialized object, then query options like $expand // would return null because navigation properties are not fetched from relational databases by default. // If the action does not have [EnableQuery], then we run the query using SingleOrDefault and return the // actual object. If we returned a SingleResult<T> in this case, we'd get an error because the serializer // does not recognize it. if (actionDescriptor.MethodInfo.GetCustomAttributes <EnableQueryAttribute>().Any()) { // calls SingleResult.Create<T>(IQueryable<T>) responseContent.Value = ExpressionHelpers.CreateSingleResult(transformedResult.Result, transformedResult.Result.ElementType); } else { try { object singleValue = QueryHelpers.SingleOrDefault(transformedResult.Result, new WebApiActionDescriptor(actionDescriptor)); if (singleValue == null) { actionExecutedContext.Result = new NotFoundObjectResult(null); } else { responseContent.Value = singleValue; } } catch (NullReferenceException) { actionExecutedContext.Result = new NotFoundObjectResult(null); } } } } }