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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With