Можно ли получить сложные объекты Entity Framework из REST API в .NET без создания объектов ViewModel?

Представьте себе набор сущностей Entity Framework:

public class Country {
    public string CountryCode { get; set; }
    public string Name { get; set; }
    public string Flag { get; set; }
}

public class Market {
    public string CountryCode { get; set; }
    public virtual Country Country { get; set; }
    public int ProductID { get; set; }      
    public virtual Product Product { get; set; }
}

public class Product {
    public int ProductID { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Market> Markets{ get; set; }
}

Представьте также DOTNET 5 api GET

// GET api/product
[HttpGet]
public async Task<IActionResult> GetProduct([FromRoute] int id)
{
    return Ok(await _context.Products
        .Include(p => p.Markets)
        .SingleAsync(m => m.ProductID == id));
}

Если к объекту не привязаны рынки, данные возвращаются без проблем, но как только у меня есть несколько связанных элементов, я получаю сообщение об ошибке:

Ошибка HTTP 502.3 - Bad Gateway
В указанном приложении CGI произошла ошибка, и сервер завершил процесс.

Я смутно припоминаю предыдущее приложение, в котором каждый сложный объект EF имел объект типа «только примитивы» для отправки и получения этого объекта клиенту и от него, но мне интересно, есть ли способ общаться без промежуточных объектов?

eg:

public class ProductViewModel {
    public int ProductID { get; set; }
    public string Name { get; set; }
    public List<MarketViewModel> Markets{ get; set; }
}

public class MarketViewModel {
    public int ProductID { get; set; }
    public Country Country { get; set; }
}

Меня беспокоят накладные расходы на кодирование при переводе каждого сложного объекта от клиента туда и обратно (что, признаюсь, я не уверен, что это даже плохо, возможно, это все равно нужно делать).

Поскольку API-интерфейсы с каркасом, похоже, принимают и возвращают объекты напрямую, мне интересно, есть ли способ напрямую обрабатывать сложную часть объекта.

Редактировать №1:

Согласно комментарию Ноэля ниже, если я изменю код, который вызывает ошибку

