是否可以在不创建 ViewModel 对象的情况下从 .NET 中的 REST api 获取复杂的 Entity Framework 对象?
Is it possible to get complex Entity Framework objects from a REST api in .NET without creating ViewModel objects?
想象一组 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));
}
如果实体没有附加市场,数据 return 没有问题,但是一旦我附加了一些链接项目,我就会收到错误消息:
HTTP Error 502.3 - Bad Gateway
The specified CGI application encountered an error and the server terminated the process.
我依稀记得以前的一个应用程序,其中每个复杂的 EF 对象都有一个 "primitives only" 类型的对象来向客户端发送和接收此对象,但我想知道是否有一种无需中间对象即可进行通信的方法?
例如:
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 似乎直接接受 return 实体,我发现自己想知道是否有一种方法可以直接处理对象的复杂部分
编辑 #1:
根据下面 Noel 的评论,如果我将导致错误的代码更改为
[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 - 根据下面 Oleg 的评论:
坏网关的解决方案是首先在 project.json
文件的依赖项中显式更新更新版本的 NewtonSoft.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 成功调用 return 了预期的复杂对象。
您可以return匿名对象或使用 ExpandoObject / JsonObject:
public HttpResponseMessage Get()
{
return this.Request.CreateResponse(
HttpStatusCode.OK,
new { Message = "Hello", Value = 123 });
}
//Json对象
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;
在我看来,您刚刚错过了 SingleAsync
的呼叫 await
。尝试使用
[HttpGet]
public async Task<IActionResult> GetProduct([FromRoute] int id)
{
return Ok(await _context.Products
.Include(p => p.Markets)
.SingleAsync(m => m.ProductID == id));
}
已更新:我发现 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 了解更多详情。
想象一组 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));
}
如果实体没有附加市场,数据 return 没有问题,但是一旦我附加了一些链接项目,我就会收到错误消息:
HTTP Error 502.3 - Bad Gateway
The specified CGI application encountered an error and the server terminated the process.
我依稀记得以前的一个应用程序,其中每个复杂的 EF 对象都有一个 "primitives only" 类型的对象来向客户端发送和接收此对象,但我想知道是否有一种无需中间对象即可进行通信的方法?
例如:
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 似乎直接接受 return 实体,我发现自己想知道是否有一种方法可以直接处理对象的复杂部分
编辑 #1:
根据下面 Noel 的评论,如果我将导致错误的代码更改为
[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 - 根据下面 Oleg 的评论:
坏网关的解决方案是首先在 project.json
文件的依赖项中显式更新更新版本的 NewtonSoft.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 成功调用 return 了预期的复杂对象。
您可以return匿名对象或使用 ExpandoObject / JsonObject:
public HttpResponseMessage Get()
{
return this.Request.CreateResponse(
HttpStatusCode.OK,
new { Message = "Hello", Value = 123 });
}
//Json对象
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;
在我看来,您刚刚错过了 SingleAsync
的呼叫 await
。尝试使用
[HttpGet]
public async Task<IActionResult> GetProduct([FromRoute] int id)
{
return Ok(await _context.Products
.Include(p => p.Markets)
.SingleAsync(m => m.ProductID == id));
}
已更新:我发现 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 了解更多详情。