gpt4 book ai didi

c# - Entity Framework :Linq查询按原始数据查找条目,但返回对已更改条目的引用

转载 作者:搜寻专家 更新时间:2023-10-30 20:02:21 29 4
gpt4 key购买 nike

我刚刚花了几天时间发现了一个由实体框架(4.4.0.0版)的一些奇怪行为引起的bug。为了解释,我写了一个小测试程序。最后你会发现我对此有些疑问。
宣言
这里我们有一个类“test”,它表示我们的测试数据集。它只有一个id(主键)和一个“value”属性。在我们的testcontext中,我们实现了一个dbset测试,它将把我们的“test”对象作为数据库表处理。

public class Test
{
public int ID { get; set; }
public int value { get; set; }
}

public class TestContext : DbContext
{
public DbSet<Test> Tests { get; set; }
}

初始化
现在,我们从“测试”表中删除任何(如果存在的)条目,并添加我们唯一的“测试”对象。它的id=1(主键)和value=10。
// Create a new DBContext...
TestContext db = new TestContext();

// Remove all entries...
foreach (Test t in db.Tests) db.Tests.Remove(t);
db.SaveChanges();

// Add one test entry...
db.Tests.Add(new Test { ID = 1, value = 10 });
db.SaveChanges();

测验
最后,我们进行了一些测试。我们根据原始值(=10)选择条目,并将条目的“值”更改为4711。但是,我们不调用db.savechanges();!!!
// Find our entry by it's value (=10)
var result = from r in db.Tests
where r.value == 10
select r;
Test t2 = result.FirstOrDefault();

// change its value from 10 to 4711...
t2.value = 4711;

现在,我们尝试按原始值(=10)查找(旧)条目,并对其结果进行一些测试。
// now we try to select it via its old value (==10)
var result2 = from r in db.Tests
where r.value == 10
select r;

// Did we get it?
if (result2.FirstOrDefault() != null && result2.FirstOrDefault().value == 4711)
{
Console.WriteLine("We found the changed entry by it's old value...");
}

当运行这个程序时,我们会看到“我们发现了按旧值修改的条目…”。这意味着我们运行了一个r.value==10的查询,找到了一些…这是可以接受的。但是,get接收已经更改的对象(不满足值=10)!!!
注意:您将得到“where r.value==4711”的空结果集。
在一些进一步的测试中,我们发现,实体框架总是发出对同一对象的引用。如果我们改变一个引用中的值,另一个引用中的值也会改变。好吧,没关系…但人们应该知道它发生了。
Test t3 = result2.FirstOrDefault();    
t3.value = 42;
if (t2.value == 42)
{
Console.WriteLine("Seems as if we have a reference to the same object...");
}

概要
在同一数据库上下文上运行LINQ查询(不调用saveChanges())时,如果同一对象具有相同的主键,我们将收到对该对象的引用。奇怪的是:即使我们改变了一个物体,我们也会找到它(只有!)按照旧的价值观。但我们将收到对已更改对象的引用。这意味着,对于自上次调用saveChanges()以来更改的任何条目,都不能保证查询中的限制(值==10)。
问题
当然,我可能要在这里生活一些影响。但我,想避免每次“savechanges()”之后的细微变化。尤其是,因为我想用它来处理事务…如果出了问题,可以还原一些更改。
如果有人能回答我以下一个甚至两个问题,我将非常高兴:
是否有可能将实体框架的行为更改为在事务期间与普通数据库通信的方式?如果是的话…怎么做?
哪里是回答“如何使用实体框架的上下文”的好资源?哪一个回答了“我能依靠什么?”以及“如何选择dbcontext对象的作用域”?
编辑1
Richard刚刚解释了如何访问原始(未更改的)数据库值。虽然这是有价值的和有帮助的,我有强烈的欲望澄清目标…
让我们看看使用sql时会发生什么。我们建立了一个“测试”表:
CREATE TABLE Tests (ID INT, value INT, PRIMARY KEY(ID));
INSERT INTO Tests (ID, value) VALUES (1,10);

然后我们有一个事务,它首先查找值为10的实体。之后,我们更新这些条目的值并再次查找这些条目。在sql中,我们已经在处理更新的版本,因此我们不会为第二个查询找到任何结果。毕竟我们做了一个“回滚”,所以我们的条目的值应该是10…
START TRANSACTION;

SELECT ID, value FROM Tests WHERE value=10; {1 result}
UPDATE Tests SET value=4711 WHERE ID=1; {our update}

SELECT ID, value FROM Tests WHERE value=10; {no result, as value is now 4711}

ROLLBACK; { just for testing transactions... }

我希望在实体框架(ef)中有这样的行为,其中db.savechanges();相当于“commit”,其中所有linq查询都相当于“select”语句,对实体的每个写访问都像一个“update”。我不关心ef何时真正调用update语句,但它的行为应该与直接使用sql数据库的方式相同…当然,如果调用了“saveChanges()”并成功返回,则应确保所有数据都正确持久化。
注意:是的,我可以在每次查询之前调用db.saveChanges(),但这样就不可能进行“回滚”。
当做,
斯特凡

最佳答案

您可以将更新打包到事务中,以获得与SQL示例中相同的结果:

using (var transaction = new TransactionScope())
{

var result = from r in db.Tests
where r.value == 10
select r;
Test t2 = result.FirstOrDefault();

// change its value from 10 to 4711...
t2.value = 4711;
// send UPDATE to Database but don't commit transcation
db.SaveChanges();

var result2 = from r in db.Tests
where r.value == 10
select r;
// should not return anything
Trace.Assert(result2.Count() == 0);

// This way you can commit the transaction:
// transaction.Complete();

// but we do nothing and after this line, the transaction is rolled back
}

有关更多信息,请参见 http://msdn.microsoft.com/en-us/library/bb896325(v=vs.100).aspx

关于c# - Entity Framework :Linq查询按原始数据查找条目,但返回对已更改条目的引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13138243/

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