    [HttpGet("{id}", Name = "GetProduct")]
    public async Task<IActionResult> GetProduct([FromRoute] int id)
    {

        Product product = await _context.Products
            .Include(t => t.Markets)
            .SingleAsync(m => m.ProductID == id);

        throw new System.Exception("error sample");
        return Ok(product);
    }

трассировка стека правильно брошена. Если я удалю исключение, появится ошибка шлюза 500. Я согласен, что это похоже на ошибку сериализации, но трудно сказать.

РЕДАКТИРОВАТЬ 2 — согласно комментарию Олега ниже:

Решение проблем с плохим шлюзом состоит в том, чтобы сначала явно обновить более позднюю версию NewtonSoft.Json в зависимостях в файле project.json:

"dependencies": {
  "Newtonsoft.Json": "8.0.1-beta3",

затем вы должны изменить файл Startup.cs

    public void ConfigureServices(IServiceCollection services)
    {

         services.AddMvc()
            .AddJsonOptions(options => {
                options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
            });

с этими двумя настройками плохой шлюз больше не возникает, и вызов API успешно возвращает сложный объект, как и ожидалось.


person Alex C    schedule 15.12.2015    source источник
comment
Какое фактическое исключение выдает ваше приложение? Ваша проблема, вероятно, связана с проблемой сериализации, которую вы могли бы решить без использования моделей представления, но 503 не говорит нам достаточно, чтобы предложить, как это исправить.   -  person Noel    schedule 15.12.2015
comment
Я откопаю это сегодня вечером. Я изо всех сил пытался получить правильные трассировки стека из .net5   -  person Alex C    schedule 15.12.2015
comment
эй, Ноэль, я поэкспериментировал с твоим предложением и обновил пост выше. Кажется, это не ключ. Трассировка стека не выбрасывается, даже если вы включаете ошибки.   -  person Alex C    schedule 16.12.2015
comment
Если вы подозреваете ошибку сериализации, попробуйте добавить Newtonsoft.Json в последнюю версию 8.0.1-beta3 явно. Вы можете изучить package.lock.json, чтобы узнать, какая версия будет загружена автоматическим разрешением зависимостей. См. ошибку, подтверждающую ошибку HTTP 502.3 — Bad Gateway во время сериализации. Можно попробовать установить Newtonsoft.Json.ReferenceLoopHandling.Ignore; в конфигурации SerializerSettings.ReferenceLoopHandling (см. вопрос).   -  person Oleg    schedule 16.12.2015
comment
@Олег ты просто чудо! это сработало! Ух ты. Большое спасибо :)   -  person Alex C    schedule 16.12.2015
comment
@AlexC: Добро пожаловать! Я рад, что смог тебе помочь. Я добавил часть ОБНОВЛЕНО к своему ответу. Я думаю, что у других людей может быть такая же проблема.   -  person Oleg    schedule 16.12.2015


Ответы (2)


Мне кажется, что вы просто пропустили await в вызове SingleAsync. Попробуй использовать

[HttpGet]
public async Task<IActionResult> GetProduct([FromRoute] int id)
{
    return Ok(await _context.Products
        .Include(p => p.Markets)
        .SingleAsync(m => m.ProductID == id));
}

ОБНОВЛЕНО: я обнаружил проблему. Я бы порекомендовал вам изучить. Вы можете изучить package.lock.json, чтобы увидеть, какая версия будет загружена автоматическим разрешением зависимостей. Тогда я бы порекомендовал вам явно добавить Newtonsoft.Json в последнюю версию 8.0.1-beta3 к зависимостям вашего проекта. Кроме того, вы должны добавить параметр Newtonsoft.Json.ReferenceLoopHandling.Ignore в конфигурацию SerializerSettings.ReferenceLoopHandling. Дополнительные сведения см. в разделе проблемы.

person Oleg    schedule 15.12.2015
comment
Извинения - я думаю, что это ошибка в анонимном/обобщенном коде, который я переписал, чтобы он был достаточно общим для вопроса о переполнении стека. Мне нужно будет проверить сегодня вечером, когда я смогу вернуться к коду. - person Alex C; 15.12.2015
comment
@AlexC: Извините, я не понимаю, что вы имеете в виду. Код, который вы представили, имеет прототип public async Task<IActionResult> GetProduct([FromRoute] int id), а внутри вы вызываете SingleAsync. Вы должны использовать await в вызове, чтобы заставить поток дождаться завершения асинхронного вызова и только тогда вернуть результаты. См. здесь. Вы пытались добавить ключ await, как я написал? - person Oleg; 15.12.2015
comment
@AlexC: посмотрите на сообщение или ответ или пост или многие другие. Будьте осторожны, если добавить async, то нужно добавить await к соответствующему асинхронному методу, который вы используете, перед возвратом данных из метода. - person Oleg; 15.12.2015
comment
@AlexC: я могу воспроизвести ошибку HTTP Error 502.3 - Bad Gateway, которую вы описываете, если я просто установлю точку останова на операторе return working кода и подожду достаточно долго. Я попытаюсь изучить проблему более глубоко и выяснить, почему MVC6 выдает ошибку по тайм-ауту. Важно, чтобы веб-браузер получал сообщение об ошибке только после того, как я позволю продолжить выполнение моего кода MVC после длительного ожидания. Я имею в виду, что ошибка 502.3 - Bad Gateway действительно сообщает код сервера (код MVC). - person Oleg; 16.12.2015
comment
@Alexc и Олег Большое спасибо вам обоим за пост и за решение для обработки циклов, которое сработало для меня! - person Jaya; 23.05.2016
comment
@JS_GodBlessAll: Добро пожаловать! Я такой, что мог бы тебе помочь. - person Oleg; 23.05.2016
comment
@JS_GodBlessAll — это праздник любви! Большое спасибо за комментарий - person Alex C; 24.05.2016

Вы можете вернуть анонимный объект или использовать ExpandoObject/JsonObject:

public HttpResponseMessage Get()
{
    return this.Request.CreateResponse(
        HttpStatusCode.OK,
        new { Message = "Hello", Value = 123 });
}

//JsonObject

dynamic json = new JsonObject();
json.Message = "Hello";
json.Value = 123;

return new HttpResponseMessage<JsonObject>(json);

//РазвернутьОбъект

 dynamic expando = new ExpandoObject();
    expando.message = "Hello";
    expando.message2 = "World";
    return expando;
person Thiago Custodio    schedule 15.12.2015