简体   繁体   English

实体框架中出现意外行为

[英]Unexpected behavior in entity framework

I ran into what I think is a really odd situation with entity framework. 我遇到了我认为实体框架非常奇怪的情况。 Basically, if I update an row directly with a sql command, when I retrive that row through linq it doesn't have the updated information. 基本上,如果我使用sql命令直接更新行,当我通过linq检索该行时,它没有更新的信息。 Please see the below example for more information. 有关更多信息,请参阅以下示例。

First I created a simple DB table 首先,我创建了一个简单的DB表

CREATE TABLE dbo.Foo (
    Id int NOT NULL PRIMARY KEY IDENTITY(1,1),
    Name varchar(50) NULL
) 

Then I created a console application to add an object to the DB, update it with a sql command and then retrieve the object that was just created. 然后我创建了一个控制台应用程序,用于向DB添加对象,使用sql命令更新它,然后检索刚刚创建的对象。 Here it is: 这里是:

public class FooContext : DbContext
{

    public FooContext() : base("FooConnectionString")
    {

    }

    public IDbSet<Foo> Foo { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Foo>().ToTable("Foo");
        base.OnModelCreating(modelBuilder);
    }

}

public class Foo
{
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Program
{
    static void Main(string[] args)
    {

        //setup the context
        var context = new FooContext();

        //add the row
        var foo = new Foo()
        {
            Name = "Before"
        };
        context.Foo.Add(foo);
        context.SaveChanges();

        //update the name
        context.Database.ExecuteSqlCommand("UPDATE Foo Set Name = 'After' WHERE Id = " + foo.Id);

        //get the new foo
        var newFoo = context.Foo.FirstOrDefault(x => x.Id == foo.Id);

        //I would expect the name to be 'After' but it is 'Before'
        Console.WriteLine(string.Format("The new name is: {0}", newFoo.Name));
        Console.ReadLine();

    }

}

The write line at the bottom prints out "Before" however I would expect that it prints out "After". 底部的写行打印出“之前”,但我希望它打印出“之后”。 The odd thing about it is that if I run profiler I see the sql query run and if I run the query in management studio myself, it returns "After" as the name. 关于它的奇怪之处在于,如果我运行探查器,我会看到sql查询运行,如果我自己在管理工作室中运行查询,则返回“After”作为名称。 I am running sql server 2014. 我正在运行SQL Server 2014。

Can someone please help me understand what is going on here? 有人可以帮我理解这里发生了什么吗?

UPDATE: 更新:

It is going to the database on the FirstOrDefault line. 它将转到FirstOrDefault行的数据库。 Please see the attached screen shot from sql profiler. 请参阅sql profiler附带的屏幕截图。

在此输入图像描述

So my question really is this: 所以我的问题是这样的:

1) If it is caching, shouldn't it not be going to the DB? 1)如果是缓存,不应该不去DB吗? Is this a bug in EF? 这是EF中的一个错误吗?

2) If it is going to the db and spending the resources, shouldn't EF update the object. 2)如果要进入db并花费资源,不应该更新EF对象。

FooContext includes change tracking and caching, so the in-memory object that is returned from your query is the same instance that you added earlier. FooContext包括更改跟踪和缓存,因此从查询返回的内存中对象与先前添加的实例相同。 Calling SaveChanges() does clear the context and FooContext is not aware of the changes that happened underneath it in the database. 调用SaveChanges()确实清除了上下文,并且FooContext不知道数据库中它下面发生的更改。

This is usually a good thing -- not making expensive database calls for every operation. 这通常是一件好事 - 不会为每个操作进行昂贵的数据库调用。

In your sample, try making the same query from a new FooContext , and you should see "After". 在您的示例中,尝试从新的FooContext进行相同的查询,您应该看到“After”。

update 更新

Responding to your updated question, yes, you are right. 回答您的更新问题,是的,您是对的。 I missed before that you were using FirstOrDefault() . 在你使用FirstOrDefault()之前我错过了。 If you were using context.Find(foo.Id) , as I wrongly assumed, then there would be no query. 如果您正在使用context.Find(foo.Id) ,正如我错误地假设的那样,那么就没有查询。

As for why the in-memory object is not updated to reflect the change in the database, I'd need to do some research to do anything more than speculate. 至于为什么内存中的对象没有更新以反映数据库中的变化,我需要做一些研究来做更多的事情而不是推测。 That said, here is my speculation: 那就是说,这是我的猜测:

  • An instance of the database context cannot return more than one instance of the same entity. 数据库上下文的实例不能返回同一实体的多个实例。 Within a unit of work, we must be able to rely on the context to return the same instance of the entity. 在一个工作单元内,我们必须能够依赖上下文来返回实体的同一个实例。 Otherwise, we might query by different criteria and get 3 objects representing the same conceptual entity. 否则,我们可能会按不同的标准进行查询,并获得表示同一概念实体的3个对象。 At that point, how can the context deal with changes to any of them? 那时,上下文如何处理其中任何一个的变化? What if the name is changed to a different value on two of them and then SaveChanges() is called -- what should happen? 如果名称在其中两个上更改为不同的值然后调用SaveChanges()会怎样? - 会发生什么?
  • Given then that the context tracks at most a single instance of each entity, why can't EF just update that entity at the point at which a query is executed? 鉴于那时上下文最多跟踪每个实体的单个实例,为什么EF只能在执行查询时更新该实体? EF could even discard that change if there is a pending in-memory change, since it knows about those changes. 如果存在未决的内存中更改,EF甚至可以丢弃该更改,因为它知道这些更改。
    • I think one part of the answer is that diffing all the columns on large entities and in large result sets is performance prohibitive. 我认为答案的一部分是在大型实体和大型结果集中区分所有列是性能过高的。
    • I think a bigger part of the answer is that it executing a simple SELECT statement should not have the potential to cause side effects throughout the system. 我认为答案的一个重要部分是它执行一个简单的SELECT语句不应该有可能在整个系统中引起副作用。 Entities may be grouped or looped over by the value of some property and to change the value of that property at an indeterminate time and as a result of a SELECT query is highly unsound. 实体可以通过某个属性的值进行分组或循环,并在不确定的时间更改该属性的值,并且由于SELECT查询非常不健全。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM