Веб-API Queryable – как применять AutoMapper?

У меня есть простой метод WebApi, подобный этому, с атрибутом запроса OData.

[Queryable] public virtual IQueryable Get() { return uow.Person().GetAll()); // Currently returns Person instead of PersonD } 

То, что я хочу сделать, – это преобразовать результат запроса из типа Person для ввода PersonDto с использованием AutoMapper до того, как WebAPI преобразует результат в JSON.

Кто-нибудь знает, как я могу это сделать? Я знаю, что я могу применить Mapper.Map после вызова GetAll (), а затем преобразовать обратно в IQueryable, однако это приведет к тому, что вся таблица будет возвращена и отображена до того, как будет применен фильтр OData (не хорошо!).

Похоже, что этот вопрос ASP.NET Web API возвращает запрашиваемые DTO? охватывает ту же проблему (см. второй ответ для лучшего ответа), где предлагается использовать AutoMapper в конце цепочки с использованием настраиваемого MediaTypeFormatter, однако я не знаю, как это сделать на примере, который я видел.

Любая помощь будет с благодарностью получена!

– Дополнительная информация

Я посмотрел исходный код для IQueryable, но, к сожалению, я не вижу способа использовать этот код для этой цели. Мне удалось написать дополнительный фильтр, который, похоже, работает, однако он, конечно, не является изящным.

 public class PersonToPersonDtoConvertAttribute : ActionFilterAttribute { public override void OnActionExecuted(System.Web.Http.Filters.HttpActionExecutedContext actionExecutedContext) { HttpResponseMessage response = actionExecutedContext.Response; if (response != null) { ObjectContent responseContent = response.Content as ObjectContent; var query = (responseContent.Value as IQueryable).ToList(); response.Content = new ObjectContent<IEnumerable>(query.ToList().Select(Mapper.Map), responseContent.Formatter); } } } 

Затем я украсил действие, как

  [Queryable] [PersonToPersonDtoConvert] public IQueryable Get() { return uow.GetRepo<IRepository>().GetAll(); } 

Существует лучшее решение. Попробуй это:

 public virtual IQueryable Get(ODataQueryOptions query) { var people = query.ApplyTo(uow.Person().GetAll()); return ConvertToDtos(people); } 

Это позволит убедиться, что запрос выполняется на Person вместо PersonDTO. Если вы хотите, чтобы преобразование происходило через атрибут, а не в коде, вы все равно захотите реализовать фильтр действий, подобный тому, что вы положили.

Используйте Queryable Extensions для AutoMapper.

Сначала определим отображение.

 Mapper.CreateMap(); 

Затем вы можете использовать что-то вроде этого:

 [EnableQuery] public IQueryable Get() { return this.dbContext.Persons.Project().To(); } 

ИМХО принятое решение неверно. Вообще говоря, если ваша служба использует DTO, вы не хотите раскрывать базовые объекты (Person) для этой службы. Почему вы запрашиваете модель Person и возвращаете объекты PersonDTO ?

Поскольку вы уже используете его, Automapper имеет Queryable Extensions, который позволяет выставлять только ваши DTO и применять фильтрацию к базовому типу в источнике данных. Например:

 public IQueryable Get(ODataQueryOptions options) { Mapper.CreateMap(); var persons = _personRepository.GetPersonsAsQueryable(); var personsDTOs = persons.Project().To(); // magic happens here... return options.ApplyTo(personsDTOs); } 

Что касается активной загрузки навигационных свойств …

@philreed: Я не смог добавить достойный ответ в комментарии, поэтому добавил его здесь. Было сообщение о том, как это сделать здесь, но сегодня я получаю 403s. Надеюсь, это временно.

В основном вы изучаете предложения Select и Expand для вашего свойства навигации. Если он присутствует, вы IQueryable Include EF с нетерпением загрузки через метод расширения IQueryable Include .

controller

 public IQueryable GetMyDtos(ODataQueryOptions options) { var eagerlyLoad = options.IsNavigationPropertyExpected(t => t.MyNavProperty); var queryable = _myDtoService.GetMyDtos(eagerlyLoad); // _myDtoService will eagerly load to prevent select N+1 problems // return (eagerlyLoad) ? efResults.Include(t => t.MyNavProperty) : efResults; return queryable; } 

Метод расширения

 public static class ODataQueryOptionsExtensions { public static bool IsNavigationPropertyExpected(this ODataQueryOptions source, Expression> keySelector) { if (source == null) { throw new ArgumentNullException("source"); } if (keySelector == null) { throw new ArgumentNullException("keySelector"); } var returnValue = false; var propertyName = (keySelector.Body as MemberExpression ?? ((UnaryExpression)keySelector.Body).Operand as MemberExpression).Member.Name; var expandProperties = source.SelectExpand == null || string.IsNullOrWhiteSpace(source.SelectExpand.RawExpand) ? new List().ToArray() : source.SelectExpand.RawExpand.Split(','); var selectProperties = source.SelectExpand == null || string.IsNullOrWhiteSpace(source.SelectExpand.RawSelect) ? new List().ToArray() : source.SelectExpand.RawSelect.Split(','); returnValue = returnValue ^ expandProperties.Contains(propertyName); returnValue = returnValue ^ selectProperties.Contains(propertyName); return returnValue; } } 

У меня есть несколько замечаний / комментариев по предлагаемому решению. 1. Если Person используется в контексте запроса и возвращается PersonDTO, вся концепция MVVM исчезает. Наверное, он крепко связан. Я бы предпочел просто использовать Person полностью или если вы не хотите возвращать дочерние записи, то, по моему мнению, Automapper имеет расширение «Project», попробуйте его использовать. 2. При таком подходе опция запроса $ inlinepagecount не будет функционировать должным образом, потому что она получит счет PersonDTO вместо Person. (Ну, одним из способов обойти эту проблему было бы использование PageResult и настройка свойств самостоятельно.) С уважением.

  • Неавторизованный webapi вызов, возвращающий страницу входа, а не 401
  • Загрузка Webapi formdata (в DB) с дополнительными параметрами
  • Маршрутизация с помощью нескольких методов Get в ASP.NET Web API
  • Где HttpContent.ReadAsAsync?
  • Как метод в MVC WebApi сопоставляется с http-глаголом?
  • Как вернуть необработанную строку с ApiController?
  • Обработка ошибок (отправка клиенту ex.Message)
  • Разница между ApiController и controllerом в ASP.NET MVC
  • Как вернуть файл (FileContentResult) в ASP.NET WebAPI
  • Поддержка ASP.Net WebAPI
  • Тип провайдера CodeDom «Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider» не может быть расположен
  • Давайте будем гением компьютера.