• Entity Framework Core系列教程-16-断开模式下插入数据


    在Entity Framework Core的断开模式下插入数据

    您了解了如何在连接模式中保存数据。在这里,您将了解有关在断开模式中保存数据的信息。
    在断开连接的方案中保存数据与在连接的方案中保存数据有些不同。在断开连接的情况下,
    DbContext不知道断开连接的实体,因为在当前DbContext实例的范围之外添加或修改了实体。因此,您需要将断开连接的实体附加到具有适当EntityState的上下文,以便对数据库执行CUD(创建,更新,删除)操作。
    下图说明了断开连接情况下的CUD操作:
    在这里插入图片描述

    根据上图,断开的实体(DbContext不会跟踪的实体)需要使用适当的EntityState附加到DbContext。例如,新实体的已添加状态,已编辑实体的已修改状态和已删除实体的已删除状态,这将在调用SaveChanges()方法时在数据库中产生INSERT,UPDATE或DELETE命令。

    为了在断开连接的情况下使用Entity Framework Core将记录插入,更新或删除到DB表中,必须执行以下步骤:

    1. 使用适当的EntityState将实体附加到DbContext,例如添加,修改或删除
    2. 调用SaveChanges()方法

    下面的示例演示如何使用上述步骤将新记录插入数据库:

    //Disconnected entity
    var std = new Student(){ Name = "Bill" };
    
    using (var context = new SchoolContext())
    {
        //1. 使用添加的EntityState将实体附加到上下文
        context.Add<Student>(std);
        //或以下内容也有效
        // context.Students.Add(std);
        // context.Entry<Student>(std).State = EntityState.Added;
        // context.Attach<Student>(std);
                      
        //2. 调用SaveChanges将新记录插入Student表
        context.SaveChanges();
    }
    

    在上面的示例中,std是Student实体的断开连接的实例。context.Add<Student>()方法将Student实体附加到具有添加状态的上下文。 SaveChanges()方法将生成并执行以下INSERT语句:

    exec sp_executesql N'SET NOCOUNT ON;
    INSERT INTO [Students] ([Name])
    VALUES (@p0);
    SELECT [StudentId]
    FROM [Students]
    WHERE @@ROWCOUNT = 1 AND [StudentId] = scope_identity();',N'@p0 nvarchar(4000), 
    @p1 nvarchar(4000) ',@p0=N'Bill'
    go
    

    EF Core提供了多种添加状态为“Added”的实体的方法。在上面的示例中,context.Students.Add(std);, context.Entry<Student>(std).State = EntityState.Added; and context.Attach<Student>(std); 将导致与上述相同的INSERT语句。

    EF Core提供了以下DbContext和DbSet方法,这些方法将断开连接的实体与添加的EntityState附加在一起,后者又将在数据库中执行INSERT语句。

    DbContext 方法 DbSet 方法 描述
    DbContext.Attach DbSet.Attach 将实体附加到DbContext。为其Key属性具有值的实体设置“不变”状态,为其Key属性为空或数据类型的默认值的实体设置“添加”状态
    DbContext.Add DbSet.Add DbContext.Add DbSet.Add将具有附加状态的实体附加到DbContext
    DbContext.AddRange DbSet.AddRange DbContext.AddRange DbSet.AddRange将实体集合附加到具有添加状态的DbContext
    DbContext.Entry - DbContext.Entry-获取指定实体的EntityEntry,该实体提供对更改跟踪信息和操作的访问
    DbContext.AddAsync DbSet.AddAsync DbContext.AddAsync DbSet.AddAsync异步方法,用于将实体附加到具有“已添加”状态的DbContext上,如果没有,则开始跟踪它。调用SaveChangesAsync()时,数据将插入数据库中
    DbContext.AddRangeAsync DbSet.AddRangeAsync DbContext.AddRangeAsync DbSet.AddRangeAsync异步方法,用于一次性将多个实体附加到具有添加状态的DbContext上,如果没有,则开始跟踪它们。调用SaveChangesAsync()时,数据将插入数据库中

    注意:上面的DbContext方法是在EF Core中引入的(它们在EF 6或更早版本中不可用)。 DbContext和DbSet方法都执行相同的操作。您使用哪一种取决于您的编码模式和偏好。

    插入关系数据

    在上一章中,我们学习了在两个实体之间创建一对一,一对多和多对多关系。Entity Framework API会插入相关实体中包含的所有关系数据。
    使用DbContext.Add或DbSet.Add方法将相关实体添加到数据库。 Add方法将实体附加到上下文,并将“已添加”状态设置为ID(键)属性为空,空或数据类型默认值的实体图中的所有实体。考虑以下示例。

    var stdAddress = new StudentAddress()
    {
        City = "SFO",
        State = "CA",
        Country = "USA"
    };
    
    var std = new Student()
    {
        Name = "Steve",
        Address = stdAddress
    };
    using (var context = new SchoolContext())
    {
        // 将实体附加到具有添加状态的DbContext
        context.Add<Student>(std);
    
        // 调用SaveChanges将新记录插入Student表
        context.SaveChanges();
    }
    

    在上面的示例中,context.Add<Student>(std)添加了Student实体的实例。 EF Core API通过Student的引用导航属性到达StudentAddress实例,并将两个实体的EntityState标记为Added,这将在SaveChanges()上构建并执行以下两个INSERT命令。

    exec sp_executesql N'SET NOCOUNT ON;
    INSERT INTO [Students] ([Name])
    VALUES (@p0);
    SELECT [StudentId]
    FROM [Students]
    WHERE @@ROWCOUNT = 1 AND [StudentId] = scope_identity();',N'@p0 nvarchar(4000), 
    @p1 nvarchar(4000) ',@p0=N'Steve'
    go
    
    exec sp_executesql N'SET NOCOUNT ON;
    INSERT INTO [StudentAddresses] ([Address], [City], [Country], [State], [StudentId])
    VALUES (@p5, @p6, @p7, @p8, @p9);
    SELECT [StudentAddressId]
    FROM [StudentAddresses]
    WHERE @@ROWCOUNT = 1 AND [StudentAddressId] = scope_identity();
    ',N'@p5 nvarchar(4000),@p6 nvarchar(4000),@p7 nvarchar(4000),@p8 nvarchar(4000),
    @p9 int',@p5=NULL,@p6=N'SFO',@p7=N'USA',@p8=N'CA',@p9=1
    Go
    
    

    插入多条记录

    使用DbContext.AddRange或DbSet.AddRange方法可一次性添加多个实体。您无需多次调用DbContext.Add方法。

    AddRange 方法 描述
    void AddRange(IEnumerable<Object> entities) void AddRange(IEnumerable<Object> entities)将具有相同或不同类型的实体的列表添加到具有“已添加”状态的DbContext中
    void AddRange(param object[] entities) void void AddRange(param object[] entities)将具有相同或不同类型的实体的数组添加到具有“ Added”状态的DbContext中
    void AddRangeAsync(IEnumerable<Object>, CancellationToken) void AddRangeAsync(IEnumerable<Object>, CancellationToken)异步方法,用于将相同或不同类型的实体列表添加到具有添加状态的DbContext中

    以下示例演示了使用AddRange添加学生实体对象列表。

    var studentList = new List<Student>() {
        new Student(){ Name = "Bill" },
        new Student(){ Name = "Steve" }
    };
    
    using (var context = new SchoolContext())
    {
        context.AddRange(studentList);
        context.SaveChanges();
    }
    

    上面的示例将在Student表中插入两个新记录:
    您还可以添加不同类型的实体的列表,如下所示:

    var std1 = new Student(){ Name = "Bill" };
    
    var std2 = new Student(){ Name = "Steve" };
    
    var computer = new Course() { CourseName = "Computer Science" };
    
    var entityList = new List<Object>() {
        std1,
        std2,
        computer
    };
    
    using (var context = new SchoolContext())
    {                
        context.AddRange(entityList);
    
        // or 
        // context.AddRange(std1, std2, computer);
    
        context.SaveChanges();
    }
    

    在上面的示例中,entityList是List<Object>的一种。因此,它可以包含任何类型的实体。 AddRange()方法将所有指定的实体添加到上下文中,SaveChanges()将一次性构建并执行INSERT语句。
    EF Core通过在一次数据库往返中对所有上述实体执行INSERT语句来提高性能。上面的示例将在数据库中执行以下语句。

    exec sp_executesql N'SET NOCOUNT ON;
    INSERT INTO [Courses] ([CourseName], [Description])
    VALUES (@p0, @p1);
    SELECT [CourseId]
    FROM [Courses]
    WHERE @@ROWCOUNT = 1 AND [CourseId] = scope_identity();
    
    DECLARE @inserted1 TABLE ([StudentId] int, [_Position] [int]);
    MERGE [Students] USING (
    VALUES (@p2, 0),
    (@p3, 1)) AS i ([Name], _Position) ON 1=0
    WHEN NOT MATCHED THEN
    INSERT ([Name])
    VALUES (i.[Name])
    OUTPUT INSERTED.[StudentId], i._Position
    INTO @inserted1;
    
    SELECT [t].[StudentId] FROM [Students] t
    INNER JOIN @inserted1 i ON ([t].[StudentId] = [i].[StudentId])
    ORDER BY [i].[_Position];
    ',N'@p0 nvarchar(4000),@p1 nvarchar(4000),@p2 nvarchar(4000),@p3 nvarchar(4000)',
    @p0=N'Computer Science',@p1=NULL,@p2=N'Steve',@p3=N'Bill'
    go
    

    使用DbSet插入数据

    如前所述,您可以使用DbSet来保存实体的实例,该实例将以与EF 6.x相同的方式转换为数据库中的INSERT / UPDATE / DELETE命令。
    使用DbSet<TEntity>.Add()方法附加具有添加状态的实体,或使用DbSet<TEntity> .AddRange()方法附加具有添加状态的实体的集合,如下所示。

    var std = new Student()
    {
        Name = "Bill"
    };
    
    using (var context = new SchoolContext())
    {
        context.Students.Add(std);
    
        // or
        // context.Students.Attach(std);
    
        context.SaveChanges();
    }
    

    在上面的示例中,context.Students的类型为DbSet<Student>类型。因此,我们只能添加Student实体。 context.Students.Add(std)将Student实体附加到具有Added状态的上下文,当调用SaveChanges()方法时,这将导致INSERT语句。
    在下一章中我们将学习如何更新断开连接的实体。

  • 相关阅读:
    图解JQUERY尺寸及位置定义
    JS中offsetTop、clientTop、scrollTop、offsetTop各属性介绍
    js拖拽的封装
    Git详解之九:Git内部原理
    Git详解之八:Git与其他系统
    量化投资的Python库——Tushare
    Python数据分析-Day2-Pandas模块
    Python数据分析-Day1-Numpy模块
    Python全栈开发-Day8-Socket网络编程
    Python全栈开发-Day7-面向对象编程2
  • 原文地址:https://www.cnblogs.com/AlexanderZhao/p/12878808.html
Copyright © 2020-2023  润新知