È possibile ottenere oggetti Entity Framework complessi da un API REST in .NET senza creare oggetti ViewModel?

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

Domanda

Immagina una serie di entità 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; }
}

Immagina anche 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));
}

Se non ci sono mercati collegati all'entità, i dati ritornano senza problemi, ma non appena ho collegato alcuni articoli collegati, ricevo un errore:

Errore HTTP 502.3 - Gateway non valido
L'applicazione CGI specificata ha riscontrato un errore e il server ha terminato il processo.

Ricordo vagamente un'applicazione precedente in cui ogni oggetto EF complesso aveva un oggetto di tipo "solo primitive" per inviare e ricevere questo oggetto da e verso il client, ma mi chiedo se c'è un modo di comunicare senza oggetti intermediari?

per esempio:

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

La mia preoccupazione è il sovraccarico di codice di tradurre ogni oggetto complesso avanti e indietro dal client (che, lo ammetto, non sono sicuro che sia anche una cosa negativa, forse deve essere fatto comunque).

Dal momento che le API scaffold sembrano prendere e restituire direttamente le entità, mi sorprendo a chiedermi se esiste un modo per gestire direttamente la parte complessa dell'oggetto

Modifica n. 1:

Il commento di Per Noel qui sotto, se cambio il codice che sta causando l'errore

    [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 traccia dello stack è corretta. Se rimuovo l'eccezione, viene visualizzato l'errore del gateway 500. Sono d'accordo che sembra che sia probabilmente un errore di serializzazione, ma è difficile da dire.

EDIT 2 - per un commento di Oleg di seguito:

La soluzione a un gateway non NewtonSoft.Json consiste innanzitutto nell'aggiornare esplicitamente una versione più recente di NewtonSoft.Json nelle dipendenze nel file project.json :

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

successivamente è necessario modificare il file Startup.cs

    public void ConfigureServices(IServiceCollection services)
    {

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

con queste due impostazioni, il gateway non funziona più e una chiamata API restituisce correttamente l'oggetto complesso come previsto.

Risposta accettata

Mi sembra che hai appena perso await nella chiamata di SingleAsync . Prova ad usare

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

AGGIORNATO : ho trovato il problema . Ti consiglio di esaminare Puoi esaminare package.lock.json per vedere quale versione verrà caricata dalla risoluzione automatica delle dipendenze. Quindi ti consiglio di aggiungere in modo esplicito Newtonsoft.Json nell'ultima versione 8.0.1-beta3 alle dipendenze del tuo progetto. Inoltre, è necessario aggiungere l'impostazione a Newtonsoft.Json.ReferenceLoopHandling.Ignore nella configurazione di SerializerSettings.ReferenceLoopHandling . Vedi il problema per maggiori dettagli.


Risposta popolare

Puoi restituire un oggetto anonimo o usare 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;



Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché