在 http post 控制器中使用实体 属性 与分配更好吗?
Is it better to use entity property vs assignments in http post controller?
假设我们有一个从另一个应用程序返回 HttpPost 对象的 aspnet core2 控制器。
我正在使用 Entity framework 内核,这两种方式都有效,但我只是对最佳实践和性能感到好奇,想知道该使用什么?
[HttpPost]
public Message Post([FromBody] Message message)
{
// my old code
// var msg = new Message { Owner = message.Owner, Text = message.Text };
//db.Messages.AddAsync(msg)
var msgEntity = db.Messages.Add(message).Entity;
db.Messages.AddAsync(message);
db.SaveChangesAsync();
return message;
}
当我使用 Entity 时发生了一些奇怪的事情,它更改了自动递增的 ID 参数 {Id:1002},而在我添加它之前,下一个连续的 ID 应该是 {Id:11}
永远不要直接从请求中保存任何内容。这是您的最佳做法。前面的代码之所以优越,唯一的原因是您明确选择了实际保留的发布值,而不是盲目地保存用户决定直接发送到您的数据库的任何内容。
更好的方法是实际使用视图模型来接受用户输入。例如:
public class MessageViewModel
{
public int OwnerId { get; set; }
[Required]
public string Text { get; set; }
}
然后,您接受它作为参数,并将其映射到您的实体:
public async Task<IActionResult> Post([FromBody]MessageViewModel model)
{
if (ModelState.IsValid)
{
var message = new Message
{
Owner = db.Users.Find(model.OwnerId),
Text = model.Text
};
db.Messages.Add(message);
await db.SaveChangesAsync();
return Ok(message);
}
return BadRequest(ModelState);
}
我在上面的代码中做了一些重要的更改:
添加了条件以在继续保存之前检查 ModelState.IsValid
是否为真。正如您现在所拥有的那样,您只是在盲目地尝试保存传入的任何内容,即使这样做最终会引发数据库异常,因为缺少所需的值等等。
有了这个条件,您现在需要处理出现问题的场景。更有可能的是,您需要 return 某种错误对象,以帮助客户解决问题。实际上只是 return ModelState
是很常见的,这将序列化其验证错误列表,但您可能还想做其他事情。
由于这现在可能需要 returning 两种不同的对象类型,因此动作签名已更改为 return IActionResult
。通常最好一直使用 return 类型。基本上,它是一个包罗万象的东西,而像 Message
这样的特定 return 很容易超出它的用处,需要您继续并稍后更改它。当绝对没有任何类型的失败机会时,你真的应该只 return 特定类型,即响应将 always 是 200 OK。然而,这种情况很少见。
视图模型只接受所有者的 id,而不是完整的(大概 ApplicationUser
)对象。接受 Message
并直接保存它也存在同样的问题。恶意用户可以篡改 Owner
对象属性,从而允许他们潜在地更改各种不应更改的内容。通常,您应该始终着手让用户操作尽可能少的数据。您公开的任何内容都应该有意,充分理解其中的含义。因为,我们现在只接受一个 id,然后我们需要查找具有该 id 的用户,以在实体上设置 Owner
属性。如果您碰巧有一个显式外键 属性,例如 Message
上的 OwnerId
,您可以直接设置它。
已将 AddAsync
更改为 Add
。根据文档,您几乎不应该使用 AddAsync
。它仅出于您不太可能遇到的非常特定的目的而存在。建议始终使用 Add
,除非您有充分的理由。
已将 await
添加到 SaveChangesAsync
调用。始终等待异步操作,除非该操作的完成与任何事情无关。在这里绝对不是这样。 SaveChangesAsync
可以引发异常,您的应用程序需要处理的异常。不等待,基本上就是吞下这些,让代码愉快地继续下去,就好像没有问题一样。虽然这听起来像是积极的,但绝对不是。不等待也可能导致其他问题,例如上下文可能会在调用完成之前被处理掉。
由于我们现在正在等待 SaveChangesAsync
,因此必须将 async
添加到方法签名中,并且您必须 return 一个 Task<IActionResult>
。
假设我们有一个从另一个应用程序返回 HttpPost 对象的 aspnet core2 控制器。 我正在使用 Entity framework 内核,这两种方式都有效,但我只是对最佳实践和性能感到好奇,想知道该使用什么?
[HttpPost]
public Message Post([FromBody] Message message)
{
// my old code
// var msg = new Message { Owner = message.Owner, Text = message.Text };
//db.Messages.AddAsync(msg)
var msgEntity = db.Messages.Add(message).Entity;
db.Messages.AddAsync(message);
db.SaveChangesAsync();
return message;
}
当我使用 Entity 时发生了一些奇怪的事情,它更改了自动递增的 ID 参数 {Id:1002},而在我添加它之前,下一个连续的 ID 应该是 {Id:11}
永远不要直接从请求中保存任何内容。这是您的最佳做法。前面的代码之所以优越,唯一的原因是您明确选择了实际保留的发布值,而不是盲目地保存用户决定直接发送到您的数据库的任何内容。
更好的方法是实际使用视图模型来接受用户输入。例如:
public class MessageViewModel
{
public int OwnerId { get; set; }
[Required]
public string Text { get; set; }
}
然后,您接受它作为参数,并将其映射到您的实体:
public async Task<IActionResult> Post([FromBody]MessageViewModel model)
{
if (ModelState.IsValid)
{
var message = new Message
{
Owner = db.Users.Find(model.OwnerId),
Text = model.Text
};
db.Messages.Add(message);
await db.SaveChangesAsync();
return Ok(message);
}
return BadRequest(ModelState);
}
我在上面的代码中做了一些重要的更改:
添加了条件以在继续保存之前检查
ModelState.IsValid
是否为真。正如您现在所拥有的那样,您只是在盲目地尝试保存传入的任何内容,即使这样做最终会引发数据库异常,因为缺少所需的值等等。有了这个条件,您现在需要处理出现问题的场景。更有可能的是,您需要 return 某种错误对象,以帮助客户解决问题。实际上只是 return
ModelState
是很常见的,这将序列化其验证错误列表,但您可能还想做其他事情。由于这现在可能需要 returning 两种不同的对象类型,因此动作签名已更改为 return
IActionResult
。通常最好一直使用 return 类型。基本上,它是一个包罗万象的东西,而像Message
这样的特定 return 很容易超出它的用处,需要您继续并稍后更改它。当绝对没有任何类型的失败机会时,你真的应该只 return 特定类型,即响应将 always 是 200 OK。然而,这种情况很少见。视图模型只接受所有者的 id,而不是完整的(大概
ApplicationUser
)对象。接受Message
并直接保存它也存在同样的问题。恶意用户可以篡改Owner
对象属性,从而允许他们潜在地更改各种不应更改的内容。通常,您应该始终着手让用户操作尽可能少的数据。您公开的任何内容都应该有意,充分理解其中的含义。因为,我们现在只接受一个 id,然后我们需要查找具有该 id 的用户,以在实体上设置Owner
属性。如果您碰巧有一个显式外键 属性,例如Message
上的OwnerId
,您可以直接设置它。已将
AddAsync
更改为Add
。根据文档,您几乎不应该使用AddAsync
。它仅出于您不太可能遇到的非常特定的目的而存在。建议始终使用Add
,除非您有充分的理由。已将
await
添加到SaveChangesAsync
调用。始终等待异步操作,除非该操作的完成与任何事情无关。在这里绝对不是这样。SaveChangesAsync
可以引发异常,您的应用程序需要处理的异常。不等待,基本上就是吞下这些,让代码愉快地继续下去,就好像没有问题一样。虽然这听起来像是积极的,但绝对不是。不等待也可能导致其他问题,例如上下文可能会在调用完成之前被处理掉。由于我们现在正在等待
SaveChangesAsync
,因此必须将async
添加到方法签名中,并且您必须 return 一个Task<IActionResult>
。