EF Core 2

一、高级部分

1、实体对象状态

1.1、Added

  • 描述: 实体对象被添加到上下文中,但还没有保存到数据库。

  • 常见场景: 使用AddAddRange方法将实体添加到上下文中。

  • 示例:

    var student = new Student { Name = "John Doe" };
    context.Students.Add(student);
    // 此时,student 的状态是 Added
    

2.2、Unchanged

  • 描述: 实体对象从数据库加载到上下文中,且没有对其进行任何修改。

  • 常见场景: 实体对象从数据库查询后,默认状态为 Unchanged。

  • 示例:
var student = context.Students.Find(1);
// 此时,student 的状态是 Unchanged

2.3、Modified

  • 描述: 实体对象被修改,且这些修改需要在保存到数据库时应用。

  • 常见场景: 修改实体的属性,并调用SaveChanges方法后。

  • 示例:

    var student = context.Students.Find(1);
    student.Name = "Jane Doe";
    // 此时,student 的状态是 Modified
    

2.4、Deleted

  • 描述: 实体对象被标记为从数据库中删除。

  • 常见场景: 使用Remove方法将实体标记为删除。

  • 示例:

    var student = context.Students.Find(1);
    context.Students.Remove(student);
    // 此时,student 的状态是 Deleted
    

2.5、Detached

  • 描述: 实体对象不被上下文跟踪,通常是新创建的实体或从上下文中分离的实体。

  • 常见场景: 实体对象从上下文分离或未被上下文跟踪。

  • 示例:

    var student = new Student { Name = "Detached Student" };
    // 此时,student 的状态是 Detached
    

检查实体状态

var student = context.Students.Find(1);
var entry = context.Entry(student);
Console.WriteLine(entry.State);  // 输出 Unchanged, Modified, Added, Deleted, 或 Detached

实例

        [HttpGet]
        public ActionResult G_Test()
        {
            Blog blog = new Blog { Title = "西游记", Content = "这是师徒四人取经的故事" };
            Console.WriteLine(_context.Entry(blog).State);//Detached

            _context.Add(blog);
            Console.WriteLine(_context.Entry(blog).State);//Added
            _context.SaveChanges();

            Blog blog1 = _context.Blog.First();
            Console.WriteLine(_context.Entry(blog1).State);//UnChanged

            blog.Content = "这是师徒四人西天取经的故事";
            Console.WriteLine(_context.Entry(blog1).State);//Modified
            _context.SaveChanges();

            _context.Blog.Remove(blog);
            Console.WriteLine(_context.Entry(blog1).State);//Deleted
            return Ok("ok");
        }
2、事务管理

2.1、默认事务行为

SaveChanges方法的调用中所做的所有更改都将在一个事务中提交

2.2、手动创建事务

using var context = new YourDbContext();  
using var transaction = context.Database.BeginTransaction();  
  
try  
{  
    // 执行第一个SaveChanges调用  
    context.YourEntities.Add(new YourEntity { /* ... */ });  
    context.SaveChanges();  
  
    // 执行其他数据库操作...  
  
    // 提交事务  
    transaction.Commit();  
}  
catch (Exception ex)  
{  
    // 如果发生异常,则回滚事务  
    transaction.Rollback();  
    // 处理异常...  
}
3.3、
using (ApplicationDbContext db = new ApplicationDbContext(options))
 {
     using (var scope = new TransactionScope())
     {
         try
         {
             db.Customers.Add(new Customer() { Name = "梁朝伟", Age = 11, Email = "lcw@qq.com" });
             db.SaveChanges();
             db.Customers.Add(new Customer() { Name = "张曼玉", Age = 55, Email = "zmy@qq.com" });
             db.SaveChanges();
             //提交事务
             scope.Complete();
         }
         catch (Exception)
         {
            scope.Current.Rollback();回滚事务

         }
 
 
     }
 }
3、批量

使用第三方包 EFCore.BulkExtensions

3.1、批量新增

using EFCore.BulkExtensions;

using (var context = new MyDbContext())
{
    var products = new List<Product>
    {
        new Product { Name = "Product1", Price = 10 },
        new Product { Name = "Product2", Price = 20 },
        // 其他产品
    };

    context.BulkInsert(products);
}

3.2、批量修改

  • EF Core自带
context.Blogs
    .Where(b => b.Rating < 3)
    .ExecuteUpdate(setters => setters.SetProperty(b => b.IsVisible, false));

//更新多个属性
context.Blogs
    .Where(b => b.Rating < 3)
    .ExecuteUpdate(setters => setters
        .SetProperty(b => b.IsVisible, false)
        .SetProperty(b => b.Rating, 0));
  • 第三方包
using EFCore.BulkExtensions;

using (var context = new MyDbContext())
{
    var products = context.Products.Where(p => p.Price < 50).ToList();
    foreach (var product in products)
    {
        product.Price += 10;
    }

    context.BulkUpdate(products);
}
  • 直接执行SQL语句,更高效
using (var context = new MyDbContext())
{
    context.Database.ExecuteSqlRaw("UPDATE Products SET Price = Price + 10 WHERE Price < 50");
}

3.3、批量删除

using EFCore.BulkExtensions;

using (var context = new MyDbContext())
{
    var products = context.Products.Where(p => p.Price < 10).ToList();
    context.BulkDelete(products);
}

也可以直接执行SQL语句

4、调用视图

和实体使用差不多。只是视图通常是只读的,不能增删改

  • 数据库定义视图
CREATE VIEW ProductView AS
SELECT ProductId, Name, Price
FROM Products
WHERE Price > 50;
  • 定义实体
public class ProductView
{
    public int ProductId { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}
  • 配置上下文
public class MyDbContext : DbContext
{
    public DbSet<ProductView> ProductViews { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer("YourConnectionString");
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<ProductView>()
            .HasNoKey() // 视图没有主键,要特别指明
            .ToView("ProductView") // 指定视图名称
            .Property(v => v.ProductId).HasColumnName("ProductId");
        
        modelBuilder.Entity<ProductView>()
            .Property(v => v.Name).HasColumnName("Name");

        modelBuilder.Entity<ProductView>()
            .Property(v => v.Price).HasColumnName("Price");
    }
}
  • 使用视图
using (var context = new MyDbContext())
{
    var productViews = context.ProductViews.ToList();
    foreach (var product in productViews)
    {
        Console.WriteLine($"ProductId: {product.ProductId}, Name: {product.Name}, Price: {product.Price}");
    }
}
5、调用存储过程

5.1、只有输入参数

  • 数据库定义存储过程
CREATE PROCEDURE GetProductsByPrice
    @MinPrice DECIMAL(18, 2),
    @MaxPrice DECIMAL(18, 2)
AS
BEGIN
    SELECT ProductId, Name, Price
    FROM Products
    WHERE Price BETWEEN @MinPrice AND @MaxPrice
END
  • 创建实体类
public class Product
{
    public int ProductId { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}
  • 配置上下文
public class MyDbContext : DbContext
{
    public DbSet<Product> Products { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer("YourConnectionString");
    }
}
  • 调用存储过程
using (var context = new MyDbContext())
{
    var minPrice = 10.0m;
    var maxPrice = 50.0m;
    
    var products = context.Products
        .FromSqlRaw("EXEC GetProductsByPrice @MinPrice = {0}, @MaxPrice = {1}", minPrice, maxPrice)
        .ToList();
    
    foreach (var product in products)
    {
        Console.WriteLine($"ProductId: {product.ProductId}, Name: {product.Name}, Price: {product.Price}");
    }
}

5.2、有输出参数

  • 数据库定义存储过程
CREATE PROCEDURE GetProductsAndCount
    @MinPrice DECIMAL(18, 2),
    @MaxPrice DECIMAL(18, 2),
    @ProductCount INT OUTPUT
AS
BEGIN
    SELECT ProductId, Name, Price
    FROM Products
    WHERE Price BETWEEN @MinPrice AND @MaxPrice
    
