¿Es posible obtener objetos complejos de Entity Framework desde una API REST en .NET sin crear objetos ViewModel?

asp.net-core asp.net-mvc c# entity-framework entity-framework-core

Pregunta

Imagina un conjunto de entidades 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; }
}

Imagina también un 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));
}

Si no hay mercados vinculados a la entidad, los datos se devuelven sin problema, pero tan pronto como tengo algunos elementos vinculados adjuntos, obtengo un error:

Error HTTP 502.3 - Puerta de enlace incorrecta
La aplicación CGI especificada encontró un error y el servidor finalizó el proceso.

Recuerdo vagamente una aplicación anterior en la que cada objeto EF complejo tenía un objeto de tipo "solo primitivos" para enviar y recibir este objeto desde y hacia el cliente, pero me pregunto si hay una manera de comunicarse sin objetos intermedios.

p.ej:

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; }
}

Mi preocupación es la sobrecarga de codificación de la traducción de cada objeto complejo de un lado a otro desde el cliente (lo cual, lo admito, no estoy seguro de que sea algo malo, tal vez tenga que hacerse de todos modos).

Como las API de andamios parecen tomar y devolver entidades directamente, me pregunto si hay una manera de manejar la parte compleja del objeto directamente.

Edición # 1:

El comentario de Per Noel a continuación, si cambio el código que está causando el error

    [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);
    }

La traza de pila es lanzada correctamente. Si elimino la excepción, aparece el error 500 gateway. Estoy de acuerdo en que parece que es probablemente un error de serialización, pero es difícil de decir.

EDITAR 2 - por un comentario de Oleg a continuación:

La solución a una puerta de enlace incorrecta es primero actualizar explícitamente una versión más reciente de NewtonSoft.Json en las dependencias en el archivo project.json :

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

A continuación debes alterar el archivo Startup.cs .

    public void ConfigureServices(IServiceCollection services)
    {

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

con esas dos configuraciones en su lugar, la puerta de enlace incorrecta ya no se produce y una llamada a la API devuelve el objeto complejo como se esperaba.

Respuesta aceptada

Me parece que solo te perdiste la await en la llamada de SingleAsync . Tratar de usar

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

ACTUALIZADO : He encontrado el problema . Le recomendaría examinar. Puede examinar package.lock.json para ver qué versión se cargará mediante la resolución automática de dependencias. Entonces recomendaría agregar explícitamente Newtonsoft.Json en la última versión 8.0.1-beta3 a las dependencias de su proyecto. Además, debe agregar la configuración de Newtonsoft.Json.ReferenceLoopHandling.Ignore en la configuración de SerializerSettings.ReferenceLoopHandling . Vea el tema para más detalles.


Respuesta popular

Puede devolver un objeto anónimo o usar 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);

// ExpandoObject

 dynamic expando = new ExpandoObject();
    expando.message = "Hello";
    expando.message2 = "World";
    return expando;


Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué