实体框架:Linq查询通过原始数据发现项目,但返回的引用,改变进入 [英] Entity Framework: Linq query finds entries by original data but returns reference to changed entry

查看:322
本文介绍了实体框架:Linq查询通过原始数据发现项目,但返回的引用,改变进入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚刚花了一些日子已经找到引起实体框架的一些奇怪的行为(4.4.0.0版本)中的错误。对于一个解释,我写了一个小测试程序。在最后,你会发现一些问题我想过这事。

I just spent some days now to find a bug caused by some strange behavior of the Entity Framework (version 4.4.0.0). For an explanation I wrote a small test program. At the end you'll find some questions I have about that.

下面我们有一个类测试,它代表了我们的测试数据集。它只有一个ID(主键)和一个值属性。在我们的TestContext我们实现了一个DbSet试验,应处理我们的测试对象作为数据库表。

Here we have a class "Test" which represents our test dataset. It only has an ID (primary key) and a "value" property. In our TestContext we implement a DbSet Tests, which shall handle our "Test" objects as a database table.

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(主键)和值= 10。

Initialization

Now, we remove any (if existent) entries from our "Tests" table and add our one and only "Test" object. It has ID=1 (primary key) and 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(); !

Tests

Finally, we run some tests. We select our entry by it's original value (=10) and we change the "value" of our entry to 4711. BUT, we do not call 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 find the (old) entry by the original value (=10) and do some tests on the results of that.

// 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的查询,找到了......这是可以接受的。但拿到收到已经改变的对象(不履行值== 10)!

When running the program we'll actually see "We found the changed entry by it's old value...". That means we have run a query for r.value == 10, found something... This would be acceptable. BUT, get receive the already changed object (not fulfilling value == 10)!!!

注意:您会得到一个空的结果对于设置里r.value == 4711。

Note: You'll get an empty result set for "where r.value == 4711".

在一些进一步的测试,我们发现,实体框架总是手了同一个对象的引用。如果我们在一个参考更改值,它在另外一个人的变化太大。好吧,没关系......但应该知道它发生

In some further testing, we find out, that the Entity Framework always hands out a reference to the same object. If we change the value in one reference, it's changed in the other one too. Well, that's ok... but one should know it happens.

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()),我们将在收到引用同一个对象,如果有相同的主键。奇怪的是:即使,如果我们改变一个对象,我们会发现它通过它的旧值(仅!)。但是,我们将在收到对已经改变对象的引用。这意味着,在我们的查询的限制(价值== 10)不能保证,因为我们的SaveChanges()的最后一个电话,我们更改的条目。

Summary

When running a LINQ query on the same Database Context (without calling SaveChanges()) we will receive references to the same object, if it has the same primary key. The strange thing is: Even, if we change an object we will find it (only!) by it's old values. But we will receive a reference to the already changed object. This means that the restrictions in our query (value == 10) is not guaranteed for any entries that we changed since our last call of SaveChanges().

当然,我可能要住在这里有一定的影响。但我,想避免的SaveChanges()后,每变化不大。特别是,因为我想用它来做事务处理......要能够恢复一些变化,如果出现错误。

Of course, I'll probably have to live with some effects here. But I, would like to avoid to "SaveChanges()" after every little change. Especially, because I would like to use it for transaction handling... to be able to revert some changes, if something goes wrong.

我会很高兴,如果有人能回答我一个甚至两个以下问题:

I would be glad, if anyone could answer me one or even both of the following questions:


  1. 有没有改变实体框架的行为,工作可能就好像我会一个交易过程中,一个正常的数据库进行通信?如果是这样的......怎么办呢?

  1. Is there a possibility to change the behavior of entity framework to work as if I would communicate with a normal database during a transaction? If so... how to do it?

哪里是回答一个很好的资源如何使用实体框架的背景下?它回答的问题,如我能靠什么?和如何选择我的DbContext对象的范围?

Where is a good resource for answering "How to use the context of entity framework?" which answers questions like "What can I rely on?" and "How to choose the scope of my DBContext object"?

理查德刚解释如何访问原始(不变)数据库值。虽然这是有价值的,有用的我有冲动明确的目标...

EDIT #1

Richard just explained how to access the original (unchanged) database values. While this is valuable and helpful I've got the urge to clarify the goal ...

让我们看看在使用SQL时会发生什么。我们建立一个表测试:

Let's have a look at what happens when using SQL. We setup a table "Tests":

CREATE TABLE Tests (ID INT, value INT, PRIMARY KEY(ID));
INSERT INTO Tests (ID, value) VALUES (1,10);



然后我们有一个交易,即首先查找其值是10,在此之后的实体,我们更新这些条目的值,并且再次寻找那些条目。在SQL中,我们已经在更新版本的工作,所以我们不会找到我们的第二个查询任何结果。毕竟我们做了一个回退,所以我们的项的值会恢复为10 ...

Then we have a transaction, that first looks for entities whose values are 10. After this, we update the value of these entries and look again for those entries. In SQL we already work on the updated version, so we will not find any results for our second query. After all we do a "rollback", so the value of our entry should be 10 again...

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查询相当于选择的语句和一个实体的每个写访问就像一个UPDATE。我不关心,当EF没有实际调用UPDATE语句,而是使用SQL数据库的直接方式也应做同样的方式。当然,如果调用SaveChanges()之称,并成功返回它应该。保证所有的数据被正确坚持

I would like to have exactly this behavior for the Entity Framework (EF), where db.SaveChanges(); is equivalent to "COMMIT", where all LINQ queries are equivalent to "SELECT" statements and every write access to an entity is just like an "UPDATE". I don't care about when the EF does actually calls the UPDATE statement, but it should behave the same way as using a SQL Database the direct way... Of course, if "SaveChanges()" is called and returning successfully it should be guaranteed that all data was persisted correctly.

请注意:是的,我可以每个查询之前调用db.SaveChanges(),但然后我会宽松的可能性回滚。

Note: Yes, I could call db.SaveChanges() before every query, but then I would loose the possibility for a "Rollback".

问候,

斯特凡

推荐答案

您可以包装在一个事务的更新才达到相同的结果,在SQL例如:

You can wrap your updates in a transaction to achive the same result as in your SQL example:

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

For more information see http://msdn.microsoft.com/en-us/library/bb896325(v=vs.100).aspx

这篇关于实体框架:Linq查询通过原始数据发现项目,但返回的引用,改变进入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