基于SqlSugar初始化数据库

增量更新表结构

/// <summary>
/// 初始化数据库
/// </summary>
/// <param name="db"></param>
/// <param name="config"></param>
private static void InitDatabase(SqlSugarScope db, DbConnectionConfig config)
{
    SqlSugarScopeProvider dbProvider = db.GetConnectionScope(config.ConfigId);

    // 兼容金仓
    dbProvider.CurrentConnectionConfig.MoreSettings.DatabaseModel = SqlSugar.DbType.MySql;
    dbProvider.CurrentConnectionConfig.MoreSettings.IsAutoToUpper = false;
    dbProvider.CurrentConnectionConfig.MoreSettings.DisableNvarchar = true;

    // 初始化/创建数据库
    if (config.DbSettings.EnableInitDb)
    {
        Console.WriteLine($"初始化数据库 {config.DbType} - {config.ConfigId} - {config.ConnectionString}");
        if (config.DbType != SqlSugar.DbType.Oracle)
            dbProvider.DbMaintenance.CreateDatabase();
    }

    // 初始化表结构
    if (config.TableSettings.EnableInitTable)
    {
        Console.WriteLine($"初始化表结构 {config.DbType} - {config.ConfigId}");
        var entityTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false))
            .Where(u => !u.GetCustomAttributes<IgnoreTableAttribute>().Any())
            .WhereIF(config.TableSettings.EnableIncreTable, u => u.IsDefined(typeof(IncreTableAttribute), false)).ToList();

        if (config.ConfigId.ToString() == SqlSugarConst.MainConfigId) // 默认库(有系统表特性、没有日志表和租户表特性)
            entityTypes = entityTypes.Where(u => u.GetCustomAttributes<SysTableAttribute>().Any() || (!u.GetCustomAttributes<LogTableAttribute>().Any() && !u.GetCustomAttributes<TenantAttribute>().Any())).ToList();
        else if (config.ConfigId.ToString() == SqlSugarConst.LogConfigId) // 日志库
            entityTypes = entityTypes.Where(u => u.GetCustomAttributes<LogTableAttribute>().Any()).ToList();
        else
            entityTypes = entityTypes.Where(u => u.GetCustomAttribute<TenantAttribute>()?.configId.ToString() == config.ConfigId.ToString()).ToList(); // 自定义的库

        // 按是否分表分组,减少循环内反射开销
        var splitTypes = new List<Type>(); // 分表: 按时间等把同一张表分成多张表,物理级别的
        var normalTypes = new List<Type>(entityTypes.Count); // 普通表
        foreach (var t in entityTypes)
        {
            if (t.GetCustomAttribute<SplitTableAttribute>() != null)
                splitTypes.Add(t);
            else
                normalTypes.Add(t);
        }

        // 预检查:获取数据库中现有表名集合(忽略大小写)
        var dbTableNames = new HashSet<string>(
            dbProvider.DbMaintenance.GetTableInfoList(false).Select(t => t.Name),
            StringComparer.OrdinalIgnoreCase);

        // 获取所有实体信息,避免重复反射
        var entityInfos = normalTypes.Select(t => new { Type = t, Info = dbProvider.EntityMaintenance.GetEntityInfo(t) }).ToList();

        // 缓存数据库中已存在表的字段信息(表名->字段名集合,忽略大小写)
        var dbTableColumnMap = new Dictionary<string, HashSet<string>>(StringComparer.OrdinalIgnoreCase);
        foreach (var item in entityInfos)
        {
            var tableName = item.Info.DbTableName;
            if (string.IsNullOrEmpty(tableName) || !dbTableNames.Contains(tableName))
                continue;
            if (!dbTableColumnMap.ContainsKey(tableName))
            {
                dbTableColumnMap[tableName] = new HashSet<string>(
                    dbProvider.DbMaintenance.GetColumnInfosByTableName(tableName).Select(c => c.DbColumnName),
                    StringComparer.OrdinalIgnoreCase);
            }
        }

        // 筛选需要初始化的表:表不存在 或 字段缺失
        var needTypes = entityInfos
            .Where(item =>
            {
                var tableName = item.Info.DbTableName;
                // 表不存在于数据库中 -> 需要初始化
                if (string.IsNullOrEmpty(tableName) || !dbTableNames.Contains(tableName))
                    return true;
                // 字段信息未获取到 -> 默认需要初始化
                if (!dbTableColumnMap.TryGetValue(tableName, out var dbColumns))
                    return true;
                // 存在实体字段不在数据库中 -> 需要初始化 (排除导航属性和已忽略列)
                return item.Info.Columns.Any(c =>
                    c.Navigat == null &&           // 排除导航属性
                    !c.IsIgnore &&                 // 排除已忽略的列
                    !string.IsNullOrEmpty(c.DbColumnName) &&
                    !dbColumns.Contains(c.DbColumnName));
            })
            .Select(item => item.Type)
            .ToList();

        int count = 0, sum = needTypes.Count;
        foreach (var entityType in needTypes)
        {
            Console.WriteLine($"表变更 {entityType} ({++count}/{sum})");
            if (entityType.GetCustomAttribute<SplitTableAttribute>() == null)
                dbProvider.CodeFirst.InitTables(entityType);
        }
        // 批量创建 dbProvider.CodeFirst.InitTables(needTypes.ToArray());

        // 分表仍需逐个创建(SqlSugar 分表 API 不支持批量数组)
        count = 0; sum = splitTypes.Count;
        foreach (var entityType in splitTypes)
        {
            Console.WriteLine($"分表变更 {entityType} ({++count}/{sum})");
            dbProvider.CodeFirst.SplitTables().InitTables(entityType);
        }
    }

    // 初始化种子数据
    if (config.SeedSettings.EnableInitSeed)
    {
        _isHandlingSeedData = true;

        Console.WriteLine($"初始化种子数据 {config.DbType} - {config.ConfigId}");
        var seedDataTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.GetInterfaces().Any(i => i.HasImplementedRawGeneric(typeof(ISqlSugarEntitySeedData<>))))
            .WhereIF(config.SeedSettings.EnableIncreSeed, u => u.IsDefined(typeof(IncreSeedAttribute), false))
            .OrderBy(u => u.GetCustomAttributes(typeof(SeedDataAttribute), false).Length > 0 ? ((SeedDataAttribute)u.GetCustomAttributes(typeof(SeedDataAttribute), false)[0]).Order : 0).ToList();

        int count = 0, sum = seedDataTypes.Count;
        foreach (var seedType in seedDataTypes)
        {
            var entityType = seedType.GetInterfaces().First().GetGenericArguments().First();
            if (config.ConfigId.ToString() == SqlSugarConst.MainConfigId) // 默认库(有系统表特性、没有日志表和租户表特性)
            {
                if (entityType.GetCustomAttribute<SysTableAttribute>() == null && (entityType.GetCustomAttribute<LogTableAttribute>() != null || entityType.GetCustomAttribute<TenantAttribute>() != null))
                    continue;
            }
            else if (config.ConfigId.ToString() == SqlSugarConst.LogConfigId) // 日志库
            {
                if (entityType.GetCustomAttribute<LogTableAttribute>() == null)
                    continue;
            }
            else
            {
                var att = entityType.GetCustomAttribute<TenantAttribute>(); // 自定义的库
                if (att == null || att.configId.ToString() != config.ConfigId.ToString()) continue;
            }

            var instance = Activator.CreateInstance(seedType);
            var hasDataMethod = seedType.GetMethod("HasData");
            var seedData = ((IEnumerable)hasDataMethod?.Invoke(instance, null))?.Cast<object>();
            if (seedData == null) continue;

            var entityInfo = dbProvider.EntityMaintenance.GetEntityInfo(entityType);


            if (entityType.GetCustomAttribute<SplitTableAttribute>(true) != null)
            {
                //拆分表的操作需要实体类型,而通过反射很难实现
                //所以,这里将Init方法写在“种子数据类”内部,再传入 db 反射调用
                var hasInitMethod = seedType.GetMethod("Init");
                var parameters = new object[] { db };
                hasInitMethod?.Invoke(instance, parameters);
            }
            else
            {
                if (entityInfo.Columns.Any(u => u.IsPrimarykey))
                {
                    // 按主键进行批量增加和更新
                    var storage = dbProvider.StorageableByObject(seedData.ToList()).ToStorage();

                    // 先修改再插入,否则会更新修改时间字段
                    if (seedType.GetCustomAttribute<IgnoreUpdateSeedAttribute>() == null) // 有忽略更新种子特性时则不更新
                    {
                        int updateCount = storage.AsUpdateable.IgnoreColumns(entityInfo.Columns.Where(u => u.PropertyInfo.GetCustomAttribute<IgnoreUpdateSeedColumnAttribute>() != null).Select(u => u.PropertyName).ToArray()).ExecuteCommand();
                        Console.WriteLine($"  修改 {updateCount}/{seedData.Count()} 条记录");
                    }
                    int insertCount = storage.AsInsertable.ExecuteCommand();
                    if (insertCount > 0)
                    {
                        Console.WriteLine($"添加数据 {entityInfo.DbTableName} ({++count}/{sum},种子数据:{seedData.Count()}) 插入 {insertCount} 条记录");
                    }
                }
                else
                {
                    // 无主键则只进行插入
                    if (!dbProvider.Queryable(entityInfo.DbTableName, entityInfo.DbTableName).Any())
                        dbProvider.InsertableByObject(seedData.ToList()).ExecuteCommand();
                }
            }
        }
        _isHandlingSeedData = false;
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值