使用 entity framework 更新后日期时间默认为空
Datetime default get null after update with entity framework
我有一个带有 DATETIME DEFAULT
字段的 table。
CREATE TABLE People
(
Id INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
Name VARCHAR(100) NOT NULL,
...
DtOccurrence DATETIME DEFAULT getDATE(),
);
使用 scaffolding
生成 Class 并使用 Entitity
生成控制器 + 视图。
默认 CRUD 工作正常,但如果我尝试更新寄存器,[DtOccurrence] 在数据库中得到 NULL。
如何解决?提前致谢
创建保存OK
更新 仅 [Name] 字段为数据库发送空 [DtOccurrence],而我自动生成的 class 没有此 [DtOccurrence] 字段:
更新:
控制器创建方法
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name")] People people)
{
if (ModelState.IsValid)
{
_context.Add(people);
await _context.SaveChangesAsync();
return RedirectToAction("Edit", "Pessoas", new { people.Id });
}
return View(people);
}
CONTROLLER编辑方式
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Name,")] People people)
{
if (id != people.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(people);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!PeopleExists(people.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(people);
}
自动生成class 脚手架
public partial class Pessoa
{
public Pessoa()
{
public int Id { get; set; }
public string Name { get; set; }
}
}
正如我在评论中提到的,虽然您向视图提供数据的初始请求是从数据库上下文中获得一个实体,但您在 Update 方法中返回的对象(人)不是 相同的实体,并且不与您的 DbContext 相关联。它是反序列化的副本。当它不包含 all 字段时调用 Update
将导致字段设置为 #null。从客户端用这样的分离实体调用 Update
也是对您的域进行未经授权更新的攻击媒介。 (调试工具/插件可以拦截对服务器的调用并以多种方式更改实体数据。)
public async Task<IActionResult> Edit(int id, [Bind("Id,Name,")] People people)
{
if (!ModelState.IsValid)
return View(people);
var dataPeople = await _context.People.SingleAsync(x => x.id == people.id);
dataPeople.name = people.name;
await _context.SaveChangesAsync(); // dataPeople is a tracked entity and will be saved, not people which is acting as a viewmodel.
return RedirectToAction(nameof(Index));
}
使用Update
将生成一个更新语句,其中实体上的所有字段都被覆盖。您可能决定将不完整的实体传递给视图,或将不完整的实体从视图传回,但 EF 不知道丢失了哪些数据,因为它不是 provided/changed,而不是已清除的数据,因此它更新一切。相反,您应该根据提供的 ID 从 DbContext 加载实体(如果未找到 ID 将出错),然后在调用 SaveChanges
之前设置要更改的被跟踪实体的属性。这确保生成的 SQL 更新语句仅包含您要更改的列。
作为一般规则,我建议使用视图模型 类 在服务器和客户端之间进行通信模型,以便清楚传递的数据实际是什么。在服务器和视图之间传递实体是一种反模式,它容易出现性能问题、序列化问题以及有意和无意的数据损坏。
其他验证应包括确保更改 complete/legal,并可能检查传递的模型和从数据库加载的数据之间的行版本号或上次修改日期,以确保它们匹配。当用户打开页面时,他们可能已经获得了记录的版本 #1。当他们最终提交表单时,如果数据库返回版本 #2,则表明其他人当时修改了该行。 (否则您将覆盖更改)
我有一个带有 DATETIME DEFAULT
字段的 table。
CREATE TABLE People
(
Id INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
Name VARCHAR(100) NOT NULL,
...
DtOccurrence DATETIME DEFAULT getDATE(),
);
使用 scaffolding
生成 Class 并使用 Entitity
生成控制器 + 视图。
默认 CRUD 工作正常,但如果我尝试更新寄存器,[DtOccurrence] 在数据库中得到 NULL。
如何解决?提前致谢
创建保存OK
更新 仅 [Name] 字段为数据库发送空 [DtOccurrence],而我自动生成的 class 没有此 [DtOccurrence] 字段:
更新: 控制器创建方法
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name")] People people)
{
if (ModelState.IsValid)
{
_context.Add(people);
await _context.SaveChangesAsync();
return RedirectToAction("Edit", "Pessoas", new { people.Id });
}
return View(people);
}
CONTROLLER编辑方式
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Name,")] People people)
{
if (id != people.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(people);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!PeopleExists(people.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(people);
}
自动生成class 脚手架
public partial class Pessoa
{
public Pessoa()
{
public int Id { get; set; }
public string Name { get; set; }
}
}
正如我在评论中提到的,虽然您向视图提供数据的初始请求是从数据库上下文中获得一个实体,但您在 Update 方法中返回的对象(人)不是 相同的实体,并且不与您的 DbContext 相关联。它是反序列化的副本。当它不包含 all 字段时调用 Update
将导致字段设置为 #null。从客户端用这样的分离实体调用 Update
也是对您的域进行未经授权更新的攻击媒介。 (调试工具/插件可以拦截对服务器的调用并以多种方式更改实体数据。)
public async Task<IActionResult> Edit(int id, [Bind("Id,Name,")] People people)
{
if (!ModelState.IsValid)
return View(people);
var dataPeople = await _context.People.SingleAsync(x => x.id == people.id);
dataPeople.name = people.name;
await _context.SaveChangesAsync(); // dataPeople is a tracked entity and will be saved, not people which is acting as a viewmodel.
return RedirectToAction(nameof(Index));
}
使用Update
将生成一个更新语句,其中实体上的所有字段都被覆盖。您可能决定将不完整的实体传递给视图,或将不完整的实体从视图传回,但 EF 不知道丢失了哪些数据,因为它不是 provided/changed,而不是已清除的数据,因此它更新一切。相反,您应该根据提供的 ID 从 DbContext 加载实体(如果未找到 ID 将出错),然后在调用 SaveChanges
之前设置要更改的被跟踪实体的属性。这确保生成的 SQL 更新语句仅包含您要更改的列。
作为一般规则,我建议使用视图模型 类 在服务器和客户端之间进行通信模型,以便清楚传递的数据实际是什么。在服务器和视图之间传递实体是一种反模式,它容易出现性能问题、序列化问题以及有意和无意的数据损坏。
其他验证应包括确保更改 complete/legal,并可能检查传递的模型和从数据库加载的数据之间的行版本号或上次修改日期,以确保它们匹配。当用户打开页面时,他们可能已经获得了记录的版本 #1。当他们最终提交表单时,如果数据库返回版本 #2,则表明其他人当时修改了该行。 (否则您将覆盖更改)