gpt4 book ai didi

c# - 禁用自动更改检测会在EF中引起哪些错误?

转载 作者:太空狗 更新时间:2023-10-29 17:37:53 24 4
gpt4 key购买 nike

最近,我通过禁用自动更改检测(Context.Configuration.AutoDetectChangesEnabled = false)来调整运行缓慢的应用程序的一部分,然后再进行批量删除,然后重新启用它并保存更改。

我读了几个不同的资料,从本质上讲,每当我在DbSet上调用诸如.Add().Remove()之类的方法时,就会调用DetectChanges(),而当我们处理大量实体时,这可能会变得昂贵。好的。

现在,我想特别注意这些文章:

Entity Framework Automatic Detect Changes (MSDN)

An alternative to disabling and re-enabling is to leave automatic detection of changes turned off at all times and either call context.ChangeTracker.DetectChanges explicitly or use change tracking proxies diligently. Both of these options are advanced and can easily introduce subtle bugs into your application so use them with care.



Secrets of Detect Changes: Part 3

Don’t turn off automatic DetectChanges unless you really need to; it will just cause you pain.



也许它在我眼前,但是假设例如,我将 .SaveChanges()包装在一个始终始终首先称为 DetectChanges()的方法中,那么我会遇到哪些我通常不会遇到的错误?我所看到的所有警告都隐约表明,糟糕的事情可能发生而无需深入了解。

最佳答案

假设我们具有以下BankAccount s和Deposit s模型-一个简单的一对多关系:BankAccount有一个Deposit s集合,并且Deposit属于单个BankAccount:

public class BankAccount
{
public int Id { get; set; }
public int AccountNumber { get; set; }
public string Owner { get; set; }
public ICollection<Deposit> Deposits { get; set; }
}

public class Deposit
{
public int Id { get; set; }
public decimal Value { get; set; }

public int BankAccountId { get; set; }
public BankAccount BankAccount { get; set; }
}

和一个简单的数据库上下文:
public class MyContext : DbContext
{
public DbSet<BankAccount> BankAccounts { get; set; }
public DbSet<Deposit> Deposits { get; set; }
}

约翰·史密斯先生想要在我们的银行拥有两个帐户,并向他的第一个帐户支付一笔1.000.000美元的 margin 。我们银行的程序员可以像这样完成这项任务:
using (var ctx = new MyContext())
{
var bankAccount123 = new BankAccount
{
AccountNumber = 123,
Owner = "John Smith",
Deposits = new List<Deposit> { new Deposit { Value = 1000000m } }
};
var bankAccount456 = new BankAccount
{
AccountNumber = 456,
Owner = "John Smith"
};

ctx.BankAccounts.Add(bankAccount123);
ctx.BankAccounts.Add(bankAccount456);

ctx.SaveChanges();
}

它像预期的那样工作:

一天后,史密斯先生给银行打了个电话:“我改变了主意。我不希望这两个帐户,只有一个,一个帐号为456,我更喜欢这个号码。我的123帐户上有100万美元。请将它们移到帐户456,然后删除我的帐户123!”

我们的程序员听说删除是危险的事情,因此决定将数据库复制到测试环境中,并首先测试他现在编写的新例程,以便遵循Smith先生的要求:
using (var ctx = new MyContext())
{
var bankAccount123 = ctx.BankAccounts.Include(b => b.Deposits)
.Single(b => b.AccountNumber == 123);
var bankAccount456 = ctx.BankAccounts
.Single(b => b.AccountNumber == 456);
var deposit = bankAccount123.Deposits.Single();

// here our programmer moves the deposit to account 456 by changing
// the deposit's account foreign key
deposit.BankAccountId = bankAccount456.Id;

// account 123 is now empty and can be deleted safely, he thinks!
ctx.BankAccounts.Remove(bankAccount123);

ctx.SaveChanges();
}

他运行了测试,并且可以正常工作:

在将代码投入生产之前,他决定增加一点性能,但是-当然-不会更改测试逻辑来转移存款和删除帐户:
using (var ctx = new MyContext())
{
// he added this well-known line to get better performance!
ctx.Configuration.AutoDetectChangesEnabled = false;

var bankAccount123 = ctx.BankAccounts.Include(b => b.Deposits)
.Single(b => b.AccountNumber == 123);
var bankAccount456 = ctx.BankAccounts
.Single(b => b.AccountNumber == 456);
var deposit = bankAccount123.Deposits.Single();

deposit.BankAccountId = bankAccount456.Id;

ctx.BankAccounts.Remove(bankAccount123);

// he heard this line would be required when AutoDetectChanges is disabled!
ctx.ChangeTracker.DetectChanges();
ctx.SaveChanges();
}

在完成日常工作之前,他会在生产环境中运行代码。

第二天,史密斯先生给银行打电话:“我需要从我的456帐户中拿出50万!”客服人员说:“对不起,先生,但是您的帐户456上没有钱。”史密斯先生:“啊,好了,他们还没有转移这笔钱。然后,请从我的123帐户拿走这笔钱!” “抱歉,先生,但是您没有123帐户!”史密斯先生:“怎么了?”客户服务:“我可以在我的银行工具中看到您的所有帐户和存款,而您的单个帐户456上没有任何内容:”



当我们的程序员增加了一点点性能提升并使史密斯先生成为可怜的人时,出了什么问题?

AutoDetectChangesEnabled设置为 false后,行为不同的重要行是 ctx.BankAccounts.Remove(bankAccount123);。现在,此行不再在内部调用 DetectChanges。结果是EF无法获得有关 BankAccountId实体中外键 deposit的更改的知识(发生在调用 Remove之前)。

启用更改检测后, Remove将根据更改的外键(“关系修复”)调整整个对象图,即 deposit.BankAccount将被设置为 bankAccount456,该 deposit将被从 bankAccount123.Deposits集合中删除并添加到 bankAccount456.Deposits集合中。

因为这没有发生,所以 Remove将父 bankAccount123标记为 Deleted,并将 deposit(仍然是 bankAccount123.Deposits集合中的一个子项)也置于 Deleted状态。调用 SaveChanges时,两者都会从数据库中删除。

尽管该示例看起来有些虚构,但我记得在禁用真实代码中的更改检测后,我也遇到了类似的“错误”,这需要一些时间来查找和理解。主要问题在于,即使变更后的代码未做任何更改,仍可以正常运行且已通过变更检测进行测试的代码可能不再起作用,并且在禁用变更检测后需要再次进行测试。也许必须修改代码以使其再次正常工作。 (在我们的示例中,程序员必须在 ctx.ChangeTracker.DetectChanges();行之前添加 Remove来修复该错误。)

这是MSDN页面正在谈论的可能的“细微错误”之一。可能还有更多。

关于c# - 禁用自动更改检测会在EF中引起哪些错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23636920/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com