zoukankan      html  css  js  c++  java
  • Entity Framework Core系列教程-17-断开模式下更新数据

    Entity Framework Core 断开连接场景中更新数据

    EF Core API会为EntityState已修改的实体在数据库中生成并执行UPDATE语句。在连接的场景中,DbContext会跟踪所有实体,因此它知道哪些实体被修改,因此会自动将EntityState设置为Modified。

    在断开连接的情况下(例如,在Web应用程序中),DbContext不知道实体,因为修改的实体超出了当前DbContext实例的范围。因此,首先我们需要将断开连接的实体附加到具有修改后的EntityState的DbContext实例。

    下表列出了用于更新实体的DbContext和DbSet方法:

    DbContext方法 DbSet方法 描述
    DbContext.Update DbSet.Update DbSet.Update将实体附加到具有修改状态的DbContext
    DbContext.UpdateRange DbSet.UpdateRange DbSet.UpdateRange将实体集合附加到具有修改状态的DbContext

    下面的示例演示如何更新断开连接的实体。

    // 断开连接的学生实体
    var stud = new Student(){ StudentId = 1, Name = "Bill" };
    stud.Name = "Steve"; 
    using (var context = new SchoolContext())
    {
        context.Update<Student>(stud);
        // 或以下内容也有效
        // context.Students.Update(stud);
        // context.Attach<Student>(stud).State = EntityState.Modified;
        // context.Entry<Student>(stud).State = EntityState.Modified; 
        context.SaveChanges(); 
    }
    

    在上面的示例中,将stu视为现有的Student实体对象,因为它具有有效的Key属性值(StudentId = 1)。Entity Framework Core引入了DbContext.Update()方法,该方法将指定的实体附加到上下文,并将其EntityState设置为Modified。或者,您也可以使用DbSet.Update()方法(context.Students.Update(stud))执行相同的操作。
    上面的示例在数据库中执行以下UPDATE语句。

    exec sp_executesql N'SET NOCOUNT ON;
    UPDATE [Students] SET [Name] = @p0
    WHERE [StudentId] = @p1;
    SELECT @@ROWCOUNT;
    ',N'@p1 int,@p0 nvarchar(4000)',@p1=1,@p0=N'Steve'
    go
    

    更新多个实体

    使用DbContext.UpdateRange或DbSet.UpdateRange方法将实体的集合或实体数组附加到DbContext,并一次性将其EntityState设置为Modified。

    var modifiedStudent1 = new Student()
    {
        StudentId = 1,
        Name = "Bill"
    };
    
    var modifiedStudent2 = new Student()
    {
        StudentId = 3,
        Name = "Steve"
    };
    
    var modifiedStudent3 = new Student()
    {
        StudentId = 3,
        Name = "James"
    };
    
    IList<Student> modifiedStudents = new List<Student>()
    {
        modifiedStudent1,
        modifiedStudent2,
        modifiedStudent3
    };
    
    using (var context = new SchoolContext())
    {
        context.UpdateRange(modifiedStudents);
        
        // 或以下内容也有效
        //context.UpdateRange(modifiedStudent1, modifiedStudent2, modifiedStudent3);
        //context.Students.UpdateRange(modifiedStudents);
        //context.Students.UpdateRange(modifiedStudent1, modifiedStudent2, modifiedStudent3);
                    
        context.SaveChanges();
    }
    

    如您所见,UpdateRange方法有两个重载。一个重载采用实体的集合,第二个重载采用object []作为参数。 DbSet.UpdateRange方法的工作方式与DbContext.UpdateRange方法相同。

    EF Core通过为以上示例中的所有实体构建UPDATE语句并在一次数据库往返中执行该语句来提高性能。

    exec sp_executesql N'SET NOCOUNT ON;
    UPDATE [Students] SET [Name] = @p0
    WHERE [StudentId] = @p1;
    SELECT @@ROWCOUNT;
    
    UPDATE [Students] SET [Name] = @p2
    WHERE [StudentId] = @p3;
    SELECT @@ROWCOUNT;
    
    UPDATE [Students] SET [Name] = @p4
    WHERE [StudentId] = @p5;
    SELECT @@ROWCOUNT;
    
    ',N'@p1 int,@p0 nvarchar(4000),@p3 int,@p2 nvarchar(4000),@p5 int,@p4 nvarchar(4000)',
    @p1=1,@p0=N'Bill',@p3=2,@p2=N'Steve',@p5=3,@p4=N'James'
    go
    

    EntityState中的更改

    Update方法根据键属性的值设置EntityState。如果根或子实体的key属性为空,指定数据类型的null或默认值,则Update()方法将其视为新实体,并将其EntityState设置为Entity Framework Core 2.x中的Added。

    public static void Main()
    {
        var newStudent = new Student()
        {
            Name = "Bill"
        };
    
        var modifiedStudent = new Student()
        {
            StudentId = 1,
            Name = "Steve"
        };
    
        using (var context = new SchoolContext())
        {
            context.Update<Student>(newStudent);
            context.Update<Student>(modifiedStudent);
    
            DisplayStates(context.ChangeTracker.Entries());
        }
    }
    private static void DisplayStates(IEnumerable<EntityEntry> entries)
    {
        foreach (var entry in entries)
        {
            Console.WriteLine($"Entity: {entry.Entity.GetType().Name},
                     State: {entry.State.ToString()} ");
        }
    }
    

    输出:

    Entity: Student, State: Added
    Entity: Student, State: Modified
    

    在上面的示例中,newStudent没有Key属性值(StudentId)。因此,Update()方法会将其标记为“已添加”,而modifiedStudent具有一个值,因此将其标记为“已修改”。

    异常

    如果DbContext的实例已经在跟踪具有相同键属性值的实体,则Update和UpdateRange方法将引发InvalidOperationException。考虑以下示例:

    var student = new Student()
    {
        StudentId = 1,
        Name = "Steve"
    };
    
    using (var context = new SchoolContext())
    {
        // 将内容加载到其StudentId为1的conext中
        context.Students.First<Student>(s => s.StudentId == 1); 
    
        // 引发异常,因为它已经在跟踪带有StudentId = 1的实体
        context.Update<Student>(student); 
    
        context.SaveChanges();
    }
    

    在上面的示例中,上下文对象加载了StudentId为1的Student实体,并开始对其进行跟踪。因此,附加具有相同键值的实体将引发以下异常:

    The instance of entity type 'Student' cannot be tracked because another instance with the same key value for {'StudentId'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.
    无法跟踪实体类型“ Student”的实例,因为已经跟踪了另一个具有相同“ {'StudentId'}”键值的实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。考虑使用'DbContextOptionsBuilder.EnableSensitiveDataLogging'查看有冲突的键值。
    

    在下一章中学习在断开连接的情况下删除记录。

  • 相关阅读:
    CodeForces 722C 题解
    ubuntu自带神奇文本编辑器-gedit使用入门
    My new Blog on cnblogs
    kaldi语音识别技术
    BUAA-OO-第三单元作业-JML之图论
    C++面向对象编程思想
    软件工程-设计模式
    BUAA-OO-第二单元作业-电梯调度
    操作系统make命令与Makefile文件编写
    操作系统启动过程分析(Linux-OS启动优化)
  • 原文地址:https://www.cnblogs.com/AlexanderZhao/p/12878807.html
Copyright © 2011-2022 走看看