2015-12-23 2 views
0

Я просмотрел все сообщения об этом и не имел успеха. Я могу позвонить в контроллер OData и посмотреть данные, полученные, отображенные и упакованные. Метод контроллера выходит, и я получаю 406 - Not Acceptable. Я только с помощью System.Web.OData имен (OData Version v4.0.30319) и, используя Postman, я вручную установить Content и Accept заголовки «приложения/JSON» не повезлоWeb API OData Controller возвращает «406 - недопустимо» при сопоставлении результатов

Может быть, я пропустил что-то за последние 2 часа, читая сообщения всех по аналогичным проблемам. Любые указатели будут оценены.

UPDATE: Проблема, как представляется, в Mapper кода (Automapper), как указывалось на Игорь ниже. Похоже, есть обязательство вернуть объект базы данных (EF), а не сопоставленный класс. С этими знаниями я нашел этот пост SO, но он не предлагает решения: ApiController vs ODataController when exposing DTOs. Должны ли мы заставлять возвращать объекты базы данных или отображать результаты? Если это так, для меня это прерыв.

Контроллер:

[EnableQuery] 
[HttpGet] 
public async Task<IHttpActionResult> Get() 
    { 
     var list = await db.ConfigSets.ToListAsync(); 
     Mapper.CreateMap<ConfigSet, ConfigSetDTO>(); 
     var configSetDTOs = Mapper.Map<List<ConfigSet>, List<ConfigSetDTO>>(list); 
     return Ok(configSetDTOs); //IT LOOKS GOOD HERE! 
    } 

WebApiConfig:

public static class WebApiConfig 
    { 
     public static void Register(HttpConfiguration config) 
     { 
      config.MapHttpAttributeRoutes(); 
      config.EnableCors(); 

      // OData - must be before config.Routes when using a prefix. In this case "api" 
      config.MapODataServiceRoute("odata", "api", GetEdmModel(), new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer)); 
      config.EnsureInitialized(); 

      config.Routes.MapHttpRoute(
       name: "DefaultApi", 
       routeTemplate: "api/{controller}/{action}/{id}", 
       defaults: new { id = RouteParameter.Optional } 
      );   
     } 

     private static IEdmModel GetEdmModel() 
     { 
      var builder = new ODataConventionModelBuilder(); 
      builder.Namespace = "Services"; 
      builder.ContainerName = "DefaultContainer"; 
      builder.EntitySet<ConfigSet>("Configuration"); 
      var edmModel = builder.GetEdmModel(); 
      return edmModel; 
     } 
    } 
+0

Я не уверен, что возвращает «Mapper.Map», но что бы это ни было, он должен реализовывать «IQueryable» или их общий. Пробовали ли вы тестировать его с помощью жесткого кодированного экземпляра «List », а в 'return' call do return' Ok (myList.AsQueryable()); '? Это должно помочь вам сузить область проблемы, общую конфигурацию или данные или формат данных, которые вы пытаетесь вернуть. – Igor

+0

@ Igor..Mapper - Automapper. Я пробовал этот код без него и того, что вы предложили, и получил тот же результат. –

+0

Думаю, я это вижу. 'Строитель.EntitySet («Конфигурация»); 'обозначает, что ожидаемый тип, который должен быть возвращен методом Get(), имеет тип' ConfigSet', но вы возвращаете тип 'ConfigSetDTO'. Чтобы убедиться, что это действительно проблема, попробуйте вернуть жесткий кодированный список экземпляров 'ConfigSet', но вызовите' AsQueryable() 'в методе' Ok'. Т.е.; 'return Ok (myConfigSetList.AsQueryable());' – Igor

ответ

1

Я никогда не использовал Get() функцию от OData ни с чем другим, чем данные EF, что я тяну с точки зрения SQL, так что я я не уверен, что это или невозможно. Вся цель, насколько я понимаю, состоит в том, что контроллер MS Odata Web API обрабатывает всю логику запросов на основе входящего URL-адреса, поэтому вы хотите использовать oData с Get, поскольку он предоставляет стандартный способ запроса данные, дополнительный код, необходимый для фильтрации заднего конца, и фильтрация происходит в базе данных, а не в памяти. Если вы подталкиваете что-то вроде Automapper между этим, я сомневаюсь, что он будет работать одинаково.

1

Старые версии OData позволяют вам просто открыть любой старый IQueryable, будь то объекты базы данных или DTO.

Однако я могу определенно сказать, что вы используете AutoMapper здесь неправильно - вы выполняете сопоставление внутри клиента, а не в базе данных, которое все равно позволит вам вернуть IQueryable. Вместо того чтобы использовать Mapper.Map, используйте метод <> расширения .ProjectTo по Вашему запросу:

using AutoMapper.QueryableExtensions; // This using is required 

[EnableQuery] 
[HttpGet] 
public IHttpActionResult Get() 
{ 
    var query = db.ConfigSets.ProjectTo<ConfigSetDTO>(); 
    return Ok(query); 
} 

Вы также не должны быть определение ваших карт в методах действий - они должны быть определены один раз в вашем запуске приложения.

+0

Я все еще получаю ошибку 406 с этими изменениями. Теперь отображение происходит через профиль Automapper, вызванный из Global.asax. Есть идеи? –

+0

Я думаю, что ваше предложение многообещающее и имеет смысл, но я получаю 406. –