项目场景:

使用FreeSql,包含所有的ORM数据库,都会存在这样的问题。在codefirst模式下,根据代码自动更新数据库,都建议不要在生产环境使用。为什么呢?

其实不建议使用,主要是根据代码自动生成数据时,极有可能会造成数据的丢失,比如修改字段类型,自动更新的结果可能并不是自己想的。

但是有一些使用场景是需要在生产环境自动升级的,比如

我们有一个CS客户端的产品,客户本地离线使用,客户本地部署,数据库也是本地数据库,版本从1000,迭代到了1100,中间发布了100个版本。这中间可能有多次数据库更改。我们的客户也可能遍布全国各地,版本也都不相同。客户如果没有新的需求,可能会一直使用当前旧版本,只有在有新的需求,或者想使用新的功能时,才会升级版本。所以升级的时间也是不确定的,升级要求客户安装新版软件,运行后自动升级。

那就真的不能在生产环境使用呢?

解决方案:

概要描述:

解决的思路其实就是自动升级,但是在判断需要升级时,才自动升级,同时升级前先备份数据库。

具体流程

程序内每次有数据库变更,发布版本时,修改程序内对应版本。比如最开始是1000,最新是1100

在数据库增加SysConfig表,字段包含DbVer表示当前数据库版本号

在数据库增加DbLog表,记录数据库升级日志,此项可选

在首次安装时,检查数据库文件不存在,表示首次安装,首次安装时创建SysConfig表和DbLog表,同时更新SysConfig表DbVer为程序中记录版本号。增加DbLog表日志

以后再次运行时,先获取SysConfig表DbVer,判断与程序中是否一致,

如果数据库比程序中大,说明运行低版本的程序,根据情况可以禁止运行。也可以不同步数据库,继续运行,根据实际情况决定。如果对程序和数据库一致性要求比较高,可以禁止运行。

如果数据库比程序小,说明数据库需要升级,此时先备份现有数据库,然后执行同步数据库。

详细说明:

直接上代码,比啥都清楚

program.cs文件代码

using Bonn.Helper;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using FreeSql.DataAnnotations;
using WindowsClient.Model;
using System.Reflection; namespace WindowsClient
{
static class Program
{
/// <summary>
/// 客户数据库路径
/// </summary>
private static string CustDbPath = Application.StartupPath + $"\\数据库\\cust.db"; /// <summary>
/// 数据库ORM
/// </summary>
public static IFreeSql fsql; /// <summary>
/// 服务器数据库版本
/// </summary>
private static int ServerDbVer = 1000; /// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
try
{ //数据库是否存在,用于插入初始数据,必须在freesql实例化前判断,因为实例化时会自动创建数据库
var custDbPathExists = File.Exists(CustDbPath); //deebug自动同步实体结构到数据库,release手动同步
bool syncDbStructure = false;
#if DEBUG
syncDbStructure = true;
#endif fsql = new FreeSql.FreeSqlBuilder()
.UseConnectionString(FreeSql.DataType.Sqlite, $@"Data Source={CustDbPath}; Pooling=true;Min Pool Size=1")
.UseAutoSyncStructure(syncDbStructure) //deebug自动同步实体结构到数据库,release手动同步
.UseMonitorCommand(cmd => Console.WriteLine($"线程:{cmd.CommandText}\r\n"))
.Build(); //请务必定义成 Singleton 单例模式 if(syncDbStructure)
{
//主要用于开发模式下,让数据库修改快速生效,不加此句时,只有在用到表时才会同步
fsql.CodeFirst.SyncStructure(GetTypesByTableAttribute());
} if (custDbPathExists == false)
{
//数据库文件不存在,表示是首次安装
fsql.CodeFirst.SyncStructure(GetTypesByTableAttribute());
var sysConfig = new SysConfig();
sysConfig.DbVer = ServerDbVer;
var dbResult = fsql.Insert(sysConfig).ExecuteAffrows();
if (dbResult <= 0)
throw new Exception("初始数据库失败。"); var row = new DbLog();
row.DbVer = ServerDbVer;
fsql.Insert(row).ExecuteAffrows();
}
int localDbVer = fsql.Select<SysConfig>().First().DbVer;
if (localDbVer != ServerDbVer)
{
//数据库版本不一样,需要升级
//备份数据库
File.Copy(CustDbPath, Application.StartupPath + $"\\数据库\\cust{DateTime.Now:yyyyMMddHHmmss}.db");
//升级数据库
fsql.CodeFirst.SyncStructure(GetTypesByTableAttribute());
var row = new DbLog();
row.DbVer = ServerDbVer;
fsql.Insert(row).ExecuteAffrows();
} Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new FrmMain());
}
catch (Exception e)
{
MessageBox.Show(e.ToString(), "出错啦", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
} public static Type[] GetTypesByTableAttribute()
{
List<Type> tableAssembies = new List<Type>();
foreach (Type type in Assembly.GetAssembly(typeof(IEntity)).GetExportedTypes())
{
foreach (Attribute attribute in type.GetCustomAttributes())
{
if (attribute is TableAttribute tableAttribute)
{
if (tableAttribute.DisableSyncStructure == false)
{
tableAssembies.Add(type);
}
}
}
};
return tableAssembies.ToArray();
}
}
}

