实体框架中出现意外行为

时间:2014-09-16 01:41:16

标签: c# linq entity-framework entity-framework-6

我遇到了我认为实体框架非常奇怪的情况。基本上,如果我使用sql命令直接更新行,当我通过linq检索该行时,它没有更新的信息。有关详细信息,请参阅以下示例。

首先我创建了一个简单的数据库表

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

然后我创建了一个控制台应用程序,用于向DB添加对象,使用sql命令更新它,然后检索刚刚创建的对象。这是:

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();

    }

}

底部的写行打印出“之前”但是我希望它打印出“After”。关于它的奇怪之处在于,如果我运行探查器,我会看到sql查询运行,如果我自己在管理工作室中运行查询,则返回“After”作为名称。我正在运行sql server 2014。

有人可以帮我理解这里发生了什么吗?

更新:

它将转到FirstOrDefault行的数据库。请参阅sql profiler附带的屏幕截图。

enter image description here

所以我的问题是:

1)如果是缓存,不应该不去DB吗?这是EF中的错误吗?

2)如果要进入数据库并花费资源,则不应该更新对象。

1 个答案:

答案 0 :(得分:6)

FooContext包括更改跟踪和缓存,因此从查询返回的内存中对象与之前添加的实例相同。调用SaveChanges()确实清除了上下文,FooContext不知道数据库中发生的更改。

这通常是一件好事 - 不会为每个操作进行昂贵的数据库调用。

在您的示例中,尝试从新的FooContext进行相同的查询,您应该看到&#34; After&#34;。

更新

回答您的更新问题,是的,您是对的。在您使用FirstOrDefault()之前我错过了。如果您使用的是context.Find(foo.Id),我错误地假设,那么就没有查询。

至于为什么内存中的对象没有更新以反映数据库中的变化,我需要做一些研究来做更多的事情而不是推测。那就是说,这是我的猜测:

  • 数据库上下文的实例不能返回同一实体的多个实例。在一个工作单元中,我们必须能够依赖上下文来返回实体的相同的实例。否则,我们可能会按不同的标准进行查询,并获得表示同一概念实体的3个对象。那时,上下文如何处理其中任何一个的变化?如果名称在其中两个名称上更改为不同的值,然后调用SaveChanges()该怎么办?
  • 由于上下文最多跟踪每个实体的单个实例,为什么EF只能在执行查询时更新该实体?如果存在未决的内存中更改,EF甚至可以丢弃该更改,因为它知道这些更改。
    • 我认为答案的一部分是在大型实体和大型结果集中区分所有列的性能过高。
    • 我认为答案的一个重要部分是它执行一个简单的SELECT语句不应该有可能在整个系统中引起副作用。实体可以通过某个属性的值进行分组或循环,并在不确定的时间更改该属性的值,并且由于SELECT查询非常不健全。