使用绑定参数更新模型会取消那些不在表单中的参数
Updating a model with bind params is nulling out ones not in form
我是 C# 和 .NET 核心的新手。
我想知道为什么在使用绑定编辑模型时,它会创建一个新模型并绑定 post 中的属性。但是,如果您没有将 post 形式的所有字段都隐藏起来,那么绑定会使它们无效吗?
它不应该加载一个模型并更新绑定参数并让它们单独存在吗?
例如,如果我正在更新一个人,而这个人有
Id, Name, Age, updated, Created
Edit(int id, [Bind("Id,Name,Age") Person p]
当我转到 _context.update(p)
时,它会将 updated
和 Created
归零,因为它们没有绑定。
为什么会这样?
如何让它只更新绑定的参数而不清空不需要加载的参数?
您传入的是 MVC 映射到实体定义中的反序列化数据块。它不会自动神奇地打开 DbContext、加载实体和覆盖值,它只是创建实体的实例,并复制值。其他一切都将保留为默认值。
作为一般规则,我建议不要将实体从客户端传递到服务器,以免混淆返回的内容。执行更新时,接受具有适用属性的视图模型进行更新,理想情况下,数据模型和视图模型应包含行版本号。 (即 SQL 服务器中的时间戳列,可以将 to/from Base64 字符串转换为 ViewModel 中的 send/compare)
从那里开始,在执行更新时,您可以通过 ID 获取实体,比较时间戳,然后可以利用 Automapper 处理将数据从 ViewModel 复制到实体中,或者手动复制值。
这样,当您的 ViewModel 返回要更新的数据时:
using (var context = new AppDbContext())
{
var entity = context.Persons.Single(x => x.Id == id);
if (entity.TimeStampBase64 != viewModel.TimestampBase64)
{
// Handle fact that data has changed since the client last loaded it.
}
Mapper.Map(entity, viewModel);
context.SaveChanges();
}
您可以按原样使用实体定义并仍然加载现有实体并使用 Automapper 将值从传输实体 class 复制到 DbContext 跟踪实体,但是最好避免混淆实体实例在跟踪的“真实”实体实例和可能不完整的未跟踪传输实例之间。代码通常会有接受实体作为参数的方法来执行验证、计算等操作,如果稍后这些方法假设它们将获得“真实”实体而不是在只有瞬态 DTO 风格的地方被调用,它可能会变得混乱一个实体。
接收实体并用它调用 DbContext.Update(entity)
似乎更简单,但这会给您带来许多问题,包括:
- 您需要将有关实体的所有内容传递给客户端,以便客户端可以将其发送回服务器。这需要隐藏输入或序列化页面中的整个实体,从而向浏览器公开有关您的域模型的更多信息。这会增加客户端和客户端的负载大小。
- 因为您需要序列化所有内容,所以“快速修复”(例如在
<script>
块中序列化整个实体供以后使用)可能会导致延迟加载“意外”,因为序列化程序将尝试触及所有导航属性。
- 将实体传回服务器以执行
Update()
意味着信任从客户端返回的数据。浏览器调试工具可以拦截表单提交或 Ajax POST 并篡改有效负载中的数据。这可能会导致意外篡改。 DbContext.Update
还会导致更新语句覆盖所有列,无论是否发生任何更改,其中更改跟踪的实体将仅构建更新语句以包含只有在实际发生任何更改时才实际更改的值。
我是 C# 和 .NET 核心的新手。
我想知道为什么在使用绑定编辑模型时,它会创建一个新模型并绑定 post 中的属性。但是,如果您没有将 post 形式的所有字段都隐藏起来,那么绑定会使它们无效吗?
它不应该加载一个模型并更新绑定参数并让它们单独存在吗?
例如,如果我正在更新一个人,而这个人有
Id, Name, Age, updated, Created
Edit(int id, [Bind("Id,Name,Age") Person p]
当我转到 _context.update(p)
时,它会将 updated
和 Created
归零,因为它们没有绑定。
为什么会这样?
如何让它只更新绑定的参数而不清空不需要加载的参数?
您传入的是 MVC 映射到实体定义中的反序列化数据块。它不会自动神奇地打开 DbContext、加载实体和覆盖值,它只是创建实体的实例,并复制值。其他一切都将保留为默认值。
作为一般规则,我建议不要将实体从客户端传递到服务器,以免混淆返回的内容。执行更新时,接受具有适用属性的视图模型进行更新,理想情况下,数据模型和视图模型应包含行版本号。 (即 SQL 服务器中的时间戳列,可以将 to/from Base64 字符串转换为 ViewModel 中的 send/compare)
从那里开始,在执行更新时,您可以通过 ID 获取实体,比较时间戳,然后可以利用 Automapper 处理将数据从 ViewModel 复制到实体中,或者手动复制值。
这样,当您的 ViewModel 返回要更新的数据时:
using (var context = new AppDbContext())
{
var entity = context.Persons.Single(x => x.Id == id);
if (entity.TimeStampBase64 != viewModel.TimestampBase64)
{
// Handle fact that data has changed since the client last loaded it.
}
Mapper.Map(entity, viewModel);
context.SaveChanges();
}
您可以按原样使用实体定义并仍然加载现有实体并使用 Automapper 将值从传输实体 class 复制到 DbContext 跟踪实体,但是最好避免混淆实体实例在跟踪的“真实”实体实例和可能不完整的未跟踪传输实例之间。代码通常会有接受实体作为参数的方法来执行验证、计算等操作,如果稍后这些方法假设它们将获得“真实”实体而不是在只有瞬态 DTO 风格的地方被调用,它可能会变得混乱一个实体。
接收实体并用它调用 DbContext.Update(entity)
似乎更简单,但这会给您带来许多问题,包括:
- 您需要将有关实体的所有内容传递给客户端,以便客户端可以将其发送回服务器。这需要隐藏输入或序列化页面中的整个实体,从而向浏览器公开有关您的域模型的更多信息。这会增加客户端和客户端的负载大小。
- 因为您需要序列化所有内容,所以“快速修复”(例如在
<script>
块中序列化整个实体供以后使用)可能会导致延迟加载“意外”,因为序列化程序将尝试触及所有导航属性。 - 将实体传回服务器以执行
Update()
意味着信任从客户端返回的数据。浏览器调试工具可以拦截表单提交或 Ajax POST 并篡改有效负载中的数据。这可能会导致意外篡改。DbContext.Update
还会导致更新语句覆盖所有列,无论是否发生任何更改,其中更改跟踪的实体将仅构建更新语句以包含只有在实际发生任何更改时才实际更改的值。