$select и $expand break ODataQueryOptions-как исправить?
Мы используем Microsoft ASP.NET MVC OData WebAPI для наших веб-сервисов. Из-за некоторых проблем архитектуры данных, связанных с идентификатором иерархии (которые выходят за рамки этого разговора), некоторые из наших операций 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 и получили работу select-and-expand, но мы не можем использовать ее против этих служб, которые используют 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()"
}
Я немного погуглил и наткнулся на это и это , но ни то, ни другое не помогло. Похоже, никто не делает того, что делаем мы, и не пытается использовать select-and-expand. Как нам это исправить? Я здесь в растерянности...
1 ответ:
Проблема в этой строке кода,
var tempQuery = oDataQueryOptions.ApplyTo(dataSet).Cast<Person>();
Приведение недействительно, так как после применения
Кроме того, попробуйте изменить свое действие на это, чтобы справиться с тем фактом, что результат может больше не быть$select
и$expand
результат больше не является человеком. Это будетWrapper<Person>
, который содержит только те свойства, которые запросил клиент. Возможно, вам придется изменить свойHierarchyNodeExpressionVisitor
, чтобы принять это во внимание.IQueryable<Person>
.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; }