2015-08-13 3 views
0

Как включить веб-api для предоставления данных, которые можно запросить с помощью оператора $ select OData?

Я использую Web API 2.2 (или Microsort.AspNet.WebApi 5.2.2) Я не использую EF, а мой backend асинхронен и не поддерживает IQueryable. Я не возражаю прокомментировать весь набор данных и фильтрацию на веб-сервере перед тем, как перейти к клиенту.

То, что у меня ниже, не является идеальным, потому что оно возвращает Task<IQuerable...>, однако я не знаю, как это сделать по-другому, и на самом деле не знаю, как это сделать вообще.

Следующий код работает, однако выдает ошибку при использовании $ выбора (назовем его кодовый блок А):

[EnableQuery(HandleNullPropagation = HandleNullPropagationOption.True)] 
public async Task<IQueryable<Cars>> GetCars(ODataQueryOptions<Cars> queryOptions) 
{ 
    // validate the query. 
    try 
    { 
     queryOptions.Validate(_validationSettings); 
    } 
    catch (ODataException ex) 
    { 
     throw new HttpRequestException(ex.Message); 
    } 

    var result = await _context.GetCarsAsync(); 
    var queryableResult = queryOptions.ApplyTo(result.AsQueryable()) as IQueryable<Cars>; 

    return queryableResult; 
} 

Следующий код не работает и возвращает 406 (назовем его блок кода B):

[EnableQuery(HandleNullPropagation = HandleNullPropagationOption.True)] 
public async Task<IQueryable> GetCars(ODataQueryOptions<Cars> queryOptions) 
{ 
    // validate the query. 
    try 
    { 
     queryOptions.Validate(_validationSettings); 
    } 
    catch (ODataException ex) 
    { 
     throw new HttpRequestException(ex.Message); 
    } 

    var result = await _context.GetCarsAsync(); 
    var queryableResult = queryOptions.ApplyTo(result.AsQueryable()); 

    return queryableResult; 
} 

Я думаю, последний код не работает, потому что он не возвращает сильно типизированный объект и какой-то образом сериализация двигатель не может справиться с этим. Я пытался это сделать, и я также попытался заменить Cars в блоке A кода dynamic, чтобы включить $ select, но не работает.

Так два вопроса:

  1. Есть ли способ, чтобы позволить $ выбрать, учитывая мой бэкенд не поддерживает IQueryable? (Не врываясь в сам интерфейс IQueryable)

  2. Что такое «правильный» способ сделать это, не возвращая Task<IQuerable...>?

UPDATE - @Marvin Smit (Ошибка - код Блок А при использовании $ выбора)

Полная ошибка при попытке спроецировать код блока А с помощью $ выберите ниже. Пример /Cars?$select=NumberPlate В основном это ошибка сериализации (что-то связано с переносом IQueryable в задачу?). Помещение точки останова показывает, что данные были успешно извлечены и спроектированы после queryOptions.ApplyTo(..., и ошибка возникает только после возвращения.

<m:error xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"> 
    <m:code/> 
    <m:message xml:lang="en-US">An error has occurred.</m:message> 
    <m:innererror> 
    <m:message> 
     The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'. 
    </m:message> 
    <m:type>System.InvalidOperationException</m:type> 
    <m:stacktrace/> 
    <m:internalexception> 
     <m:message>Cannot serialize a null 'feed'.</m:message> 
     <m:type> 
     System.Runtime.Serialization.SerializationException 
     </m:type> 
     <m:stacktrace> 
     at System.Web.Http.OData.Formatter.Serialization.ODataFeedSerializer.WriteObjectInline(Object graph, IEdmTypeReference expectedType, ODataWriter writer, ODataSerializerContext writeContext) 
     at System.Web.Http.OData.Formatter.Serialization.ODataFeedSerializer.WriteObject(Object graph, Type type, ODataMessageWriter messageWriter, 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, CancellationToken cancellationToken) 
     --- 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.Runtime.CompilerServices.TaskAwaiter.GetResult() 
     at System.Web.Http.WebHost.HttpControllerHandler. 
     <WriteBufferedResponseContentAsync>d__1b.MoveNext() 
     </m:stacktrace> 
    </m:internalexception> 
    </m:innererror> 
</m:error> 
+1

Что ошибка для блока кода А? для кода B (http 406) можно быстро просмотреть http://stackoverflow.com/questions/14251851/what-is-406-not-acceptable-response-in-http –

+0

@MarvinSmit, см. обновленный вопрос с информацией, приведенной выше , Спасибо за информацию о 406, я добавил приложение/json к этому, и похоже, что теперь это ошибка сериализации, аналогичная приведенной выше для блока кода A. Забавно это не происходит при возврате IQueryable - только когда вы завершаете это в задаче. Однако, как мой back-end асинхронный, какой у меня выбор? Решением может быть завершение реализации IQueryable (a la 'http://blogs.msdn.com/b/alexj/archive/2010/03/01/tip-55-how-to-extend-an-iqueryable-by -wrapping-it.aspx) с асинхронным провайдером. Слишком много работы! – SDK

+0

Хотя я не уверен, почему в вашем случае это «типичная ошибка» для OData с webapi. (попробуйте сделать это на любой конечной точке OData, используйте $ select of property и убедитесь, что вы запрашиваете XML в качестве вывода). Это просто не нравится сериализация «слабо типизированных» объектов. Следовательно, JSON. Но вы просите JSON. (http://stackoverflow.com/questions/27925062/web-api-with-odata-v4-throwing-exception-on-select) –

ответ

2

Проблема не связана с задачами.

После некоторых исследований проблема эта строка кода в блоке А:

queryOptions.ApplyTo(result.AsQueryable()) as IQueryable<Cars> 

queryOptions.ApplyTo возвращает IQueryable, который не имеет тип IQueryable<Cars> и поэтому вы не можете просто бросить его. Результат равен нулю, и вы получите эту ошибку. Кажется, что нет метода queryOptions.ApplyTo<T>, возвращающего IQueryable<T>. Проверьте this и связанный веб-сайт с некоторыми пояснениями по ним.

На самом деле вам не нужно применять параметры запроса, чтобы заставить фильтры odata работать. Просто использование этого кода будет работать.

[EnableQuery(HandleNullPropagation = HandleNullPropagationOption.True)] 
public async Task<IQueryable<Cars>> GetCars(ODataQueryOptions<Cars> queryOptions) 
{ 
    // validate the query. 
    try 
    { 
     queryOptions.Validate(_validationSettings); 
    } 
    catch (ODataException ex) 
    { 
     throw new HttpRequestException(ex.Message); 
    } 

    var result = await _context.GetCarsAsync(); 
    return result.AsQueryable(); 
} 

Фильтры автоматически применяются при реализации asp.net odata после возвращения метода.

Приложение применяется, если вы хотите применить параметры запроса в своем коде, а затем вернуть что-то иное, чем IQueryable (example here in scenario 9).

Что касается ошибки сериализации в блоке B (после добавления приложения/json), я не знаю.

+0

Так же просто, как удаление некоторых лишних строк кода. Спасибо @Fabian. Я начал все это, пытаясь вернуть 'async Task ', но по какой-то причине он не сериализуется. Думая об этом, это может быть из-за заголовка Accept. Хорошо, ваше решение ослепительно очевидно, и победитель работает. – SDK

0

Я обнаружил этот подход:

[EnableQuery] 
public async Task<IQueryable<Cars>> GetCars() 
{ 
    // return IQueryable<Cars> from context, do not perform request to database 
    return await Task.FromResult(_context.GetCarsQuery()); 
}