緋聞 發表於 2026-2-12 22:23:00

【EF Core】实体追踪——Entry中记录的数据

<p>这回终于可以 Free 了,刚好快过年了,工厂的机器也很人性化地坏了,需要供应商维修,不用测试项目了。所以老周也回家快活了几天。其实他们自己有开发团队,小改小测的他们完全可以自己弄,非要找老周麻烦。</p>
<p>咱们接着上次的话题聊,上次老周给大伙伴们胡诌了一番有关实体状态追踪的基础。这一次咱们把注意力放到名为 EntityEntry 的对象上。咦,这名怎么看着这么奇葩?咱们不管它奇不奇葩,只要知道它负责保存实体对象的属性值就行了。</p>
<p>毕竟实体类通常就是一个普通类,EF Core 需要状态追踪功能,总不能让开发者自己去跟踪吧,所以,EF 内部会用字典数据结构来保存实体的各个属性的值。字典是个好东西,啥都能放。有时候在写 Web API 时,一些返回 JSON 结构是动态的,为它们都定义一个类来序列化是不明智的,直接拼装 JSON 有点麻烦,这时候用字典就很爽。</p>
<p>当实体从数据库查询出来时,EF 先为实体对象创建一个快照,表明它的原始数据。然后,当你对实体进行各种搔操作之后,调用一下&nbsp;DetectChanges 方法,它会扫描实体对象各个属性的值,并和当初创建的快照比较,以确定实体是否被修改(或删除)。</p>
<p>为了让初学的大伙伴们好理解,咱们做个对比实验。</p>
<p>假设有这么个实体类,它表示一本书的信息。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> Book
{
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span> BookId { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> Name { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } = <span style="color: rgba(0, 0, 255, 1)">null</span>!<span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> ISBN { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } = <span style="color: rgba(0, 0, 255, 1)">null</span>!<span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> Author { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } = <span style="color: rgba(0, 0, 255, 1)">null</span>!<span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span> PubYear {<span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
}</span></pre>
</div>
<p>然后是实现数据库上下文。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> MyDb : DbContext
{
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> DbSet&lt;Book&gt; Books { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }

    </span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
      optionsBuilder.UseSqlite(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">data source=shop.db</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
    }

    </span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> OnModelCreating(ModelBuilder modelBuilder)
    {
      </span><span style="color: rgba(0, 0, 255, 1)">var</span> bookEnt = modelBuilder.Entity&lt;Book&gt;<span style="color: rgba(0, 0, 0, 1)">();
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 主键名称</span>
      bookEnt.HasKey(x =&gt; x.BookId).HasName(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">PK_Book</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 字符串长度</span>
      bookEnt.Property(a =&gt; a.Name).HasMaxLength(<span style="color: rgba(128, 0, 128, 1)">65</span><span style="color: rgba(0, 0, 0, 1)">);
      bookEnt.Property(b </span>=&gt; b.Author).HasMaxLength(<span style="color: rgba(128, 0, 128, 1)">20</span><span style="color: rgba(0, 0, 0, 1)">);
      bookEnt.Property(c </span>=&gt; c.ISBN).HasMaxLength(<span style="color: rgba(128, 0, 128, 1)">15</span><span style="color: rgba(0, 0, 0, 1)">);
      bookEnt.ToTable(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">tb_Books</span><span style="color: rgba(128, 0, 0, 1)">"</span>, t =&gt;<span style="color: rgba(0, 0, 0, 1)">
      {
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 约束</span>
            t.HasCheckConstraint(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">CK_Pubyear</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">\"PubYear\" &gt;= 2020 AND \"PubYear\" &lt;= 2080</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
      });
    }
}</span></pre>
</div>
<p>上面那些都是常规操作了,大家瞄两眼就行。下面代码创建数据库并插入一条数据。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">using</span> MyDb context = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">();
context.Database.EnsureCreated();</span><span style="color: rgba(0, 0, 0, 1)">

      <span style="background-color: rgba(255, 255, 0, 1)">Book bb </span></span><span style="background-color: rgba(255, 255, 0, 1)">= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">()
</span></span>      <span style="background-color: rgba(255, 255, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">{
</span></span>            <span style="background-color: rgba(255, 255, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">Name </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">回魂术</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span></span>            <span style="background-color: rgba(255, 255, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">Author </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">老周</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span></span>            <span style="background-color: rgba(255, 255, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">ISBN </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">551269882</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span></span>            <span style="background-color: rgba(255, 255, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">PubYear </span>= <span style="color: rgba(128, 0, 128, 1)">2028</span></span>
      <span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(255, 255, 0, 1)">};</span>
context.Books.Add(bb);
context.SaveChanges();</span></pre>
</div>
<p>下面重头戏来了。咱们从数据库中查询出这条记录,然后改变 PubYear 属性的值。</p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 0, 255, 1)">var</span> theBook =<span style="color: rgba(0, 0, 0, 1)"> context.Books.FirstOrDefault();
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (theBook == <span style="color: rgba(0, 0, 255, 1)">null</span>) <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 打印一次追踪</span>
<span style="color: rgba(0, 0, 0, 1)"> Console.WriteLine(context.ChangeTracker.ToDebugString());
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 更改属性</span>
theBook.PubYear = <span style="color: rgba(128, 0, 128, 1)">2030</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 再打印一次</span>
Console.WriteLine(context.ChangeTracker.ToDebugString());</pre>
</div>
<p>ChangeTracker.ToDebugString 方法方便测试,可以直接观察框架对实体对象的更改记录。两次调用的输出如下:</p>
<div class="cnblogs_code">
<pre>Book {BookId: <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">} Unchanged
    BookId: </span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)"> PK
    Author: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">老周</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
    ISBN: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">551269882</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
    Name: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">回魂术</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
    PubYear: </span><span style="color: rgba(128, 0, 128, 1)">2028</span><span style="color: rgba(0, 0, 0, 1)">

Book {BookId: </span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">} Unchanged
    BookId: </span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)"> PK
    Author: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">老周</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
    ISBN: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">551269882</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
    Name: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">回魂术</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
    PubYear: </span><span style="color: rgba(128, 0, 128, 1)">2030</span> Originally <span style="color: rgba(128, 0, 128, 1)">2028</span></pre>
</div>
<p>很明显,EF 并不知道咱们修改了实体,所以,调用一下&nbsp;DetectChanges 方法会触发一次扫描和比较。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 打印一次追踪</span>
<span style="color: rgba(0, 0, 0, 1)">Console.WriteLine(context.ChangeTracker.ToDebugString());
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 更改属性</span>
theBook.PubYear = <span style="color: rgba(128, 0, 128, 1)">2030</span><span style="color: rgba(0, 0, 0, 1)">;
<strong><span style="background-color: rgba(255, 255, 0, 1)">context.ChangeTracker.DetectChanges()</span></strong>;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 再打印一次</span>
Console.WriteLine(context.ChangeTracker.ToDebugString());</pre>
</div>
<p>这次 EF 就知道实体被修改了。</p>
<div class="cnblogs_code">
<pre>Book {BookId: <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">} Unchanged
    BookId: </span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)"> PK
    Author: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">老周</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
    ISBN: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">551269882</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
    Name: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">回魂术</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
    PubYear: </span><span style="color: rgba(128, 0, 128, 1)">2028</span><span style="color: rgba(0, 0, 0, 1)">

Book {BookId: </span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">} <span style="background-color: rgba(255, 255, 0, 1)">Modified</span>
    BookId: </span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)"> PK
    Author: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">老周</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
    ISBN: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">551269882</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
    Name: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">回魂术</span><span style="color: rgba(128, 0, 0, 1)">'</span>
    <span style="background-color: rgba(255, 255, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">PubYear: </span><span style="color: rgba(128, 0, 128, 1)">2030</span> Modified Originally <span style="color: rgba(128, 0, 128, 1)">2028</span></span></pre>
</div>
<p>由于 PubYear 属性被更新,使得实体的状态变更为 Modified。</p>
<p>那,为什么我调用 SaveChanges 方法时 EF 能顺利生成更新 SQL 呢,因为这个方法会先 DetectChanges,再根据实体的状态来生成更新语句。</p>
<p>但是,如果你在代码里面把&nbsp;AutoDetectChangesEnabled 属性设置为 false,那么调用 SaveChanges 方法是不会更新的。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">using</span> MyDb context = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 注意这一行</span>
context.<strong><span style="background-color: rgba(255, 255, 0, 1)">ChangeTracker.AutoDetectChangesEnabled = <span style="color: rgba(0, 0, 255, 1)">false</span></span></strong><span style="color: rgba(0, 0, 0, 1)">;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 找出第一条记录</span>
Book onebook =<span style="color: rgba(0, 0, 0, 1)"> context.Books.First();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 打印一次</span>
Console.WriteLine($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">{onebook.Name}, {onebook.PubYear}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 改一下年份</span>
onebook.PubYear = <span style="color: rgba(128, 0, 128, 1)">2031</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 提交更改</span>
<span style="color: rgba(0, 0, 0, 1); background-color: rgba(0, 255, 0, 1)">context.SaveChanges();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 再查询一次</span>
Book otherOne = context.Books.First(b =&gt; b.BookId ==<span style="color: rgba(0, 0, 0, 1)"> onebook.BookId);

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 再次打印</span>
Console.WriteLine($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">{otherOne.Name}, {otherOne.PubYear}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 看看实体追踪信息</span>
Console.WriteLine(context.Entry(otherOne).DebugView.LongView);</pre>
</div>
<p>看看调试信息。</p>
<div class="cnblogs_code">
<pre>回魂术, <span style="color: rgba(128, 0, 128, 1)">2028</span><span style="color: rgba(0, 0, 0, 1)">
回魂术, </span><span style="color: rgba(128, 0, 128, 1)">2031</span><span style="color: rgba(0, 0, 0, 1)">
Book {BookId: </span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">} <strong><span style="background-color: rgba(255, 0, 0, 1); color: rgba(255, 255, 255, 1)">Unchanged</span></strong>
    BookId: </span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)"> PK
    Author: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">老周</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
    ISBN: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">551269882</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
    Name: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">回魂术</span><span style="color: rgba(128, 0, 0, 1)">'</span>
    <span style="background-color: rgba(0, 255, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">PubYear: </span><span style="color: rgba(128, 0, 128, 1)">2031</span> Originally <span style="color: rgba(128, 0, 128, 1)">2028</span></span></pre>
</div>
<p>虽然 EF 追踪到 PubYear 属性被改为 2031,但注意它现在的状态是 Unchanged,所以 SaveChanges 不会更新数据库。</p>
<p>大伙伴们,这里你千万别犯糊涂,把概念搞混了。<span style="background-color: rgba(255, 0, 0, 1); font-size: 15px; color: rgba(255, 255, 255, 1)"><strong>AutoDetectChangesEnabled 属性设置为 false 只表明 EF 在 SaveChanges 方法中不会自动扫描检测实体的状态,可人家没说不会追踪实体的变更哟</strong></span>。</p>
<p>你如果只是查询数据,不更改数据,不需要追踪实体状态(以提升不太明显的性能),那么你不应该关闭&nbsp;AutoDetectChangesEnabled 属性,而应该设置&nbsp;QueryTrackingBehavior 属性。</p>
<div class="cnblogs_code">
<pre>context.ChangeTracker.QueryTrackingBehavior = <strong><span style="background-color: rgba(255, 255, 0, 1)">QueryTrackingBehavior.NoTracking</span></strong>;</pre>
</div>
<p>禁用追踪后,更新数据库就跟 Sugar 一样,需要你开手动档。你只需要调用一下 Update 方法,将实体状态变为 Modified 就行,缺点是生成的 UPDATE 语句会把所有字段都 SET 一遍。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 找出第一条记录</span>
Book onebook =<span style="color: rgba(0, 0, 0, 1)"> context.Books.First();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 打印一次</span>
Console.WriteLine($<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">{onebook.Name}, {onebook.PubYear}</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 改一下年份</span>
onebook.PubYear = <span style="color: rgba(128, 0, 128, 1)">2024</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 改变状态</span>
<span style="color: rgba(0, 0, 0, 1)"><strong><span style="background-color: rgba(255, 255, 0, 1)">context.Update(onebook)</span></strong>;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 提交更改</span>
context.SaveChanges();</pre>
</div>
<p>那有没有方法让生成的 UPDATE 语句只 SET 被改动过的字段呢?大伙伴肯定猜到,老周既然这么写,那说明肯定有的。但老周希望你不要死记方法,而是思路。咱们好好想一下:EF 是不是在查询出数据到为实体建立快照,然后进行比较,以确定哪些属性(字段)被修改了。既然这样,咱们在查询实体后,手动给它弄个快照,然后再修改属性值,再提交更新不就完事了吗?好,想干就干。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 找出第一条记录</span>
Book onebook =<span style="color: rgba(0, 0, 0, 1)"> context.Books.First();

context.Books.Attach(onebook);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 记录快照</span>
<span style="color: rgba(0, 0, 0, 1)"><strong><span style="background-color: rgba(255, 255, 0, 1)">context.Entry(onebook).OriginalValues.SetValues(onebook)</span></strong>;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 改一下年份</span>
onebook.PubYear = <span style="color: rgba(128, 0, 128, 1)">2021</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 打印一下状态信息</span>
<span style="color: rgba(0, 0, 0, 1)">Console.WriteLine(context.Entry(onebook).DebugView.LongView);

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 严重警告:如果你把 AutoDetectChangesEnabled 属性设置为 false,那一定要调用下面这一行以扫描更改,否则只能更新个毛线
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果你没有改动 AutoDetectChangesEnabled 属性,它默认是打开的,那下面这行可以忽略
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> context.ChangeTracker.DetectChanges();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 提交更改</span>
<span style="color: rgba(0, 0, 0, 1)">context.SaveChanges();

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 再查询一次</span>
Book otherOne = context.Books.First(b =&gt; b.BookId ==<span style="color: rgba(0, 0, 0, 1)"> onebook.BookId);

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 再次打印状态信息</span>
Console.WriteLine(context.Entry(otherOne).DebugView.LongView);</pre>
</div>
<p>结果如下:</p>
<div class="cnblogs_code">
<pre>Book {BookId: <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">} Modified
    BookId: </span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)"> PK
    Author: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">老周</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
    ISBN: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">551269882</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
    Name: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">回魂术</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
    PubYear: </span><span style="color: rgba(128, 0, 128, 1)">2021</span> <strong><span style="background-color: rgba(255, 255, 0, 1)">Modified Originally <span style="color: rgba(128, 0, 128, 1)">2025</span></span></strong><span style="color: rgba(0, 0, 0, 1)">
Book {BookId: </span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">} Detached
    BookId: </span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)"> PK
    Author: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">老周</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
    ISBN: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">551269882</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
    Name: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">回魂术</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
    PubYear: </span><span style="color: rgba(128, 0, 128, 1)">2021</span></pre>
</div>
<p>生成的SQL语句如下:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">UPDATE</span> "tb_Books" <span style="background-color: rgba(255, 255, 0, 1)"><span style="color: rgba(0, 0, 255, 1)">SET</span> "PubYear" <span style="color: rgba(128, 128, 128, 1)">=</span> <span style="color: rgba(0, 128, 0, 1)">@p0</span></span>
<span style="color: rgba(0, 0, 255, 1)">WHERE</span> "BookId" <span style="color: rgba(128, 128, 128, 1)">=</span> <span style="color: rgba(0, 128, 0, 1)">@p1</span><span style="color: rgba(0, 0, 0, 1)">
RETURNING </span><span style="color: rgba(128, 0, 0, 1); font-weight: bold">1</span>;</pre>
</div>
<p>因为咱们设置为不追踪实体(QueryTrackingBehavior.NoTracking),所以在查询后,要用 Attach 方法把实体连接到追踪器,并设定状态为 Unchanged。然后,context.Entry 方法获取到 EntityEntry 对象(本文的主角出场了),再往&nbsp;OriginalValues 里面放点原材料(目前查询出来的值),这样快照就建立了。再然后,可以大胆地修改实体了,这时候 EF 能扫描到更改。由于咱们设置的不追踪,所以更新之后 EF 又把实体给甩了,于是实体状态又变回 Detached。</p>
<p>说简单点,在手动档追踪下,实体的状态经历了 Detached -&gt; Unchanged -&gt; Modified -&gt; Detached 的生死轮回。</p>
<p>-----------------------------------------------------------------------------------------------------------------------------------------------------------</p>
<p>Entry 是“记录”的意思,EntityEntry 望文生义一下就是“实体记录”,它维护着实体的状态和各属性的值。一句话斯基总结:它是为实体追踪(跟踪)服务的,管理着实体相关的数据。</p>
<p>在 80% 的使用场景下,我们不需要用 Entry 的,走常规流程,从数据库中查询数据,自动追踪,修改后提交就完事了。不过,像影子属性这种不在实体类中的成员,你咋办?影子属性的元数据在数据库模型中,而值是保存在 EntityEntry 中。下面咱们用一个实例来说明。</p>
<p>咱们定义一个用户实体。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> User
{
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span> Id { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> Name { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } = <span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">.Empty;
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> LogName { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } = <span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">.Empty;
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span>? Password {<span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
}</span></pre>
</div>
<p>然后是实现自己的数据库上下文。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> AppContext : DbContext
{
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> DbSet&lt;User&gt; Users { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }

    </span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
      optionsBuilder.UseSqlite(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">data source=demo996.db</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)</span><span style="color: rgba(0, 0, 0, 1)">;
    }

    </span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> OnModelCreating(ModelBuilder modelBuilder)
    {
      EntityTypeBuilder</span>&lt;User&gt; entUser = modelBuilder.Entity&lt;User&gt;<span style="color: rgba(0, 0, 0, 1)">();
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 常规配置</span>
      entUser.Property(x =&gt; x.Name).HasColumnName(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">u_name</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
      entUser.Property(w </span>=&gt; w.LogName).HasColumnName(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">log_name</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
      entUser.Property(t </span>=&gt; t.Password).HasColumnName(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">u_pwd</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
      entUser.Property(m </span>=&gt; m.Id).HasColumnName(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">u_id</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
      entUser.HasKey(x </span>=&gt; x.Id).HasName(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">PK_Userid</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 影子属性</span>
      <strong><span style="background-color: rgba(255, 255, 0, 1)">entUser.Property&lt;DateTime?&gt;(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">LastLog</span><span style="color: rgba(128, 0, 0, 1)">"</span>).HasColumnName(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">_last_log</span><span style="color: rgba(128, 0, 0, 1)">"</span></span></strong><span style="color: rgba(0, 0, 0, 1)"><strong><span style="background-color: rgba(255, 255, 0, 1)">)</span></strong>;
    }
}</span></pre>
</div>
<p>User 实体有一个影子属性 LastLog,记录用户上一次登录的时间。在实体中不需要使用,或不希望被访问的属性,在建立数据库模型时可以作为影子属性。</p>
<p>在查询表达式中可以使用 EF.Property 方法来获取影子属性的值,EF 类中的方法成员都会抛出异常,没有真正实现,而是通过表达式树来翻译处理。因此,在非查询语句中访问影子属性,或要修改影子属性就不能使用 EF 类了。影子属性的值保存在 Entry 中,可以用以下代码来设置 LastLog 属性的值。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 先查询出实体</span>
User? u = context.Users.FirstOrDefault(x =&gt; x.Name == <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Teto</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (u <span style="color: rgba(0, 0, 255, 1)">is</span> <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
{
    Console.WriteLine(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">用户不存在</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 通过 Entry 修改影子属性</span>
DateTime theTime =<span style="color: rgba(0, 0, 0, 1)"> DateTime.Now;
context.<strong><span style="background-color: rgba(255, 255, 0, 1)">Entry(u).Property(</span></strong></span><strong><span style="background-color: rgba(255, 255, 0, 1)"><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">LastLog</span><span style="color: rgba(128, 0, 0, 1)">"</span>).CurrentValue =</span></strong><span style="color: rgba(0, 0, 0, 1)"><strong><span style="background-color: rgba(255, 255, 0, 1)"> theTime</span></strong>;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 打印追踪信息</span>
<span style="color: rgba(0, 0, 0, 1)">Console.WriteLine(context.ChangeTracker.DebugView.LongView);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 提交保存</span>
context.SaveChanges();</pre>
</div>
<p>直接使用 Entry 修改属性值会自动应用实体的状态。User 实体变为 Modeified 状态。</p>
<div class="cnblogs_code">
<pre>User {Id: <span style="color: rgba(128, 0, 128, 1)">3</span><span style="color: rgba(0, 0, 0, 1)">} <strong><span style="background-color: rgba(255, 255, 0, 1)">Modified</span></strong>
    Id: </span><span style="color: rgba(128, 0, 128, 1)">3</span><span style="color: rgba(0, 0, 0, 1)"> PK
    LastLog: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 128, 0, 1)">2026/2/12 18:25:01</span><span style="color: rgba(128, 0, 0, 1)">'</span> Modified Originally <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 128, 0, 1)">2026/2/12 17:41:20</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
    LogName: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 128, 0, 1)">teto</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
    Name: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 128, 0, 1)">Teto</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
    Password: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 128, 0, 1)">balabala</span><span style="color: rgba(128, 0, 0, 1)">'</span></pre>
</div>
<p>想验证是否更新数据库,可以查询一遍整个表。</p>
<div class="cnblogs_code">
<pre>using(var context =<span style="color: rgba(0, 0, 0, 1)"> new AppContext())
{
    Console.WriteLine(</span><span style="color: rgba(128, 128, 128, 1)">"</span><span style="color: rgba(128, 128, 128, 1)">\n------ 所有用户 ------");</span>
<span style="color: rgba(0, 0, 0, 1)">    foreach(User usr in context.Users)
    {
      Console.WriteLine($</span><span style="color: rgba(128, 128, 128, 1)">"</span><span style="color: rgba(128, 128, 128, 1)">""</span>
<span style="color: rgba(0, 0, 0, 1)">            用户ID:{usr.Id}
            用户名:{usr.Name}
            上次登录时间:{context.Entry(usr).Property(</span><span style="color: rgba(128, 128, 128, 1)">"</span><span style="color: rgba(128, 128, 128, 1)">LastLog").CurrentValue}</span>
            <span style="color: rgba(128, 128, 128, 1)">"</span><span style="color: rgba(128, 128, 128, 1)">"");</span>
      Console.Write(<span style="color: rgba(128, 128, 128, 1)">"</span><span style="color: rgba(128, 128, 128, 1)">\n");</span>
<span style="color: rgba(0, 0, 0, 1)">    }
}</span></pre>
</div>
<div class="cnblogs_code">
<pre>------ 所有用户 ------<span style="color: rgba(0, 0, 0, 1)">
用户ID:</span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">
用户名:Kaito
上次登录时间:</span><span style="color: rgba(128, 0, 128, 1)">2026</span>/<span style="color: rgba(128, 0, 128, 1)">2</span>/<span style="color: rgba(128, 0, 128, 1)">12</span> <span style="color: rgba(128, 0, 128, 1)">17</span>:<span style="color: rgba(128, 0, 128, 1)">40</span>:<span style="color: rgba(128, 0, 128, 1)">45</span><span style="color: rgba(0, 0, 0, 1)">

用户ID:</span><span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">
用户名:Gumi
上次登录时间:</span><span style="color: rgba(128, 0, 128, 1)">2026</span>/<span style="color: rgba(128, 0, 128, 1)">2</span>/<span style="color: rgba(128, 0, 128, 1)">12</span> <span style="color: rgba(128, 0, 128, 1)">17</span>:<span style="color: rgba(128, 0, 128, 1)">40</span>:<span style="color: rgba(128, 0, 128, 1)">13</span><span style="color: rgba(0, 0, 0, 1)">

用户ID:</span><span style="color: rgba(128, 0, 128, 1)">3</span><span style="color: rgba(0, 0, 0, 1)">
用户名:Teto
上次登录时间:</span><span style="color: rgba(128, 0, 128, 1)">2026</span>/<span style="color: rgba(128, 0, 128, 1)">2</span>/<span style="color: rgba(128, 0, 128, 1)">12</span> <span style="color: rgba(128, 0, 128, 1)">18</span>:<span style="color: rgba(128, 0, 128, 1)">25</span>:<span style="color: rgba(128, 0, 128, 1)">01</span></pre>
</div>
<p>上面的做法要先查询一次,然后更新,即数据库往返两回。99.99965% 的情况下也没啥影响的,而且很多时候用户编辑数据时确实得先查后改的,毕竟编辑界面你得先显示现有的数据才方便用户去修改。如果你不想 SELECT 只想直接 UPDATE 也可以的。</p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 直接实例化
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Id 是主键,必须赋值,明确要更新的记录</span>
User data = <span style="color: rgba(0, 0, 255, 1)">new</span>() { <strong><span style="background-color: rgba(255, 255, 0, 1)">Id = <span style="color: rgba(128, 0, 128, 1)">2</span></span></strong><span style="color: rgba(0, 0, 0, 1)"> };
</span><span style="color: rgba(0, 0, 255, 1)">var</span> entry =<span style="color: rgba(0, 0, 0, 1)"> context.Entry(data);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 标记实体的状态为已修改</span>
entry.State =<span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(0, 255, 0, 1)"> EntityState.Modified</span>;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 先改变实体的状态再去改变某个属性的状态
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 因为在设置实体为 Modified 时会把所有属性都设置为 Modified
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 先设置实体再设置属性成员就不会被覆盖</span>
<span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> p <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> entry.Properties)
{
   </span><span style="color: rgba(0, 0, 255, 1)">if</span> (p.Metadata.Name == <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">LastLog</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
   {
         </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 只修改这个属性</span>
         p.<span style="background-color: rgba(0, 255, 0, 1)">IsModified = <span style="color: rgba(0, 0, 255, 1)">true</span></span><span style="color: rgba(0, 0, 0, 1)">;
         p.CurrentValue </span>=<span style="color: rgba(0, 0, 0, 1)"> DateTime.Now;
   }
   </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">
   {
         </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 其他属性不改,标记为非修改状态</span>
         p.<span style="background-color: rgba(0, 255, 0, 1)">IsModified = <span style="color: rgba(0, 0, 255, 1)">false</span></span><span style="color: rgba(0, 0, 0, 1)">;
   }
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 打印追踪信息</span>
<span style="color: rgba(0, 0, 0, 1)"> Console.WriteLine(context.ChangeTracker.DebugView.LongView);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 提交保存</span>
context.SaveChanges();</pre>
</div>
<p>在改变实体状态时,先设置整个实体为 Modified,此时由于没有初始快照做比较,实体的所有属性(不含主键)都被标记为 Modified,如果这样更新数据库的话,会把 Name、LogName、Password 等属性都更改为 null 了。所以,咱们在设置实体为 Modified 后,还要对各个属性做做手脚。用 foreach 枚举各个属性,只有 LastLog 属性才设置为 Modified,其他的属性设置为未更改,这样发送到数据库的 UPDATE 语句只会 SET LastLog 属性。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">UPDATE</span> "Users" <span style="color: rgba(0, 0, 255, 1)">SET</span> "_last_log" <span style="color: rgba(128, 128, 128, 1)">=</span> <span style="color: rgba(0, 128, 0, 1)">@p0</span>
<span style="color: rgba(0, 0, 255, 1)">WHERE</span> "u_id" <span style="color: rgba(128, 128, 128, 1)">=</span> <span style="color: rgba(0, 128, 0, 1)">@p1</span><span style="color: rgba(0, 0, 0, 1)">
RETURNING </span><span style="color: rgba(128, 0, 0, 1); font-weight: bold">1</span>;</pre>
</div>
<p>由于咱们要更新的是影子属性,不能使用 ExecuteUpdate 这样的便捷方法。</p>
<p>本文到此结束。</p><br><br>
来源:https://www.cnblogs.com/tcjiaan/p/19601119
頁: [1]
查看完整版本: 【EF Core】实体追踪——Entry中记录的数据