Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to enable $expand and $select on ODataQueryOptions?

Tags:

c#

linq

odata

The issue started here, where I am unable to call $select or $expand on results.

Given:

            var results = options.ApplyTo(_uow.Repository<ContentType>()
                .Query()
                .Get()
                .Include(u => u.User)
                .Where(u => u.UserId == userId)
                .OrderBy(o => o.Description)).Cast<ContentType>()
                .Select(x => new ContentTypeDTO()
                {
                    //projection goes here
                    ContentTypeId = x.ContentTypeId,
                    Description = x.Description,
                    UserDTO = new UserDTO 
                    { 
                        UserId = x.UserId,
                        UserName = x.User.UserName
                    }
                });

Exception:

Unable to cast the type 'System.Web.Http.OData.Query.Expressions.SelectExpandBinder+SelectSome`1[[Project.DAL.Data.Models.ContentType, Project.DAL.Data, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' to type 'Project.DAL.Data.Models.ContentType'. LINQ to Entities only supports casting EDM primitive or enumeration types."

Based on this solution, I understand that when $select or $expand are applied to the result, it is no longer a ContentType, as is expected by the Cast<ContentType>(). That solution uses HierarchyNodeExpressionVisitor, which I am unable to locate.

I want to use 'options.ApplyTo()' as I want the filtering done on the db server, to ensure I don't get back an inflated data set with hundreds of thousands of rows.

Is there a suggested way of getting around this?

-- UPDATE --

In using @Schandlich 's suggested solution below, the operation still fails with the Ok override:

Locals:

+       this    {Project.AdminWebsite.Api.Controllers.ContentTypeController}    Project.AdminWebsite.Api.Controllers.ODataBaseController {Project.AdminWebsite.Api.Controllers.ContentTypeController}
+       content {System.Data.Entity.Infrastructure.DbQuery<Project.Core.UI.Models.ContentTypeDTO>}  object {System.Data.Entity.Infrastructure.DbQuery<Project.Core.UI.Models.ContentTypeDTO>}
+       type    {Name = "DbQuery`1" FullName = "System.Data.Entity.Infrastructure.DbQuery`1[[Project.Core.UI.Models.ContentTypeDTO, Project.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]"} System.Type {System.RuntimeType}
+       resultType  {Name = "OkNegotiatedContentResult`1" FullName = "System.Web.Http.Results.OkNegotiatedContentResult`1[[System.Data.Entity.Infrastructure.DbQuery`1[[Project.Core.UI.Models.ContentTypeDTO, Project.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"}    System.Type {System.RuntimeType}
+       result  {System.Web.Http.Results.OkNegotiatedContentResult<System.Data.Entity.Infrastructure.DbQuery<Project.Core.UI.Models.ContentTypeDTO>>}   System.Web.Http.IHttpActionResult {System.Web.Http.Results.OkNegotiatedContentResult<System.Data.Entity.Infrastructure.DbQuery<Project.Core.UI.Models.ContentTypeDTO>>}

Call stack:

>   Project.AdminWebsite.dll!Project.AdminWebsite.Api.Controllers.ODataBaseController.Ok(object content, System.Type type) Line 27  C#
    Project.AdminWebsite.dll!Project.AdminWebsite.Api.Controllers.ContentTypeController.Get(System.Web.Http.OData.Query.ODataQueryOptions<Project.DAL.Data.Models.ContentType> odataQueryOptions) Line 132  C#
    [Lightweight Function]  
    System.Web.Http.dll!System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.GetExecutor.AnonymousMethod__9(object instance, object[] methodParameters) Unknown
    System.Web.Http.dll!System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(object instance, object[] arguments)   Unknown
    System.Web.Http.dll!System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(System.Web.Http.Controllers.HttpControllerContext controllerContext, System.Collections.Generic.IDictionary<string,object> arguments, System.Threading.CancellationToken cancellationToken)  Unknown
    System.Web.Http.dll!System.Web.Http.Tracing.Tracers.HttpActionDescriptorTracer.ExecuteAsync.AnonymousMethod__1()    Unknown
    System.Web.Http.dll!System.Web.Http.Tracing.ITraceWriterExtensions.TraceBeginEndAsync<object>(System.Web.Http.Tracing.ITraceWriter traceWriter, System.Net.Http.HttpRequestMessage request, string category, System.Web.Http.Tracing.TraceLevel level, string operatorName, string operationName, System.Action<System.Web.Http.Tracing.TraceRecord> beginTrace, System.Func<System.Threading.Tasks.Task<object>> execute, System.Action<System.Web.Http.Tracing.TraceRecord,object> endTrace, System.Action<System.Web.Http.Tracing.TraceRecord> errorTrace)   Unknown
    System.Web.Http.dll!System.Web.Http.Tracing.Tracers.HttpActionDescriptorTracer.ExecuteAsync(System.Web.Http.Controllers.HttpControllerContext controllerContext, System.Collections.Generic.IDictionary<string,object> arguments, System.Threading.CancellationToken cancellationToken) Unknown
    System.Web.Http.dll!System.Web.Http.Controllers.ApiControllerActionInvoker.InvokeActionAsyncCore()  Unknown
    mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start<System.Web.Http.Controllers.ApiControllerActionInvoker.InvokeActionAsyncCore>(ref System.Web.Http.Controllers.ApiControllerActionInvoker.InvokeActionAsyncCore stateMachine)  Unknown
    mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.__Canon>.Start<System.Web.Http.Controllers.ApiControllerActionInvoker.InvokeActionAsyncCore>(ref System.Web.Http.Controllers.ApiControllerActionInvoker.InvokeActionAsyncCore stateMachine)  Unknown
    System.Web.Http.dll!System.Web.Http.Controllers.ApiControllerActionInvoker.InvokeActionAsyncCore(System.Web.Http.Controllers.HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken) Unknown
    System.Web.Http.dll!System.Web.Http.Controllers.ApiControllerActionInvoker.InvokeActionAsync(System.Web.Http.Controllers.HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken) Unknown
    System.Web.Http.dll!System.Web.Http.Tracing.Tracers.HttpActionInvokerTracer.System.Web.Http.Controllers.IHttpActionInvoker.InvokeActionAsync.AnonymousMethod__1()   Unknown
    System.Web.Http.dll!System.Web.Http.Tracing.ITraceWriterExtensions.TraceBeginEndAsync<System.Net.Http.HttpResponseMessage>(System.Web.Http.Tracing.ITraceWriter traceWriter, System.Net.Http.HttpRequestMessage request, string category, System.Web.Http.Tracing.TraceLevel level, string operatorName, string operationName, System.Action<System.Web.Http.Tracing.TraceRecord> beginTrace, System.Func<System.Threading.Tasks.Task<System.Net.Http.HttpResponseMessage>> execute, System.Action<System.Web.Http.Tracing.TraceRecord,System.Net.Http.HttpResponseMessage> endTrace, System.Action<System.Web.Http.Tracing.TraceRecord> errorTrace)    Unknown
    System.Web.Http.dll!System.Web.Http.Tracing.Tracers.HttpActionInvokerTracer.System.Web.Http.Controllers.IHttpActionInvoker.InvokeActionAsync(System.Web.Http.Controllers.HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken) Unknown
    System.Web.Http.dll!System.Web.Http.Controllers.ActionFilterResult.ActionInvoker.InvokeActionAsync()    Unknown
    System.Web.Http.dll!System.Web.Http.Controllers.ActionFilterResult.ExecuteAsync()   Unknown
    mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start<System.Web.Http.Controllers.ActionFilterResult.ExecuteAsync>(ref System.Web.Http.Controllers.ActionFilterResult.ExecuteAsync stateMachine)    Unknown
    mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.__Canon>.Start<System.Web.Http.Controllers.ActionFilterResult.ExecuteAsync>(ref System.Web.Http.Controllers.ActionFilterResult.ExecuteAsync stateMachine)    Unknown
    System.Web.Http.dll!System.Web.Http.Controllers.ActionFilterResult.ExecuteAsync(System.Threading.CancellationToken cancellationToken)   Unknown
    System.Web.Http.dll!System.Web.Http.ApiController.ExecuteAsync(System.Web.Http.Controllers.HttpControllerContext controllerContext, System.Threading.CancellationToken cancellationToken)   Unknown
    System.Web.Http.dll!System.Web.Http.Tracing.Tracers.HttpControllerTracer.ExecuteAsyncCore() Unknown
    mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start<System.Web.Http.Tracing.Tracers.HttpControllerTracer.ExecuteAsyncCore>(ref System.Web.Http.Tracing.Tracers.HttpControllerTracer.ExecuteAsyncCore stateMachine)    Unknown
    mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.__Canon>.Start<System.Web.Http.Tracing.Tracers.HttpControllerTracer.ExecuteAsyncCore>(ref System.Web.Http.Tracing.Tracers.HttpControllerTracer.ExecuteAsyncCore stateMachine)    Unknown
    System.Web.Http.dll!System.Web.Http.Tracing.Tracers.HttpControllerTracer.ExecuteAsyncCore(System.Web.Http.Controllers.HttpControllerContext controllerContext, System.Threading.CancellationToken cancellationToken)    Unknown
    System.Web.Http.dll!System.Web.Http.Tracing.Tracers.HttpControllerTracer.System.Web.Http.Controllers.IHttpController.ExecuteAsync.AnonymousMethod__0()  Unknown
    System.Web.Http.dll!System.Web.Http.Tracing.ITraceWriterExtensions.TraceBeginEndAsync<System.Net.Http.HttpResponseMessage>(System.Web.Http.Tracing.ITraceWriter traceWriter, System.Net.Http.HttpRequestMessage request, string category, System.Web.Http.Tracing.TraceLevel level, string operatorName, string operationName, System.Action<System.Web.Http.Tracing.TraceRecord> beginTrace, System.Func<System.Threading.Tasks.Task<System.Net.Http.HttpResponseMessage>> execute, System.Action<System.Web.Http.Tracing.TraceRecord,System.Net.Http.HttpResponseMessage> endTrace, System.Action<System.Web.Http.Tracing.TraceRecord> errorTrace)    Unknown
    System.Web.Http.dll!System.Web.Http.Tracing.Tracers.HttpControllerTracer.System.Web.Http.Controllers.IHttpController.ExecuteAsync(System.Web.Http.Controllers.HttpControllerContext controllerContext, System.Threading.CancellationToken cancellationToken)    Unknown
    System.Web.Http.dll!System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsyncCore(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) Unknown
    System.Web.Http.dll!System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsync() Unknown
    mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start<System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsync>(ref System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsync stateMachine)    Unknown
    mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.__Canon>.Start<System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsync>(ref System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsync stateMachine)    Unknown
    System.Web.Http.dll!System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsync(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) Unknown
    System.Net.Http.dll!System.Net.Http.HttpMessageInvoker.SendAsync(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)  Unknown
    System.Web.Http.dll!System.Web.Http.Dispatcher.HttpRoutingDispatcher.SendAsync(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)    Unknown
    System.Net.Http.dll!System.Net.Http.DelegatingHandler.SendAsync(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)   Unknown
    System.Web.Http.dll!System.Web.Http.Tracing.Tracers.RequestMessageHandlerTracer.AnonymousMethod__FabricatedMethod6()    Unknown
    System.Web.Http.dll!System.Web.Http.Tracing.Tracers.RequestMessageHandlerTracer.SendAsync.AnonymousMethod__1()  Unknown
    System.Web.Http.dll!System.Web.Http.Tracing.ITraceWriterExtensions.TraceBeginEndAsync<System.Net.Http.HttpResponseMessage>(System.Web.Http.Tracing.ITraceWriter traceWriter, System.Net.Http.HttpRequestMessage request, string category, System.Web.Http.Tracing.TraceLevel level, string operatorName, string operationName, System.Action<System.Web.Http.Tracing.TraceRecord> beginTrace, System.Func<System.Threading.Tasks.Task<System.Net.Http.HttpResponseMessage>> execute, System.Action<System.Web.Http.Tracing.TraceRecord,System.Net.Http.HttpResponseMessage> endTrace, System.Action<System.Web.Http.Tracing.TraceRecord> errorTrace)    Unknown
    System.Web.Http.dll!System.Web.Http.Tracing.Tracers.RequestMessageHandlerTracer.SendAsync(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) Unknown
    System.Net.Http.dll!System.Net.Http.DelegatingHandler.SendAsync(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)   Unknown
    System.Web.Http.dll!System.Web.Http.HttpServer.AnonymousMethod__FabricatedMethod9() Unknown
    System.Web.Http.dll!System.Web.Http.HttpServer.SendAsync()  Unknown
    mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start<System.Web.Http.HttpServer.SendAsync>(ref System.Web.Http.HttpServer.SendAsync stateMachine)  Unknown
    mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.__Canon>.Start<System.Web.Http.HttpServer.SendAsync>(ref System.Web.Http.HttpServer.SendAsync stateMachine)  Unknown
    System.Web.Http.dll!System.Web.Http.HttpServer.SendAsync(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)  Unknown
    System.Net.Http.dll!System.Net.Http.HttpMessageInvoker.SendAsync(System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)  Unknown
    System.Web.Http.WebHost.dll!System.Web.Http.WebHost.HttpControllerHandler.ProcessRequestAsyncCore() Unknown
    mscorlib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start<System.Web.Http.WebHost.HttpControllerHandler.ProcessRequestAsyncCore>(ref System.Web.Http.WebHost.HttpControllerHandler.ProcessRequestAsyncCore stateMachine)    Unknown
    mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start<System.Web.Http.WebHost.HttpControllerHandler.ProcessRequestAsyncCore>(ref System.Web.Http.WebHost.HttpControllerHandler.ProcessRequestAsyncCore stateMachine)    Unknown
    System.Web.Http.WebHost.dll!System.Web.Http.WebHost.HttpControllerHandler.ProcessRequestAsyncCore(System.Web.HttpContextBase contextBase)   Unknown
    System.Web.Http.WebHost.dll!System.Web.Http.WebHost.HttpControllerHandler.ProcessRequestAsync(System.Web.HttpContext context)   Unknown
    System.Web.dll!System.Web.HttpTaskAsyncHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest.AnonymousMethod__0()    Unknown
    System.Web.dll!System.Web.TaskAsyncHelper.BeginTask(System.Func<System.Threading.Tasks.Task> taskFunc, System.AsyncCallback callback, object state) Unknown
    System.Web.dll!System.Web.HttpTaskAsyncHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(System.Web.HttpContext context, System.AsyncCallback cb, object extraData)  Unknown
    System.Web.dll!System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()  Unknown
    System.Web.dll!System.Web.HttpApplication.ExecuteStep(System.Web.HttpApplication.IExecutionStep step, ref bool completedSynchronously)  Unknown
    System.Web.dll!System.Web.HttpApplication.PipelineStepManager.ResumeSteps(System.Exception error)   Unknown
    System.Web.dll!System.Web.HttpApplication.BeginProcessRequestNotification(System.Web.HttpContext context, System.AsyncCallback cb)  Unknown
    System.Web.dll!System.Web.HttpRuntime.ProcessRequestNotificationPrivate(System.Web.Hosting.IIS7WorkerRequest wr, System.Web.HttpContext context)    Unknown
    System.Web.dll!System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(System.IntPtr rootedObjectsPointer, System.IntPtr nativeRequestContext, System.IntPtr moduleData, int flags) Unknown
    System.Web.dll!System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(System.IntPtr rootedObjectsPointer, System.IntPtr nativeRequestContext, System.IntPtr moduleData, int flags)   Unknown
    [Native to Managed Transition]  
    [Managed to Native Transition]  
    System.Web.dll!System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(System.IntPtr rootedObjectsPointer, System.IntPtr nativeRequestContext, System.IntPtr moduleData, int flags) Unknown
    System.Web.dll!System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(System.IntPtr rootedObjectsPointer, System.IntPtr nativeRequestContext, System.IntPtr moduleData, int flags)   Unknown
    [AppDomain Transition]  

