• .NET 云原生架构师训练营(模块二 基础巩固 EF Core 更新和迁移)--学习笔记


    2.4.6 EF Core -- 更新

    • 状态
    • 自动变更检测
    • 不查询删除和更新
    • 并发

    状态

    • Entity State
    • Property State

    Entity State

    • Added 添加
    • Unchanged 没有变化
    • Modified 已修改
    • Deleted 已删除
    • Detached 未跟踪

    Property State

    • IsModified
    • CurrentValue
    • OriginValue

    自动变更检测

    • 使用自动变更检测完成确定字段的更新
    • 使用自动变更检测完成任意字段的更新

    使用自动变更检测完成确定字段的更新

    ProjectController

    [HttpPatch]
    [Route("{id}")]
    public async Task<ActionResult<Project>> SetTitleAsync(string id, [FromQuery] string title, CancellationToken cancellationToken)
    {
        // 查询实体信息
        var origin = await _lighterDbContext.Projects.FirstOrDefaultAsync(p => p.Id == id, cancellationToken);
    
        // 修改实体属性
        origin.Title = title;
    
        // 数据提交保存
        await _lighterDbContext.SaveChangesAsync();
    
        return origin;
    }
    

    修改分组信息

    // 查询实体信息
    var originGroup = await _lighterDbContext.ProjectGroups.Where(g => g.ProjectId == id).ToListAsync(cancellationToken: cancellationToken);
    
    // 修改实体属性
    foreach (var group in originGroup)
    {
        group.Name = $"{title} - {group.Name}";
    }
    

    查询项目信息时带出分组信息

    [HttpGet]
    public async Task<IEnumerable<Project>> GetListAsync(CancellationToken cancellationToken)
    {
        return await _lighterDbContext.Projects.Include(p => p.Groups).ToListAsync(cancellationToken);
    }
    

    使用自动变更检测完成任意字段的更新

    [HttpPatch]
    [Route("{id}")]
    public async Task<ActionResult<Project>> SetAsync(string id, CancellationToken cancellationToken)
    {
        // 查询实体信息
        var origin = await _lighterDbContext.Projects.FirstOrDefaultAsync(p => p.Id == id, cancellationToken);
        var properties = _lighterDbContext.Entry(origin).Properties.ToList();
    
        // 修改实体属性
        foreach (var query in HttpContext.Request.Query)
        {
            var property = properties.FirstOrDefault(p => p.Metadata.Name == query.Key);
            if (property == null)
                continue;
    
            var currentValue = Convert.ChangeType(query.Value.First(), property.Metadata.ClrType);
    
            _lighterDbContext.Entry(origin).Property(query.Key).CurrentValue = currentValue;
            _lighterDbContext.Entry(origin).Property(query.Key).IsModified = true;
        }
    
        // 数据提交保存
        await _lighterDbContext.SaveChangesAsync(cancellationToken);
    
        return origin;
    }
    

    不查询删除和更新

    删除之前先查询

    var id = 1;
    using(var db = new entityContext())
    {
        var entity = db.dbset.FirstOrDefault(e=>e.ID == id);
        if(entity != null)
        {
            db.dbset.Remove(entity);
            db.SaveChanges();
        }
    }
    

    不查询删除

    var id = 1;
    using(var db = new entityContext())
    {
        var entity = new Entity{ID = id};
        
        db.dbset.Attach(entity);
        db.dbset.Remove(entity);
        db.SaveChanges();
    }
    

    不查询更新

    try
    {
        using(var db = new dbContext())
        {
            var entity = new myEntity{PageID = pageid};
            db.Pages.Attach(entity);// added
            
            entity.Title = "new title";// modified, isModified=true
            entity.Url = "new-url";
            
            db.SaveChanges();
        }
    }
    catch(DataException)
    {
        
    }
    

    并发

    乐观处理:系统认为数据的更新在大多数情况下是不会产生冲突的,只在数据库更新操作提交的时候才对数据作冲突检测(推荐)

    悲观处理:根据命名即对数据库进行操作更新时,对操作持悲观保守的态度,认为产生数据冲突的可能性很大,需要先对请求的数据加锁再进行相关操作

    在 Entity 中添加行版本号字段

    /// <summary>
    /// 行版本号
    /// </summary>
    [Timestamp]
    public byte[] RowVersion { get; set; }
    

    每次对数据进行更新的时候,都会产生最新的版本号,如果更新的时候查询的版本号与之前的版本号不一致,就会报错

    在 UpdateAsync 方法中的查询和更新中间如果数据库的行版本号发生了修改,就会报错

    ProjectController

    [HttpPut]
    [Route("{id")]
    public async Task<ActionResult<Project>> UpdateAsync(string id, [FromBody] Project project, CancellationToken cancellationToken)
    {
        var origin = await _lighterDbContext.Projects.FirstOrDefaultAsync(p => p.Id == id, cancellationToken);
    
        if (origin == null)
            return NotFound();
    
        _lighterDbContext.Entry(origin).CurrentValues.SetValues(project);
    
        await _lighterDbContext.SaveChangesAsync(cancellationToken);
        return origin;
    }
    

    通过客户端传入行版本号,解决前端浏览器数据覆盖问题

    _lighterDbContext.Entry(origin).Property(p => p.RowVersion).OriginalValue = project.RowVersion;
    

    2.4.7 EF Core -- 迁移

    生成 SQL 脚本

    从空白开始生成sql脚本  
    dotnet ef migrations script
    
    
    生成指定版本到最新版本的sql 
    dotnet ef migrations script AddNewTables
    
    
    从A-B版本生成迁移SQL脚本 
    dotnet ef migrations script AddNewTables AddAuditTable
    

    2.4.8 EF Core -- 其他

    database-first

    dotnet ef dbcontext scaffold "server=172.0.0.1;port=7306;user=root;password=root123456@;database=lighter" Pomelo.EntityFrameworkCore.MySql -o Models
    

    GitHub源码链接:

    https://github.com/MINGSON666/Personal-Learning-Library/tree/main/ArchitectTrainingCamp/LighterApi

    知识共享许可协议

    本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

    欢迎转载、使用、重新发布,但务必保留文章署名 郑子铭 (包含链接: http://www.cnblogs.com/MingsonZheng/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。

    如有任何疑问,请与我联系 (MingsonZheng@outlook.com) 。

  • 相关阅读:
    mysql的数据结构
    mysql 如何在虚拟机中创建多个实例并启动
    mysql的查询语句原理
    mysql 之如何修复删除用户表
    docker之卷管理 与 卷挂载
    mysql的高可用
    python全栈脱产第35天------IPC机制、生产者消费者模型、线程、
    python全栈脱产第33天------基于udp协议的套接字、socketserver模块的使用、进程理论
    python全栈脱产第34天------开启进程的两种方式、join方法、进程对象其他相关的属性和方法、僵尸进程、孤儿进程、守护进程、互斥锁
    python全栈脱产第32天------基于tcp协议通信的套接字、模拟ssh实现远程执行命令、粘包问题
  • 原文地址:https://www.cnblogs.com/MingsonZheng/p/14218950.html
Copyright © 2020-2023  润新知