【问题标题】:Differences in marking modified properties between DbSet.Attach() and DbSet.Update()DbSet.Attach() 和 DbSet.Update() 之间标记修改属性的区别
【发布时间】:2019-08-15 15:29:04
【问题描述】:

假设我们有一个 Person 类(具有 Id 和 Age 属性),假设我们需要将 id 为 1 的人的 Age 从 29 更改为 30,我分别使用 DbSet.Update()DbSet.Attach()

Person p = new Person(){ Id = 1 }  
var entity = context.Persons.Update(p);
p.Age = 30;

Console.WriteLine("entity state:" + entity.State);
foreach (var modifiedProperty in entity.Properties.Where(p => p.IsModified))
{
   Console.Write($"The {modifiedProperty.Metadata.Name} property is marked as modified,");
}
context.SaveChanges();

输出:

实体状态:已修改。

Age 属性标记为已修改,Name 属性标记为已修改*

这是预期的结果,但如果我使用Attach() as

Person p = new Person(){ Id = 1 }  
var entity = context.Persons.Attach(p);
p.Age = 30;

Console.WriteLine("entity state:" + entity.State);
foreach (var modifiedProperty in entity.Properties.Where(p => p.IsModified))
{
   Console.Write($"The {modifiedProperty.Metadata.Name} property is marked as modified,");
}
context.SaveChanges();

输出:

实体状态:未更改。

之后什么都没有,因为没有属性被识别为已修改,但是我以与 Update() 方法相同的方式更改了 Age 属性,为什么 Attach() 无法识别修改的属性?如果Attach()不能识别被修改的属性,怎么还会生成正确的更新sql语句到数据库?

【问题讨论】:

    标签: c# entity-framework-core


    【解决方案1】:

    您非常混淆sql server 中的内容上下文缓存中的内容。 DbContext 缓存实体并跟踪它们,因此它知道在调用SaveChanges() 时要做什么。如果实体在缓存中不存在,则由开发人员帮助实体框架确定正确的逻辑

    DbSet.Update(TEntity) Method

    开始跟踪处于修改状态的给定实体,以便在调用 SaveChanges() 时在数据库中更新它。

    DbSet.Attach(TEntity) Method

    开始跟踪处于未更改状态的给定实体,以便在调用 SaveChanges() 时不会执行任何操作。

    说到你的意思:

    但我更改 Age 属性的方式与 Update() 方法中的相同

    是的,你做了,但这不会改变你连接它的方式。事件的顺序非常重要。在以下示例中,我们将 Age 更改为 31 之前,我们将实体附加到实体框架上下文。它不知道您更改了它在更改发生时没有跟踪它(记录不会更改为 31):

    using (var context = new EntityContext())
    {
        p.Age = 31;
        context.Persons.Attach(p);
        context.SaveChanges();
    }
    

    如果 Attach() 无法识别修改的属性,为什么它仍然会生成正确的更新 sql 语句到数据库

    如果我们更改事件的顺序以附加实体我们更改值之前,那么实体框架可以检测到更改并将其保存到数据库(记录将更改为 32): p>

    using (var context = new EntityContext())
    {
        context.Persons.Attach(p);
        p.Age = 32;
        context.SaveChanges();
    }
    

    所有工作示例:

    DotNetFiddle Core Example

    // Entity Framework Extensions
    // Doc: https://entityframework-extensions.net/context-factory
    
    // @nuget: Microsoft.EntityFrameworkCore
    // nuget: Z.EntityFramework.Extensions.EFCore
    // @nuget: Microsoft.EntityFrameworkCore.SqlServer
    // @nuget: Microsoft.Extensions.Logging
    
    using System;
    using Microsoft.EntityFrameworkCore;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using Microsoft.Extensions.Logging;
    using System.Linq;
    
    public class Program
    {
        public static void Main()
        {
            var p = new Person();
            using (var context = new EntityContext())
            {
                context.Database.EnsureCreated();
                p.Age = 29;
                context.Persons.Add(p);
                context.SaveChanges();
            }
    
            using (var context = new EntityContext())
            {
                Console.WriteLine("Created Person Age 29");
                FiddleHelper.WriteTable("Student", context.Persons.ToList());
            }
    
            using (var context = new EntityContext())
            {
                p.Age = 30;
                context.Persons.Update(p);
                context.SaveChanges();
            }
    
            using (var context = new EntityContext())
            {
                Console.WriteLine("Update Person Age 30");
                FiddleHelper.WriteTable("Student", context.Persons.ToList());
            }
    
            using (var context = new EntityContext())
            {
                p.Age = 31;
                context.Persons.Attach(p);
                context.SaveChanges();
            }
    
            using (var context = new EntityContext())
            {
                Console.WriteLine("Change Person to 31, THEN Attach");
                FiddleHelper.WriteTable("Student", context.Persons.ToList());
            }
    
            using (var context = new EntityContext())
            {
                context.Persons.Attach(p);
                p.Age = 32;
                context.SaveChanges();
            }
    
            using (var context = new EntityContext())
            {
                Console.WriteLine("Attach THEN Change Person to 32");
                FiddleHelper.WriteTable("Student", context.Persons.ToList());
            }
    
        }
    
        public class EntityContext : DbContext
        {
            protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            {
                var connectionString = FiddleHelper.GetConnectionStringSqlServer();
                optionsBuilder.UseSqlServer(connectionString);
            }
            public DbSet<Person> Persons { get; set; }
        }
    
        public class Person
        {
            [Key]
            [DatabaseGenerated(DatabaseGeneratedOption.None)]
            public int Id { get; set; }
            public int Age { get; set; }
            public string Name { get; set; }
        }
    }
    

    【讨论】:

    • Philips 我做了和你一样的事情。我的问题是,我在附加对象后更改了 Age 属性,为什么 Dbcontext 仍然认为实体状态未更改并且没有属性处于修改状态?
    • 顺便说一句,我对DbSet.Attach的定义很困惑,它说“调用SaveChanges()时不会执行任何操作”,但是如果你在附加后改变某事,将执行更新操作(在我的情况下,年龄已更新)?
    猜你喜欢
    • 2013-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-03
    相关资源
    最近更新 更多