在 ASP.NET MVC Core Web API 和 Entity Framework Core 中使用 TryUpdateModel 更新模型不起作用
Update model with TryUpdateModel in ASP.NET MVC Core Web API and Entity Framework Core doesn't work
我是 ASP.NET MVC Core Web API 和 EF Core 的新手,我正在创建一个简单的 MVC web API 演示,如下所示。
[HttpPut("{id}")]
public async void Put(int id, [FromBody]Student student)
{
if (ModelState.IsValid)
{
var stu = _context.Students
.Where(s => s.StudentId == id)
.SingleOrDefault();
var updateResult = await TryUpdateModelAsync<Student>(stu, "", s => s.FirstName, s => s.LastName, s => s.EnrollmentDate);
_context.SaveChanges();
}
}
问题是 TryUpdateModelAsync
不起作用,更改没有更新到数据库。
我想知道:
如果TryUpdateModelAsync
可以在MVC Web中使用API?
我真的不想写像下面这样无聊的代码,如何避免从一个对象到另一个相同类型的对象进行属性值设置? (这是我使用 TryUpdateModelAsync
的第一个原因)
stu.FirstName = student.FirstName;
stu.LastName = student.LastName;
stu.SomeOtherProperties = student.SomeOtherProperties;
_context.SaveChanges();
.NET核心版本:1.1.0
ASP.Net核心版本:1.1.0
Entity Framework核心版本:1.1.0
尝试从您的 body 中传递学生实例,而不是您查找的实例。
public async void Put(int id, [FromBody]Student student)
{
if (ModelState.IsValid)
{
student.StudentId = id; // If the ID isn't passed in or you could validate that it matches the id passed in
var updateResult = await TryUpdateModelAsync<Student>(student, "", s => s.FirstName, s => s.LastName, s => s.EnrollmentDate);
_context.SaveChanges();
}
}
FromBody 使用格式化程序将请求正文反序列化为 JSON 对象。
TryUpdateModelAsync 依赖于模型绑定并使用值提供程序来获取数据。
您可以将 posted 数据更改为使用模型绑定的表单 post。
WebAPI一般使用DTO The official docs显示正确的方法。
请参阅 GitHub issue TryUpdateModelAsync() doesn't work for ASP.NET Core MVC Web API 以获得最终答案。
[FromBody]
属性使用格式化程序将请求主体反序列化为对象(例如使用 JSON 或 XML)。
另一方面,模型绑定(包括 TryUpdateModel
API)使用值提供程序从表单主体(如果有)、查询字符串、路由数据或一些其他地方。
这里有几种方法:
- 您当然可以编写 "boring" 代码。或者您可以考虑使用自动执行此 "left-hand/right-hand" 操作的库,例如 AutoMapper。
- 您可以将 posted 数据更改为表单 post,在这种情况下模型绑定将正常工作。
- 您可以更改 posted 数据以使用 JSON PATCH 协议,该协议正是为这种情况而创建的。有一个很棒的博客 post 关于如何使用 ASP.NET Core 做到这一点:http://benfoster.io/blog/aspnet-core-json-patch-partial-api-updates
这是一种从一个模型到另一个模型执行基本更新的方法,如果任何属性更新失败,它将 return false,否则为 true。我怀疑 TryUpdateModelAsync
可能会执行额外的工作,但至少对于我来说这已经足够了。
您需要添加 using System;
和 using System.Linq.Expressions;
。除此之外,只需粘贴到您要使用它的 class 中。我在我的自定义控制器 class 中使用它,以便我可以以类似于 TryUpdateModelAsync
.
的方式在我的所有派生控制器中使用它
/// <summary>
/// Updates the destination model instance using values from the source model
/// </summary>
/// <typeparam name="TModel">The type of model to update</typeparam>
/// <param name="source">The model containing the values to use</param>
/// <param name="destination">The model to update</param>
/// <param name="properties">The properties of the model to update</param>
public static bool TryUpdateModel<TModel>(TModel source, TModel destination, params Expression<Func<TModel, object>>[] properties) where TModel : class
{
var ok = true;
foreach (var prop in properties)
{
try
{
string propertyName = string.Empty;
if (prop.Body is MemberExpression member)
propertyName = member.Member.Name;
else if (prop.Body is UnaryExpression unary)
propertyName =((MemberExpression)unary.Operand).Member.Name;
if (string.IsNullOrWhiteSpace(propertyName))
{
ok = false;
continue;
}
var input = source.GetType().GetProperty(propertyName).GetValue(source, null);
destination.GetType().GetProperty(propertyName).SetValue(destination, input);
}
catch
{
ok = false;
}
}
return ok;
}
在您的情况下,用法如下。您可能不需要 <student>
部分,但为了完整起见,我将其包括在内。
var updateResult = TryUpdateModel<Student>(student, stu,
s => s.FirstName,
s => s.LastName,
s => s.EnrollmentDate
);
我是 ASP.NET MVC Core Web API 和 EF Core 的新手,我正在创建一个简单的 MVC web API 演示,如下所示。
[HttpPut("{id}")]
public async void Put(int id, [FromBody]Student student)
{
if (ModelState.IsValid)
{
var stu = _context.Students
.Where(s => s.StudentId == id)
.SingleOrDefault();
var updateResult = await TryUpdateModelAsync<Student>(stu, "", s => s.FirstName, s => s.LastName, s => s.EnrollmentDate);
_context.SaveChanges();
}
}
问题是 TryUpdateModelAsync
不起作用,更改没有更新到数据库。
我想知道:
如果
TryUpdateModelAsync
可以在MVC Web中使用API?我真的不想写像下面这样无聊的代码,如何避免从一个对象到另一个相同类型的对象进行属性值设置? (这是我使用
TryUpdateModelAsync
的第一个原因)stu.FirstName = student.FirstName; stu.LastName = student.LastName; stu.SomeOtherProperties = student.SomeOtherProperties; _context.SaveChanges();
.NET核心版本:1.1.0
ASP.Net核心版本:1.1.0
Entity Framework核心版本:1.1.0
尝试从您的 body 中传递学生实例,而不是您查找的实例。
public async void Put(int id, [FromBody]Student student)
{
if (ModelState.IsValid)
{
student.StudentId = id; // If the ID isn't passed in or you could validate that it matches the id passed in
var updateResult = await TryUpdateModelAsync<Student>(student, "", s => s.FirstName, s => s.LastName, s => s.EnrollmentDate);
_context.SaveChanges();
}
}
FromBody 使用格式化程序将请求正文反序列化为 JSON 对象。
TryUpdateModelAsync 依赖于模型绑定并使用值提供程序来获取数据。
您可以将 posted 数据更改为使用模型绑定的表单 post。
WebAPI一般使用DTO The official docs显示正确的方法。
请参阅 GitHub issue TryUpdateModelAsync() doesn't work for ASP.NET Core MVC Web API 以获得最终答案。
[FromBody]
属性使用格式化程序将请求主体反序列化为对象(例如使用 JSON 或 XML)。
另一方面,模型绑定(包括 TryUpdateModel
API)使用值提供程序从表单主体(如果有)、查询字符串、路由数据或一些其他地方。
这里有几种方法:
- 您当然可以编写 "boring" 代码。或者您可以考虑使用自动执行此 "left-hand/right-hand" 操作的库,例如 AutoMapper。
- 您可以将 posted 数据更改为表单 post,在这种情况下模型绑定将正常工作。
- 您可以更改 posted 数据以使用 JSON PATCH 协议,该协议正是为这种情况而创建的。有一个很棒的博客 post 关于如何使用 ASP.NET Core 做到这一点:http://benfoster.io/blog/aspnet-core-json-patch-partial-api-updates
这是一种从一个模型到另一个模型执行基本更新的方法,如果任何属性更新失败,它将 return false,否则为 true。我怀疑 TryUpdateModelAsync
可能会执行额外的工作,但至少对于我来说这已经足够了。
您需要添加 using System;
和 using System.Linq.Expressions;
。除此之外,只需粘贴到您要使用它的 class 中。我在我的自定义控制器 class 中使用它,以便我可以以类似于 TryUpdateModelAsync
.
/// <summary>
/// Updates the destination model instance using values from the source model
/// </summary>
/// <typeparam name="TModel">The type of model to update</typeparam>
/// <param name="source">The model containing the values to use</param>
/// <param name="destination">The model to update</param>
/// <param name="properties">The properties of the model to update</param>
public static bool TryUpdateModel<TModel>(TModel source, TModel destination, params Expression<Func<TModel, object>>[] properties) where TModel : class
{
var ok = true;
foreach (var prop in properties)
{
try
{
string propertyName = string.Empty;
if (prop.Body is MemberExpression member)
propertyName = member.Member.Name;
else if (prop.Body is UnaryExpression unary)
propertyName =((MemberExpression)unary.Operand).Member.Name;
if (string.IsNullOrWhiteSpace(propertyName))
{
ok = false;
continue;
}
var input = source.GetType().GetProperty(propertyName).GetValue(source, null);
destination.GetType().GetProperty(propertyName).SetValue(destination, input);
}
catch
{
ok = false;
}
}
return ok;
}
在您的情况下,用法如下。您可能不需要 <student>
部分,但为了完整起见,我将其包括在内。
var updateResult = TryUpdateModel<Student>(student, stu,
s => s.FirstName,
s => s.LastName,
s => s.EnrollmentDate
);