本文内容来自书籍: Marinko Spasojevic - Ultimate ASP.NET Core Web API - From Zero To Six-Figure Backend Developer (2nd edition)
需要给PUT API提供一个新的DTO,虽然内容是一样的,不过还是需要区分开比较好,这个原因会在模型验证的章节说明
两种情况的更新
Context
中,因此,如果查询的数据(已经被跟踪)被修改了,那么状态会变成Modified
,然后直接调用Save
方法,就会产生Update SQL
Context
中进行,也就是说你需要更新的这个实体,是没有被当前的Context
所跟踪的,而且这个实体是具有Id
的,这样的没有被跟踪的实体,需要先是将它加入Context
或者说被跟踪,然后再设置状态为Modified
,然后Save
的时候,就会有Update SQL
,不过有一个问题,就是,即使只是更新了其中一个字段,也会有全字段的更新PUT 负责全更新
PATCH 部分更新
但是这个不是它们两者之间的唯一区别,请求体也不一样
[FromBody]JsonPatchDocument<Company>
,格式是[ { "op": "replace", "path": "/name", "value": "new name" }, { "op": "remove", "path": "/name" } ]
一共有六种不同的操作
// 赋值 { "op": "add", "path": "/name", "value": "new name" }, // 赋值 { "op": "replace", "path": "/name", "value": "new name" }, // 赋值(默认值) { "op": "remove", "path": "/name" }, // 将from的值赋给path { "op": "copy", "from": "/name", "path": "/title" }, // 将from的值赋给path { "op": "move", "from": "/name", "path": "/title" }, // 测试特定字段是否有特定值 { "op": "test", "path": "/name", "value": "new name" }
[FromBody]Company
media type
也不一样
application/json-patch+json
,虽然直接用application/json
也是能用的,不过建议是用这种application/json
那么在ASP.NET Core中,需要安装相应的包实现
在主项目中,安装Microsoft.AspNetCore.Mvc.NewtonsoftJson
,用以支持请求体转换成PatchDocument
在Presentation
中安装Microsoft.AspNetCore.JsonPatch
,用以支持JsonPatchDocument
在控制器中使用
在主项目的配置中,如果直接使用AddNewtonsoftJson
,那么它会替换System.Text.Json formatters
,然后是全局替换,所有的JSON序列化工作都被转移了,但是作者不希望这样做,只希望在局部使用这个JSON包,所以在Program.cs
中
// 添加一个本地方法 NewtonsoftJsonPatchInputFormatter GetJsonPatchInputFormatter() => new ServiceCollection() .AddLogging() .AddMvc() .AddNewtonsoftJson() .Services.BuildServiceProvider() .GetRequiredService<IOptions<MvcOptions>>().Value.InputFormatters .OfType<NewtonsoftJsonPatchInputFormatter>() .First(); builder.Services.AddControllers(config => { config.RespectBrowserAcceptHeader = true; config.ReturnHttpNotAcceptable = true; config.InputFormatters.Insert(0, GetJsonPatchInputFormatter()); }) .AddXmlDataContractSerializerFormatters() .AddCustomCsvFormatter() .AddApplicationPart(typeof(AssemblyReference).Assembly);
自动映射,可以正向和反向一起,这样就不用重复写
CreateMap<EmployeeForUpdateDto, Employee>().ReverseMap();
[HttpPatch("{id:guid}")] public IActionResult PartiallyUpdateEmployeeForCompany(Guid companyId, Guid id, [FromBody] JsonPatchDocument<EmployeeForUpdateDto>? patchDoc) { if (patchDoc is null) return BadRequest("patchDoc object sent from client is null."); var result = _service.EmployeeService.GetEmployeeForPatch(companyId, id, compTrackChanges: false, empTrackChanges: true); patchDoc.ApplyTo(result.employeeToPatch); _service.EmployeeService.SaveChangesForPatch(result.employeeToPatch, result.employeeEntity); return NoContent(); }
然后可以看到,在请求体的接受上,与PUT
方法是不一样的,然后处理方式是先查询出原有的对象,然后将JSONdoc转换为DTO,然后调用Save方法,内部会将DTO转换为Entity,由于前面查询Entity是有跟踪的,所以在修改了Entity之后,模型状态就是Modified
了,直接保存就会有Update SQL
了