Error Message:

Unable to cast the type 'System.Web.Http.OData.Query.Expressions.SelectExpandBinder+SelectAll`1[[Project.DAL.Data.Models.ContentType, Project.DAL.Data, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' to type 'Project.DAL.Data.Models.ContentType'. LINQ to Entities only supports casting EDM primitive or enumeration types.
like image 536
ElHaix Avatar asked Feb 10 '14 22:02

ElHaix


People also ask

What is $expand in OData?

OData query option $expand is used to read multiple entities or entity sets in a single service call instead of two different calls. Prerequisite, entity sets which are used should be associated. To know about Association in OData service click here.

What is $value in OData?

The $value option is used to get individual properties of an Entity. There are two ways to get individual properties from an entity. We can get the response in either OData format or get the raw value of the property. We need to add method to the controller named GetProperty here property is a name of the property.

How to implement support for OData query options in web API?

Now to implement support for OData Query Options we have to do a few things listed down here. Install package 'Microsoft.AspNet.WebApi.OData.5.7.0' Enable support for OData query options in Web API Configuration class.

How to override query options in odataqueryoptions?

Instantiating ODataQueryOptions (generic or non-generic) requires an ODataQueryContext and an HttpRequestMessage. Both of these are available as properties on the original ODataQueryOptions. So in order to override our query options we only have to split the incoming RequestUri and create new options from it.

How do I override the default OData query expansion depth?

To override the default, set the MaxExpansionDepth property on the [Queryable] attribute. For more information about the $expand option, see Expand System Query Option ($expand) in the official OData documentation. The $select option specifies a subset of properties to include in the response body.

What is the difference between $expand and $select In OData?

$expand causes related entities to be included inline in the response. $select selects a subset of properties to include in the response. $value gets the raw value of a property. For this article, I'll use an OData service that defines three entities: Product, Supplier, and Category. Each product has one category and one supplier.


1 Answers

From $select and $expand break ODataQueryOptions -- how to fix?

public IHttpActionResult Get(ODataQueryOptions<YourEntity> odataQueryOptions)
{
    //Your applyTo logic and results.

    if (odataQueryOptions.SelectExpand != null)
    {
        Request.SetSelectExpandClause(odataQueryOptions.SelectExpand.SelectExpandClause);
    }

    return Ok(results, results.GetType());
}

private IHttpActionResult Ok(object content, Type type)
{
    Type resultType = typeof(OkNegotiatedContentResult<>).MakeGenericType(type);
    return Activator.CreateInstance(resultType, content, this) as IHttpActionResult;
}

But as I stated in your other question, keep looking for ways to avoid manually applying the results if you can.

like image 67
Schandlich Avatar answered Nov 15 '22 00:11

Schandlich