SysConfig.cs

using FreeSql.DataAnnotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace WindowsClient.Model
{
/// <summary>
///
/// </summary>
[Table(Name = "sys_config")]
public class SysConfig : IEntity
{
/// <summary>
/// 主键
/// </summary>
[Column(Name = "id", IsIdentity = true, IsPrimary = true)]
public int Id { get; set; } /// <summary>
/// 主键
/// </summary>
[Column(Name = "dbVer")]
public int DbVer { get; set; } /// <summary>
/// 创建时间
/// </summary>
[Column(ServerTime = DateTimeKind.Local, CanUpdate = false)]
public DateTime CreateTime { get; set; } /// <summary>
/// 修改时间
/// </summary>
[Column(ServerTime = DateTimeKind.Local, CanUpdate = true)]
public DateTime UpdateTime { get; set; } }
}

DbLog.cs

using FreeSql.DataAnnotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace WindowsClient.Model
{
/// <summary>
///
/// </summary>
[Table(Name = "db_log")]
public class DbLog : IEntity
{
/// <summary>
/// 主键
/// </summary>
[Column(Name = "id", IsIdentity = true, IsPrimary = true)]
public int Id { get; set; } /// <summary>
/// 主键
/// </summary>
[Column(Name = "dbVer")]
public int DbVer { get; set; } /// <summary>
/// 创建时间
/// </summary>
[Column(ServerTime = DateTimeKind.Local, CanUpdate = false)]
public DateTime CreateTime { get; set; } /// <summary>
/// 修改时间
/// </summary>
[Column(ServerTime = DateTimeKind.Local, CanUpdate = true)]
public DateTime UpdateTime { get; set; }
}
}

总结:

以前是手写的SQL语句,现在用FreeSql确实方便多了。感觉FreeSql。

以上方案有需要改进的,或者好的建议,希望大家评论留言。

