使用 EF 与分离实体更新记录的正确方法

问题描述 投票:0回答:1

当我觉得它应该非常简单时,我真的很难让它工作。

我已向服务层添加了一个缓存,用于存储从 EF 返回的数据。

当实体从 EF 返回并更新时,它的状态为

EntityState.Modified

当它从缓存返回时,它的状态是

EntityState.Detached
,这都是有意义的。

如果实体是

DbContext.Update
,我会调用
Detached
,并且这对于所有非导航属性都适用。

对于跟踪的实体,我可以直接更新 FK ID 属性,但如果它一开始是分离的,则必须通过导航属性进行更新。由于某种原因,这也会导致为每个相关实体运行

UPDATE
语句,而不仅仅是我期望的多对多表。

被跟踪实体:

// This will save to the DB
job.PrimaryAccountId = 123;

初始分离的实体:

// This will NOT save to the DB
job.PrimaryAccountId = 123;

// This will not save to the DB
job.PrimaryAccount.AccountId = 123;

执行上述任一操作后更新并保存:

await _dataAccess.Update(job);
await _dataAccess.SaveChangesAsync();
c# entity-framework-core ef-core-8.0
1个回答
0
投票

此操作应被视为非法,因此如果该实例被跟踪,则在缓存实例中执行此操作将非常糟糕。

job.PrimaryAccount.AccountId = 123;

您的目标是将作业关联到不同的帐户,而不是更新现有帐户以更改其 ID。 (如果是PK,那是不允许的)

处理分离的实体可能会变得混乱。处理分离的实体图(相关实体)肯定会变得更加混乱。

如果您已加载作业及其关联的主帐户,并在缓存中分离了这些引用,则重新附加这些引用将导致问题。例如,如果我执行以下操作:

var job = _context.Jobs.Include(x => x.PrimaryAccount).Single(x => x.JobId == jobId);

...然后我缓存了它,并且一些代码更新了作业:

var job = _cachedJobs.First(x => x.JobId == jobId);
job.PrimaryAccountId = newAccountId;

现在我们遇到了问题。该作业有一个指向旧帐户的导航属性,但我们更改了作业的 FK。如果要更新分离的实体,则需要先删除所有导航属性引用,然后再调用

Update
:

var job = _cachedJobs.First(x => x.JobId == jobId);
job.PrimaryAccount = null;
job.PrimaryAccountId = newAccountId;

..从这里您可以致电:

_context.Update(job);

最终我不建议在外部缓存实体,而是按需加载实体并更新其跟踪的引用。缓存带来的问题比它看起来能解决的问题还要多。

Update()
会自动覆盖实体的所有值,而不仅仅是更改的值,并且即使实际上没有任何更改,也会执行
UPDATE
语句。您还会使系统面临过时数据覆盖或更频繁的并发异常。

引入缓存的理由几乎总是为了避免额外的数据库往返。这对于读取操作当然很有价值,但不应该成为更新操作的一个因素。获取单个实体,甚至它的相关数据都非常快,有助于检测和避免陈旧数据覆盖,并通过更改跟踪器提高更新效率,仅针对更改的值(如果确实有任何更改)生成更新语句。

© www.soinside.com 2019 - 2024. All rights reserved.