【问题标题】:$select and $expand break ODataQueryOptions -- how to fix?$select 和 $expand 中断 ODataQueryOptions - 如何修复?
【发布时间】:2013-09-23 15:48:01
【问题描述】:

我们将 Microsoft ASP.NET MVC OData WebAPI 用于我们的 Web 服务。由于围绕层次 ID 的一些数据架构问题(不在本次讨论的范围内),我们的一些 GET 操作必须使用 ODataQueryOptions 并手动操作表达式以添加额外的限制。我们这样做(为了清楚起见,删除了错误处理代码并内联了对其他方法的调用):

public IQueryable<Person> Get(ODataQueryOptions<Person> oDataQueryOptions)
{
    IQueryable<Person> result;
    IQueryable<Person> dataSet = context.Persons;

    var tempQuery = oDataQueryOptions.ApplyTo(dataSet).Cast<Person>();
    var modifier = new HierarchyNodeExpressionVisitor(GetDescendantsOfNode, GetAncestorsOfNode);
    var expression = modifier.ModifyHierarchyNodeExpression(tempQuery.Expression);

    result = context.Persons.Provider.CreateQuery<Person>(expression);

    return result;
}

这已经有一段时间了,但我们一直在热切地等待选择和扩展,以便我们可以更好地控制从我们的服务接收的数据。周一,我们将开发环境更新为 WebApi OData 5.0.0-rc1 并让选择和扩展工作正常,但我们无法针对使用 ODataQueryOptions 的这些服务使用它。我们只能将它用于我们的其他服务。如果我们使用$select 和/或$expand 查询上面的代码,我们会得到以下错误:

"message": "The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'.",
"type": "System.InvalidOperationException",
"stacktrace": "",
"internalexception":
{
    "message": "Unable to cast the type 'System.Web.Http.OData.Query.Expressions.SelectAllAndExpand`1' to type 'OurCompany.Domains.Data.Models.Person'. LINQ to Entities only supports casting EDM primitive or enumeration types.",
    "type": "System.NotSupportedException",
    "stacktrace": " at System.Data.Objects.ELinq.ExpressionConverter.ValidateAndAdjustCastTypes(TypeUsage toType, TypeUsage fromType, Type toClrType, Type fromClrType) at System.Data.Objects.ELinq.ExpressionConverter.GetCastTargetType(TypeUsage fromType, Type toClrType, Type fromClrType, Boolean preserveCastForDateTime) at System.Data.Objects.ELinq.ExpressionConverter.CreateCastExpression(DbExpression source, Type toClrType, Type fromClrType) at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.CastMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call) at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq) at System.Data.Objects.ELinq.ExpressionConverter.Convert() at System.Data.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption) at System.Data.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption) at System.Data.Objects.ObjectQuery`1.System.Collections.Generic.IEnumerable.GetEnumerator() at System.Data.Entity.Infrastructure.DbQuery`1.System.Collections.IEnumerable.GetEnumerator() at System.Web.Http.OData.Formatter.Serialization.ODataFeedSerializer.WriteFeed(IEnumerable enumerable, IEdmTypeReference feedType, ODataWriter writer, ODataSerializerContext writeContext) at System.Web.Http.OData.Formatter.ODataMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content, HttpContentHeaders contentHeaders) at System.Web.Http.OData.Formatter.ODataMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.WebHost.HttpControllerHandler.d__10.MoveNext()"
}

我做了一些谷歌搜索,偶然发现了 thisthis,但都没有帮助。似乎没有人在做我们正在做的事情并尝试使用选择和扩展。我们如何解决这个问题?我在这里不知所措......

【问题讨论】:

    标签: c# select asp.net-web-api odata expand


    【解决方案1】:

    问题出在这行代码,

    var tempQuery = oDataQueryOptions.ApplyTo(dataSet).Cast<Person>();
    

    一旦应用 $select$expand,Cast 就无效了,结果不再是 Person。这将是一个Wrapper&lt;Person&gt;,仅包含客户要求的属性。您可能需要修改您的 HierarchyNodeExpressionVisitor 以考虑到这一点。

    另外,请尝试将您的操作更改为此以处理结果可能不再是 IQueryable&lt;Person&gt; 的事实。

        public IHttpActionResult Get(ODataQueryOptions<Person> oDataQueryOptions)
        {
            IQueryable result;
            IQueryable<Person> dataSet = context.Persons;
    
            IQueryable tempQuery = oDataQueryOptions.ApplyTo(dataSet);
            var modifier = new HierarchyNodeExpressionVisitor(GetDescendantsOfNode, GetAncestorsOfNode);
            var expression = modifier.ModifyHierarchyNodeExpression(tempQuery.Expression);
    
            result = context.Persons.Provider.CreateQuery(expression);
    
            return Ok(result, result.GetType());
        }
    
        private IHttpActionResult Ok(object content, Type type)
        {
            Type resultType = typeof(OkNegotiatedContentResult<>).MakeGenericType(type);
            return Activator.CreateInstance(resultType, content, this) as IHttpActionResult;
        }
    

    【讨论】:

    • 实际情况并非如此。当我使用调试器单步执行时,结果是Person,而不是Wrapper&lt;Person&gt;。事实上,我的方法中的任何地方都没有抛出异常。方法返回后抛出异常。
    • LINQ 查询几乎总是被延迟评估。因此,当有人尝试枚举结果而不是在应用查询时会引发异常。这意味着当格式化程序将响应写入流时,将在格式化程序中引发异常。异常消息非常清楚正在发生的事情“”无法将类型“System.Web.Http.OData.Query.Expressions.SelectAllAndExpand`1”转换为类型“OurCompany.Domains.Data.Models.Person”。 LINQ to Entities 仅支持转换 EDM 基元或枚举类型。”
    • HierarchyNodeExpressionVisitor 在哪里?无处可寻。
    • 我和议会有同样的问题... HierarchyNodeExpressionVisitor 在哪里?
    • @parliament 你是怎么解决这个问题的,还是像我国的议会一样悬而未决?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-06-04
    • 2015-07-30
    • 2013-09-05
    • 2015-04-04
    • 1970-01-01
    • 1970-01-01
    • 2013-12-23
    相关资源
    最近更新 更多