我一直在寻找在断开连接的情况下记录实体的更改。
原始问题:我调用 DbContext.Update(Entity)
在更新的复杂实体上,即使没有任何变化,所有内容都被 ChangeTracker 和我的并发标记为已更改 行版本 列数。
POST 用户没有更改
"userSubPermissions": [],
"fidAdresseNavigation": {
"idAdresse": 1,
"fidPlzOrt": 3,
"strasse": "Gerold Str.",
"hausnr": "45",
"rowVersion": 10,
"isDeleted": 0,
"fidPlzOrtNavigation": {
"idPlzOrt": 3,
"plz": "52062",
"ort": "Aachen",
"rowVersion": 9,
"isDeleted": 0
}
},
"idUser": 35,
"fidAnrede": null,
"fidAdresse": 1,
"fidAspnetuser": "a7ab78be-859f-4735-acd1-f06cd832be7e",
"vorname": "Max",
"nachmname": "Leckermann",
"eMail": "kunde@leasing.de",
"rowVersion": 11,
"isDeleted": 0
POST 返回用户
"userSubPermissions": [],
"fidAdresseNavigation": {
"idAdresse": 1,
"fidPlzOrt": 3,
"strasse": "GeroldPenis Str.",
"hausnr": "45",
"rowVersion": 11,
"isDeleted": 0,
"fidPlzOrtNavigation": {
"idPlzOrt": 3,
"plz": "52062",
"ort": "Aachen",
"rowVersion": 10,
"isDeleted": 0
}
},
"idUser": 35,
"fidAnrede": null,
"fidAdresse": 1,
"fidAspnetuser": "a7ab78be-859f-4735-acd1-f06cd832be7e",
"vorname": "Max",
"nachmname": "Leckermann",
"eMail": "kunde@leasing.de",
"rowVersion": 12,
"isDeleted": 0
RowVersion 逻辑位于 数据库上下文 并且仅在修改实体状态时更改行版本。
foreach (var entity in ChangeTracker.Entries().Where(e => e.State == EntityState.Modified))
{
var saveEntity = entity.Entity as ISavingChanges;
saveEntity.OnSavingChanges();
}
根据我过去 2 天的调查,EF Core 似乎有两种选择。
1.- 如本文和许多类似帖子所述,您可以使用 TrackGraph https://www.mikesdotnetting.com/article/303/entity-framework-core-trackgraph-for-disconnected-data
问题:此解决方案在实体 ID 存在时将整个实体标记为已更改,即使没有任何更改。我得到与上述完全相同的结果,行版本 计数意味着我的实体及其所有属性已被标记为已修改。
2.- 如本文所述
https://blog.tonysneed.com/2017/10/01/trackable-entities-for-ef-core/您可以下载 NuGet 包并实现 可合并 和 可追踪
我的实体上的接口(interface)。
问题:客户端需要跟踪模型中的更改并将它们传递给 API,我想避免这种情况,因为 Angular 似乎没有为此提供好的解决方案。
3.- 书中有另一种解决方案编程 Entity Framework 关于 EF 6 如何处理断开连接的场景,通过记录原始值 第 4 章:记录原始值 .
当更新进入时,上下文从数据库中读取实体,然后将传入的实体与数据库实体进行比较,以查看是否对属性进行了任何更改,然后将它们标记为已修改。
https://www.oreilly.com/library/view/programming-entity-framework/9781449331825/ch04.html
问题:书中描述的一些代码在 EF Core 中是不可实现的一个例子。
((IObjectContextAdapter)this).ObjectContext
.ObjectMaterialized += (sender, args) =>
{
var entity = args.Entity as IObjectWithState;
if (entity != null)
{
entity.State = State.Unchanged;
entity.OriginalValues =
BuildOriginalValues(this.Entry(entity).OriginalValues);
}
};
必须有一种方法来实现这种巧妙的解决方案,在断开连接的场景中处理 API 而不是客户端的更改,否则,这可能是 EF Core 中非常有用的功能,因为大多数开发人员都将它用于断开连接的场景。
最佳答案
我的回复是使用 轨迹图
概要
我要只修改几列实体以及 添加\修改嵌套子实体
Scenario
实体和仅修改 ScenarioDate
. TempScenario
, 我正在添加一条新记录 Scenariostation
,我正在添加以及修改记录public partial class Scenario
{
public Scenario()
{
InverseTempscenario = new HashSet<Scenario>();
Scenariostation = new HashSet<Scenariostation>();
}
public int Scenarioid { get; set; }
public string Scenarioname { get; set; }
public DateTime? Scenariodate { get; set; }
public int Streetlayerid { get; set; }
public string Scenarionotes { get; set; }
public int? Modifiedbyuserid { get; set; }
public DateTime? Modifieddate { get; set; }
public int? Tempscenarioid { get; set; }
public virtual Scenario Tempscenario { get; set; }
public virtual ICollection<Scenario> InverseTempscenario { get; set; }
public virtual ICollection<Scenariostation> Scenariostation { get; set; }
}
public partial class Scenariostation
{
public Scenariostation()
{
Scenariounit = new HashSet<Scenariounit>();
}
public int Scenariostationid { get; set; }
public int Scenarioid { get; set; }
public int Stationid { get; set; }
public bool? Isapplicable { get; set; }
public int? Createdbyuserid { get; set; }
public int? Modifiedbyuserid { get; set; }
public DateTime? Modifieddate { get; set; }
public virtual Scenario Scenario { get; set; }
public virtual Station Station { get; set; }
}
public partial class Station
{
public Station()
{
Scenariostation = new HashSet<Scenariostation>();
}
public int Stationid { get; set; }
public string Stationname { get; set; }
public string Address { get; set; }
public NpgsqlPoint? Stationlocation { get; set; }
public int? Modifiedbyuserid { get; set; }
public DateTime? Modifieddate { get; set; }
public virtual ICollection<Scenariostation> Scenariostation { get; set; }
}
Usual Approach
不管用通常的方法
public virtual void Update(T entity)
{
if (entity == null)
throw new ArgumentNullException("entity");
var returnEntity = _dbSet.Attach(entity);
_context.Entry(entity).State = EntityState.Modified;
}
但是,如果您使用此 DbContext.Entry(entity).EntityState = EntityState.IsModified
,则断开连接的 EF Core 更新会出现问题。 ,所有列都会更新。因此某些列将更新为其默认值,即 null 或默认数据类型值。
此外,
ScenarioStation
的一些记录根本不会更新,因为实体状态将是 UnChanged
.所以为了仅更新列 从客户端发送的,不知何故需要告知 EF Core .
使用 ChangeTracker.TrackGraph
最近我发现了这个
DbConetxt.ChangeTracker.TrackGraph
可用于标记Added
的方法, UnChanged
实体的状态。difference is that with
TrackGraph
, you can add custom logic, as it iteratively navigates through Navigation properties of the entity.
我使用 TrackGraph 的自定义逻辑
public virtual void UpdateThroughGraph(T entity, Dictionary<string, List<string>> columnsToBeUpdated)
{
if (entity == null)
throw new ArgumentNullException("entity");
_context.ChangeTracker.TrackGraph(entity, e =>
{
string navigationPropertyName = e.Entry.Entity.GetType().Name;
if (e.Entry.IsKeySet)
{
e.Entry.State = EntityState.Unchanged;
e.Entry.Property("Modifieddate").CurrentValue = DateTime.Now;
if (columnsToBeUpdated.ContainsKey(navigationPropertyName))
{
foreach (var property in e.Entry.Properties)
{
if (columnsToBeUpdated[e.Entry.Entity.GetType().Name].Contains(property.Metadata.Name))
{
property.IsModified = true;
}
}
}
}
else
{
e.Entry.State = EntityState.Added;
}
});
}
使用这种方法,我能够轻松处理任何嵌套子实体及其列的仅必需的列更新以及新的添加/修改。
关于c# - EF Core 中的断开连接的实体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55844343/