Is it possible to get complex Entity Framework objects from a REST api in .NET without creating ViewModel objects?

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

Question

Imagine a set of Entity Framework entities:

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

Imagine as well a 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));
}

If there are no markets attached to the entity the data returns without issue, but as soon as I have a few linked items attached I get an error:

HTTP Error 502.3 - Bad Gateway
The specified CGI application encountered an error and the server terminated the process.

I vaguely recall a previous application where every complex EF object had a "primitives only" type object to send and receive this object to and from the client, but I wonder if there is a way to communicate without intermediary objects?

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

My concern is the coding overhead of translating every complex object back and forth from the client (which, I'll admit, I'm not sure is even a bad thing, maybe it has to be done anyways).

Since the scaffolded APIs seem to take and return entities directly I find myself wondering if there is a way to handle the complex part of the object directly

Edit #1:

Per Noel's comment below, if I change the code which is causing the error to

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

the stack trace is Correctly thrown. If I remove the exception, the 500 gateway error appears. I agree that it looks like it's probably a serialization error, but it's tough to say.

EDIT 2 - per a comment from Oleg below:

The solution to a bad gateway is to first explicitly update a more recent version of NewtonSoft.Json in the dependencies in the project.json file:

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

next you must alter the Startup.cs file

    public void ConfigureServices(IServiceCollection services)
    {

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

with those two settings in place, the bad gateway no longer occurs and an api call successfully returns the complex object as expected.

1
5
12/16/2015 1:42:03 AM

Accepted Answer

It seems to me that you just missed await in the call of SingleAsync. Try to use

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

UPDATED: I found the issue. I would recommend you to examine You can examine package.lock.json to see, which version will be loaded by automatic resolution of dependencies. Then I would you recommend to explicitly add Newtonsoft.Json in the latest version 8.0.1-beta3 to the dependencies of your project. Additionally you should add the setting of to Newtonsoft.Json.ReferenceLoopHandling.Ignore in the the configuration of SerializerSettings.ReferenceLoopHandling. See the issue for more details.

4
12/16/2015 5:51:07 AM

Popular Answer

You can return anonymous object or use 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;


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow