C# ASP.NET Core 2.0 web api实体框架核心-寻找更优雅的解决方案
我正在ASP.NET Core 2.0上开发一个web api项目,其中Entity Framework Core连接到postgres数据库 下面的PUT方法可以毫无问题地工作,但我觉得我可能误解了update方法的使用,我还觉得应该有一个更优雅的解决方案来更新现有对象,以一种保存PUT请求而不是补丁的方式C# ASP.NET Core 2.0 web api实体框架核心-寻找更优雅的解决方案,c#,rest,asp.net-web-api,entity-framework-core,npgsql,C#,Rest,Asp.net Web Api,Entity Framework Core,Npgsql,我正在ASP.NET Core 2.0上开发一个web api项目,其中Entity Framework Core连接到postgres数据库 下面的PUT方法可以毫无问题地工作,但我觉得我可能误解了update方法的使用,我还觉得应该有一个更优雅的解决方案来更新现有对象,以一种保存PUT请求而不是补丁的方式 // PUT: api/Discount/5 [HttpPut("{id}")] [ProducesResponseType(204)] [Produces
// PUT: api/Discount/5
[HttpPut("{id}")]
[ProducesResponseType(204)]
[ProducesResponseType(400)]
[ProducesResponseType(404)]
[ProducesResponseType(500)]
public IActionResult Put(int id, [FromBody]Discount discount)
{
if (ModelState.IsValid)
{
using (var context = new BarberskabetDbContext())
{
if (context.Discounts.Any(x => x.DiscountId == id && !x.Deleted))
{
var dbDiscount = context.Discounts.FirstOrDefault(x => x.DiscountId == id && !x.Deleted);
dbDiscount.AppliesOnce = discount.AppliesOnce;
dbDiscount.AppliesToProductType = discount.AppliesToProductType;
dbDiscount.Code = discount.Code;
dbDiscount.DiscountType = discount.DiscountType;
dbDiscount.Duration = discount.Duration;
dbDiscount.DurationUsageLimit = discount.DurationUsageLimit;
dbDiscount.EndsAt = discount.EndsAt;
dbDiscount.OncePerCustomer = discount.OncePerCustomer;
dbDiscount.RestrictByEmail = discount.RestrictByEmail;
dbDiscount.StartsAt = discount.StartsAt;
dbDiscount.Status = discount.Status;
dbDiscount.TimesUsed = discount.TimesUsed;
dbDiscount.UsageLimit = discount.UsageLimit;
dbDiscount.Value = discount.Value;
context.Update(dbDiscount);
if (context.SaveChanges() == 0)
{
return StatusCode(500, "Unable to update record.");
}
return NoContent();
}
else
{
return NotFound();
}
}
}
else
{
return BadRequest(ModelState);
}
}
我觉得应该有更好的方法将新信息放入数据库,但我很难看到它
编辑:
我对优雅/更好的想法是,减少代码以获得相同的效果,并更好地使用工具,正如Ivan正确建议的那样。
感谢所有建议Automapper删除从dto到模型的繁琐的变量重新映射的人。还特别感谢Ivan Stoev解释了强制更新和部分更新的工作原理,并表示最好对异常进行错误处理,而不是受影响的行数。更新方法用于所谓的强制更新,即断开您确信存在的实体的连接。它不从数据库加载实体,只更新所有属性
以您为例,强制更新如下所示:
if (context.Discounts.Any(x => x.DiscountId == id && !x.Deleted))
{
context.Update(discount);
context.SaveChanges();
}
如果要仅更新更改的属性(如果有),则应加载数据库实体并使用CurrentValues.UpdateValues方法应用更改:
var dbDiscount = context.Discounts.FirstOrDefault(x => x.DiscountId == id && !x.Deleted);
if (dbDiscount != null)
{
context.Entry(dbDiscount).CurrentValues.SetValues(discount);
context.SaveChanges();
}
请注意,在这种情况下,如果所有属性值都与原始属性值相同,则EF可能根本不会发出update命令,即SaveChanges可以返回0,因此不应将其用作成功的指示。事实上,您不应该将其用于此目的,因为如果出现问题,EF将抛出异常
有关更多信息和示例,请参阅文档主题。更新方法用于所谓的强制更新,即当您断开实体连接并确定其存在时。它不从数据库加载实体,只更新所有属性
以您为例,强制更新如下所示:
if (context.Discounts.Any(x => x.DiscountId == id && !x.Deleted))
{
context.Update(discount);
context.SaveChanges();
}
如果要仅更新更改的属性(如果有),则应加载数据库实体并使用CurrentValues.UpdateValues方法应用更改:
var dbDiscount = context.Discounts.FirstOrDefault(x => x.DiscountId == id && !x.Deleted);
if (dbDiscount != null)
{
context.Entry(dbDiscount).CurrentValues.SetValues(discount);
context.SaveChanges();
}
请注意,在这种情况下,如果所有属性值都与原始属性值相同,则EF可能根本不会发出update命令,即SaveChanges可以返回0,因此不应将其用作成功的指示。事实上,您不应该将其用于此目的,因为如果出现问题,EF将抛出异常
有关更多信息和示例,请参阅文档主题。定义更优雅、更好的。难道你不想看到在那里命名的属性吗?然后使用AutoMapper。如果说“更优雅”是指减少控制器方法中的代码,我会看看AutoMapper。它是一个用于.NET的简单对象/模型映射器。每当我的型号/DTO的尺寸开始显著增长时,我都倾向于使用它。此外,我可能会将CRUD操作分解到解决方案中的另一个类,并在我的控制器中使用存储库模式。难道你不想看到在那里命名的属性吗?然后使用AutoMapper。如果说“更优雅”是指减少控制器方法中的代码,我会看看AutoMapper。它是一个用于.NET的简单对象/模型映射器。每当我的型号/DTO的尺寸开始显著增长时,我都倾向于使用它。此外,我可能会将CRUD操作分解到解决方案中的另一个类,并在我的控制器中使用存储库模式