自动检测断开实体的变化
Auto detection of changes with disconnected entities
我正在 Web 服务器上制作一个简单的编辑器,它允许用户 change/add 数据到存储在 MS SQL 服务器上的单个 table。
我正在使用 Entity Framework 6 来执行此操作,我想知道我应该如何跟踪对实体模型所做的更改。
我本来希望我可以在上下文中加载新数据,并让上下文自动与数据库中的内容进行比较,然后调用 SaveChanges()。
但是从我在网上读到的内容来看,我似乎需要遍历所有数据,并检查自己发生了什么变化,这样我就可以调用 Context.Entry(myEntry).State = Added
或 Context.Entry(myEntry).State = Modified
EF 有没有办法自动检测新增内容、修改内容和未更改内容?
我建议将 ViewModel 或 DTO 传递给视图,然后在提交时将它们映射回重新加载的实体。 EF 将自动仅更新在设置值时更改的值。在不更改值的情况下设置值不会触发更新。 (附加实体并设置其修改后的状态将更新 all 列)传递实体虽然方便,但比 UI 可能呈现的更多,并且可以在被送回之前被篡改。永远不要相信客户返回的任何信息。当序列化到客户端时,数据不再是一个实体,它是一个 JSON 数据块。当发送回服务器时,它不是被跟踪的实体,而是带有实体签名的 POCO。 EF 实体可以提供的任何更改跟踪都不会应用于客户端或继续存在 serialization/deserialization.
例如:
给定一个 Child,它有名字和出生日期。我们 select 一个 DTO 传递给视图。视图更改名称,我们取回 DTO 并复制 all 值,修改或以其他方式返回实体并调用 SaveChanges()
// For example, loading the child in the controller to pass to the view...
ChildDTO childDto = null;
using (var context = new TestDbContext())
{
childDto = context.Children
.Select(x => new ChildDto
{
ChildId = x.ChildId,
Name = x.Name,
BirthDte = x.BirthDate
}).Single(x => x.ChildId == 1);
}
// View updates just the name...
childDto.Name = "Luke";
// Example if the view passed DTO back to controller to update...
using (var context = new TestDbContext())
{
var child = context.Children.Single(x => x.ChildId == 1);
child.Name = childDto.Name;
child.BirthDate = childDto.BirthDate;
context.SaveChanges();
}
如果名字变了,出生日期没变,EF生成的更新语句只会更新名字。如果实体名称已经是“Luke”,则不会发出更新语句。您可以使用 SQL 探查器验证此行为,以查看 if/when/what SQL EF 发送到数据库。
Automapper 可以帮助简化此操作以将 DTO 返回到实体中:
var mappingConfig = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Child, ChildDTO>();
cfg.CreateMap<ChildDTO, Child>();
});
然后在阅读时,利用 ProjectTo
而不是 Select
:
using (var context = new TestDbContext())
{
childDto = context.Children
.ProjectTo<ChildDTO>(mappingConfig)
.Single(x => x.ChildId == 1);
}
... 更新实体时:
using (var context = new TestDbContext())
{
var child = context.Children.Single(x => x.ChildId == 1);
var mapper = mappingConfig.CreateMapper();
mapper.Map(childDto, child); // copies values from DTO to the entity instance.
context.SaveChanges();
}
在将值复制到实体之前验证 DTO 很重要,无论是手动还是使用 Automapper。也可以将 Automapper 配置设置为仅复制 expected/allowed 的值进行更改。
我正在 Web 服务器上制作一个简单的编辑器,它允许用户 change/add 数据到存储在 MS SQL 服务器上的单个 table。
我正在使用 Entity Framework 6 来执行此操作,我想知道我应该如何跟踪对实体模型所做的更改。
我本来希望我可以在上下文中加载新数据,并让上下文自动与数据库中的内容进行比较,然后调用 SaveChanges()。
但是从我在网上读到的内容来看,我似乎需要遍历所有数据,并检查自己发生了什么变化,这样我就可以调用 Context.Entry(myEntry).State = Added
或 Context.Entry(myEntry).State = Modified
EF 有没有办法自动检测新增内容、修改内容和未更改内容?
我建议将 ViewModel 或 DTO 传递给视图,然后在提交时将它们映射回重新加载的实体。 EF 将自动仅更新在设置值时更改的值。在不更改值的情况下设置值不会触发更新。 (附加实体并设置其修改后的状态将更新 all 列)传递实体虽然方便,但比 UI 可能呈现的更多,并且可以在被送回之前被篡改。永远不要相信客户返回的任何信息。当序列化到客户端时,数据不再是一个实体,它是一个 JSON 数据块。当发送回服务器时,它不是被跟踪的实体,而是带有实体签名的 POCO。 EF 实体可以提供的任何更改跟踪都不会应用于客户端或继续存在 serialization/deserialization.
例如:
给定一个 Child,它有名字和出生日期。我们 select 一个 DTO 传递给视图。视图更改名称,我们取回 DTO 并复制 all 值,修改或以其他方式返回实体并调用 SaveChanges()
// For example, loading the child in the controller to pass to the view...
ChildDTO childDto = null;
using (var context = new TestDbContext())
{
childDto = context.Children
.Select(x => new ChildDto
{
ChildId = x.ChildId,
Name = x.Name,
BirthDte = x.BirthDate
}).Single(x => x.ChildId == 1);
}
// View updates just the name...
childDto.Name = "Luke";
// Example if the view passed DTO back to controller to update...
using (var context = new TestDbContext())
{
var child = context.Children.Single(x => x.ChildId == 1);
child.Name = childDto.Name;
child.BirthDate = childDto.BirthDate;
context.SaveChanges();
}
如果名字变了,出生日期没变,EF生成的更新语句只会更新名字。如果实体名称已经是“Luke”,则不会发出更新语句。您可以使用 SQL 探查器验证此行为,以查看 if/when/what SQL EF 发送到数据库。
Automapper 可以帮助简化此操作以将 DTO 返回到实体中:
var mappingConfig = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Child, ChildDTO>();
cfg.CreateMap<ChildDTO, Child>();
});
然后在阅读时,利用 ProjectTo
而不是 Select
:
using (var context = new TestDbContext())
{
childDto = context.Children
.ProjectTo<ChildDTO>(mappingConfig)
.Single(x => x.ChildId == 1);
}
... 更新实体时:
using (var context = new TestDbContext())
{
var child = context.Children.Single(x => x.ChildId == 1);
var mapper = mappingConfig.CreateMapper();
mapper.Map(childDto, child); // copies values from DTO to the entity instance.
context.SaveChanges();
}
在将值复制到实体之前验证 DTO 很重要,无论是手动还是使用 Automapper。也可以将 Automapper 配置设置为仅复制 expected/allowed 的值进行更改。