Проекция Automapper с выбором OData, если DTO имеет производный класс, выдает ошибку

Проекция Automapper с OData работает нормально, как и ожидалось, но если я получаю класс из DTO, это вызывает следующую ошибку:

"exceptionMessage": "The 'TypeIs' expression with an input of type 'EmployeeDTO' and a check of type 'DeveloperDTO' is not supported. Only entity types and complex types are supported in LINQ to Entities queries.",
"exceptionType": "System.NotSupportedException",

Ирония заключается в том, что производный класс не используется в проекции и даже не отображается.

Чтобы лучше понять проблему, я смог упростить задачу, используя базу данных «Борей». Вот код:

public class EmployeeDTO
{
    public int EmployeeID { get; set; }
    public string LastName { get; set; }
    public string FirstName { get; set; }
}

public class DeveloperDTO : EmployeeDTO
{
}

public class EmployeesController : ApiController
{
    [HttpGet]
    [EnableQuery(AllowedQueryOptions = AllowedQueryOptions.Select)]
    public IHttpActionResult Get()
    {
        Mapper.CreateMap<Employee, EmployeeDTO>();

        var dbContext = new NORTHWNDEntities();
        var models = dbContext.Employees;

        var dtos = models.Project().To<EmployeeDTO>();
        return Ok(dtos);
    }
}

Вызов этого контроллера с помощью этого вызова OData вызывает ошибку:

http://<hostname>/api/employees/get?$select=LastName

Это происходит только при использовании функции выбора OData. Другие, которые я пробовал (skip, top, orderby), все работают без проблем.

А теперь самое интересное. Просто закомментируйте базовый класс (EmployeeDTO) определения класса DeveloperDTO, и функция выбора OData работает просто отлично. Как видите, ничто даже не использует класс DeveloperDTO. Он не нанесен на карту, абсолютно не используется во всем проекте.

Это касается Visual Studio 2013, .NET4.5, Wep API 2.2, Automapper 3.3.1 и Microsoft ASP.NET Web API 2.2 для OData 4.0 v5.4.

Воссоздайте проблему:

  1. Создайте новое приложение Web API 2.
  2. Добавьте новый элемент «Модель данных объекта ADO.NET» и выберите «Конструктор EF из базы данных». Выберите базу данных "Борей" и только выберите таблицу "Сотрудники" (Борей не поддерживает Entity Framework).
  3. С помощью Nuget добавьте «Automapper» и «Microsoft ASP.NET Web API 2.2 для OData 4.0».
  4. Добавьте новый элемент «WEb API 2 Controller — Empty» и назовите его «EmployeesController». Замените класс по умолчанию кодом, указанным выше. Не забудьте включить два класса DTO.

Запустите это приложение с указанным выше URL-адресом, и вы получите сообщение об ошибке. Закомментируйте класс DeveloperDTO, и вы не получите ошибку.

Временное исправление: если я не создам какой-либо класс из DTO и продублирую все члены во всех классах, которые были бы производными от DTO, тогда все будет работать так, как ожидалось. Однако для обслуживания я бы предпочел использовать наследование.

Я углубился в код MS Odata, но он быстро становится волосатым, очень трудно следить за потоком, и я не мог понять лямбду, которую он применял к запрашиваемому, чтобы попытаться воссоздать проблему без «магии» веб-API. и оформление OData.

Я нашел много сообщений на SO о проекции Automapper и OData в отношении составных DTO, но ни один из них не выглядит применимым к этому случаю.

Любая помощь будет оценена. Спасибо искренне!


person portigui    schedule 24.03.2015    source источник


Ответы (1)


Я часами боролся с этой проблемой и обвинял OData.

Прежде всего, я нашел решение в здесь Решение заключается в вызове метода Игнорировать в построителе моделей.

В вашем случае при настройке odata с помощью builder добавьте

builder.Ignore<DeveloperDTO>(); //or whatever you don't want to map.

ODataConventionModelBuilder просматривает все найденные модели. И каким-то образом OData пытается запросить базовый класс как производный класс, а затем бум?? (Я думаю).

Также нашел документацию построителей моделей. http://odata.github.io/WebApi/#02-04-convention-model-builder, который может быть полезен.

person alperenc    schedule 04.07.2015
comment
Отличная мысль, и я рад, что она работает для вас. Однако мне нужна карта базового класса. В описании проблемы я сказал, что она не отображается, но это было частью решения проблемы. Я думал, что если я удалю сопоставление для базового класса, это может сработать, но это оказалось не так. Я указал, что это не отображается в описании проблемы, потому что это сделало проблему еще более странной, и подумал, что это может помочь в решении. Тем не менее, спасибо, что нашли время ответить, всегда ценю. - person portigui; 07.07.2015
comment
После того, как я разместил свой комментарий, я подумал о том, что сработает. Создайте еще один класс, который также является производным от базового класса, но без дополнительных членов, который используется в качестве фактического общедоступного DTO. Тогда я мог бы игнорировать базовый класс. - person portigui; 07.07.2015