    SELECT @ProductCount = COUNT(*)
    FROM Products
    WHERE Price BETWEEN @MinPrice AND @MaxPrice
END
  • 创建实体类
  • 配置上下文
  • 调用存储过程
using System.Data;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;

using (var context = new MyDbContext())
{
    var minPrice = 10.0m;
    var maxPrice = 50.0m;
    var productCountParameter = new SqlParameter
    {
        ParameterName = "@ProductCount",
        SqlDbType = SqlDbType.Int,
        Direction = ParameterDirection.Output
    };

    var products = context.Products
        .FromSqlRaw("EXEC GetProductsAndCount @MinPrice = {0}, @MaxPrice = {1}, @ProductCount = @ProductCount OUTPUT", minPrice, maxPrice, productCountParameter)
        .ToList();

    var productCount = (int)productCountParameter.Value;
    
    Console.WriteLine($"Total Products: {productCount}");
    foreach (var product in products)
    {
        Console.WriteLine($"ProductId: {product.ProductId}, Name: {product.Name}, Price: {product.Price}");
    }
}

6、并发控制

6.1、悲观并发控制

悲观并发控制假设并发冲突会频繁发生,因此在读取数据时锁定数据,以防止其他事务修改这些数据

ROWLOCK:行锁。

TABLOCK:表锁。

XLOCK:排他锁。

UPDLOCK:更新锁,防止其他事务获取排他锁。

HOLDLOCK:保持锁,直到事务完成,等同于 SERIALIZABLE 隔离级别。

示例:假设我们有一个 Product 实体类和对应的 Products 数据表。我们希望在更新产品价格时,锁定该产品记录,防止其他事务修改。

  1. 定义实体类和数据库上下文(略,正常配置)
  2. 使用事务和锁定提示更新产品价格
public void UpdateProductPrice(int productId, decimal newPrice)
{
    using (var context = new MyDbContext())
    {
        using (var transaction = context.Database.BeginTransaction())
        {
            try
            {
                // 使用 UPDLOCK 锁定产品记录
                var product = context.Products
                    .FromSqlRaw("SELECT * FROM Products WITH (UPDLOCK) WHERE ProductId = {0}", productId)
                    .SingleOrDefault();

                if (product != null)
                {
                    // 更新产品价格
                    product.Price = newPrice;
                    context.SaveChanges();

                    // 提交事务
                    transaction.Commit();
                }
                else
                {
                    throw new Exception("Product not found.");
                }
            }
            catch (Exception)
            {
                // 回滚事务
                transaction.Rollback();
                throw;
            }
        }
    }
}

6.2、乐观并发控制

乐观并发控制则允许多个用户同时操作同一个资源,通过并发冲突检测避免并发操作。和悲观并发控制相比,乐观并发控制不需要显示使用事务,而且也不需要使用锁。

EF Core 使用并发令牌列实现乐观并发控制,并发令牌列就是需要防止并发冲突的列。

//1、通用
public class Blog
    {
        public int Id { get; set; }

        public string Title { get; set; }

        public string Content { get; set; }
    }

modelBuilder.Entity<Blog>(option =>
            {
                option.ToTable("M_Blog").Property(o=>o.Content).IsConcurrencyToken();//指定并发令牌列
            });



//2、sqlserver自带的
public class Blog
    {
        public int Id { get; set; }

        public string Title { get; set; }

        public string Content { get; set; }

        public byte[] Rowversion{ get; set; }
    }

modelBuilder.Entity<Blog>(option =>
            {
                option.ToTable("M_Blog").Property(o=>o.Rowversion).IsRowVersion();//指定并发令牌列
            });

测试

        [HttpPost]
        public IActionResult G_test1(string content)
        {
            Blog? blog = _context.Blog.Find(2);
            blog.Content = content;

            try
            {
                _context.SaveChanges();
                return Ok($"content内容已经是{content}");
            }
            catch (DbUpdateConcurrencyException ex)//捕捉到发生并发冲突
            {
                var entry = ex.Entries.First();
                var dbValues = entry.GetDatabaseValues();
                string dbContent = dbValues.GetValue<string>(nameof(blog.Content));
                return Ok($"并发冲突,数据已经被更改为{content}");
            }
        }

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值