FreeSql生产环境自动升级数据库解决方案的更多相关文章

  1. 系统环境: CentOS 64位+千万不要在生产环境中升级glibc!

    # cd /lib64# LD_PRELOAD=/lib64/libc-2.15.so ln -sf /lib64/libc-2.15.so libc.so.6 libc-2.15.so 这个文件名根 ...

  2. 生产环境中,数据库升级维护的最佳解决方案flyway

    官网:https://flywaydb.org/ 转载:http://casheen.iteye.com/blog/1749916 1.  引言 想到要管理数据库的版本,是在实际产品中遇到问题后想到的 ...

  3. 生产环境中mysql数据库由主从关系切换为主主关系

    目录 一.清除原从数据库数据及主从关系 1.1.关闭主从数据库原有的主从关系 1.2.清除从数据库原有数据 二.将主库上的数据备份到从库 2.1.备份主库数据到从库 2.2.在从库使用tsc.sql文 ...

  4. [MySQL-MM] 生产环境自动恢复MM中一台M2库的过程,分享从零开始写的自动化重建脚本以及思路 (转)

    必须是MM架构,而且一台主库M1是完好无损的,一台主库M2可以根据M1主库来进行重建:如果MS架构,自己可以稍微做一下脚本修改动作,也能使用,架构如下图所示: 3  总体思路,建立主脚本a_build ...

  5. linux日志审计项目案例实战(生产环境日志审计项目解决方案)

    所谓日志审计,就是记录所有系统及相关用户行为的信息,并且可以自动分析.处理.展示(包括文本或者录像) 推荐方法:sudo配合syslog服务,进行日志审计(信息较少,效果不错) 1.安装sudo命令. ...

  6. 生产环境自动备份win服务器所有web项目(IIS+项目代码)

    @echo offrem 功能:每月自动备份本服务器所有web项目rem 日期:2022.3.10rem 制作人:zl rem 定义变量Y为备份时间:YYYYMMset y=%date:~0,4%%d ...

  7. 黄聪:禁止wordpress版本自动升级的解决方案

    在WordPress配置文件中找到wp-config.php,添加如下常量 define( 'AUTOMATIC_UPDATER_DISABLED', true );

  8. 理解Docker(6):若干企业生产环境中的容器网络方案

    本系列文章将介绍 Docker的相关知识: (1)Docker 安装及基本用法 (2)Docker 镜像 (3)Docker 容器的隔离性 - 使用 Linux namespace 隔离容器的运行环境 ...

  9. MySQL 8 升级数据库

    开始升级前 因为从MySQL 8.0 到MySQL 5.7,或者从MySQL 8.0 到之前的 MySQL 8.0版本都是不支持的.所有在在升级前要做好数据库备份,包括mysql 系统schema(数 ...

  10. LNMP一键安装包 PHP自动升级脚本

    LNMP一键安装包 PHP自动升级脚本 2011年03月15日 上午 | 作者:VPS侦探 前一段时间完成了lnmp一键安装包的PHP自动升级脚本,今天发布出来,如果想升级PHP版本的lnmp用户可以 ...

随机推荐

  1. WinRT: 可能是 Windows 上最好用的 Native ABI 和远程调用方案

    前言 Windows 自从很久以来就有一个叫做 COM 的 Native ABI.这是一套面向对象的 ABI,在此之上 Windows 基于 COM ABI 暴露了各种各样的 API,例如 Manag ...

  2. Excel联动(wps)

    Excel联动(wps) https://www.wps.cn/learning/course/detail/id/330423.html demo: https://files.cnblogs.co ...

  3. SDL开发笔记(三):使用SDL渲染窗口颜色和图片

    若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...

  4. 项目实战:Qt+OSG三维点云引擎(支持原点,缩放,单独轴或者组合多轴拽拖旋转,支持导入点云文件)

    需求   开发基于osg的三维点云引擎模块.  1.基于x,y,z坐标轴.  2.可设置原点,设置缩放比例.  3.可设置y轴和z轴单位.  4.三轴中,XY为2D图的水平.竖直方向:Z轴,对应高度图 ...

  5. 【LeetCode数组#4滑动窗口】长度最小的子数组+子数组最大平均数I

    长度最小的子数组 力扣题目链接(opens new window) 给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度.如果不存 ...

  6. eclipse c++ 安装

    eclipse及其插件安装 对于我这种被VS惯坏了的人来说,make file 非常不友好的,最近要在redhat 下面去编译c++动态库和应用程序,原有的工程是在window下面的,要到linux下 ...

  7. iOS上拉边界下拉白色空白问题解决概述

    表现 手指按住屏幕下拉,屏幕顶部会多出一块白色区域.手指按住屏幕上拉,底部多出一块白色区域. 产生原因 在 iOS 中,手指按住屏幕上下拖动,会触发 touchmove 事件.这个事件触发的对象是整个 ...

  8. 从 Neo4j 导入 Nebula Graph 实践见 SPark 数据导入原理

    本文主要讲述如何使用数据导入工具 Nebula Graph Exchange 将数据从 Neo4j 导入到 Nebula Graph Database.在讲述如何实操数据导入之前,我们先来了解下 Ne ...

  9. RocketMQ(8) 消费幂等

    1 什么是消费幂等 当出现消费者对某条消息重复消费的情况时,重复消费的结果与消费一次的结果是相同的,并且多次消 费并未对业务系统产生任何负面影响,那么这个消费过程就是消费幂等的. 幂等:若某操作执行多 ...

  10. 学习ASP.NET MVC 编程系列文章目录

    学习ASP.NET MVC(一)--我的第一个ASP.NET MVC应用程序 学习ASP.NET MVC(二)--我的第一个ASP.NET MVC 控制器 学习ASP.NET MVC(三)--我的第一 ...