// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 // // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 // // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! namespace Admin.NET.Core; /// /// 表种子数据代码生成策略类 /// [CodeGenStrategy(CodeGenSceneEnum.TableSeedData)] public class TableSeedDataCodeGenStrategy : CodeGenEntityStrategyBase, ISingleton { private readonly TemplateContextOutput _template = new() { Name = "SeedData.cs.vm", OutPath = "SeedData/{ModuleName}SeedData.cs" }; public override async Task> GenerateCode(CreateSeedDataInput input) { var config = _dbConnectionOption.ConnectionConfigs.FirstOrDefault(u => u.ConfigId.ToString() == input.ConfigId) ?? throw Oops.Oh(ErrorCodeEnum.db1004); input.Position = string.IsNullOrWhiteSpace(input.Position) ? "X.Core" : input.Position; var db = _db.AsTenant().GetConnectionScope(input.ConfigId); var tableInfo = db.DbMaintenance.GetTableInfoList(false).First(u => u.Name == input.TableName); // 表名 List dbColumnInfos = db.DbMaintenance.GetColumnInfosByTableName(input.TableName, false); // 所有字段 IEnumerable entityInfos = await GetEntityInfos(); Type entityType = null; foreach (var item in entityInfos) { if (tableInfo.Name.ToLower() != (config.DbSettings.EnableUnderLine ? UtilMethods.ToUnderLine(item.DbTableName) : item.DbTableName).ToLower()) continue; entityType = item.Type; break; } if (entityType == null) throw Oops.Oh(ErrorCodeEnum.db1003); input.EntityName = entityType.Name; input.SeedDataName = entityType.Name + "SeedData"; if (!string.IsNullOrWhiteSpace(input.Suffix)) input.SeedDataName += input.Suffix; // 查询所有数据 var query = db.QueryableByObject(entityType); // 优先用创建时间排序 DbColumnInfo orderField = dbColumnInfos.FirstOrDefault(u => u.DbColumnName.ToLower() is "create_time" or "createtime"); if (orderField != null) query = query.OrderBy(orderField.DbColumnName); // 优先用创建时间排序,再使用第一个主键排序 if (dbColumnInfos.Any(u => u.IsPrimarykey)) query = query.OrderBy(dbColumnInfos.First(u => u.IsPrimarykey).DbColumnName); var records = ((IEnumerable)await query.ToListAsync()).ToDynamicList(); // 过滤已存在的数据 if (input.FilterExistingData && records.Any()) { // 获取实体类型-所有种数据数据类型 var entityTypes = App.EffectiveTypes.Where(u => u.FullName != null && !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false) && u.FullName.EndsWith("." + input.EntityName)) .Where(u => !u.GetCustomAttributes().Any()) .ToList(); if (entityTypes.Count == 1) // 只有一个实体匹配才能过滤 { // 获取实体的主键对应的属性名称 var pkInfo = entityTypes[0].GetProperties().FirstOrDefault(u => u.GetCustomAttribute()?.IsPrimaryKey == true); if (pkInfo != null) { var seedDataTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.GetInterfaces() .Any(i => i.HasImplementedRawGeneric(typeof(ISqlSugarEntitySeedData<>)) && i.GenericTypeArguments[0] == entityTypes[0])).ToList(); // 可能会重名的种子数据不作为过滤项 string doNotFilterFullName1 = $"{input.Position}.SeedData.{input.SeedDataName}"; string doNotFilterFullName2 = $"{input.Position}.{input.SeedDataName}"; // Core中的命名空间没有SeedData PropertyInfo idPropertySeedData = records[0].GetType().GetProperty("Id"); for (int i = seedDataTypes.Count - 1; i >= 0; i--) { string fullName = seedDataTypes[i].FullName; if ((fullName == doNotFilterFullName1) || (fullName == doNotFilterFullName2)) continue; // 删除重复数据 var instance = Activator.CreateInstance(seedDataTypes[i]); var hasDataMethod = seedDataTypes[i].GetMethod("HasData"); var seedData = ((IEnumerable)hasDataMethod?.Invoke(instance, null))?.Cast(); if (seedData == null) continue; List recordsToRemove = new(); foreach (var record in records) { object recordId = pkInfo.GetValue(record); if (seedData.Select(d1 => idPropertySeedData.GetValue(d1)).Any(dataId => recordId != null && dataId != null && recordId.Equals(dataId))) recordsToRemove.Add(record); } foreach (var itemToRemove in recordsToRemove) records.Remove(itemToRemove); } } } } // 获取所有字段信息 var propertyList = entityType.GetProperties().Where(x => false == (x.GetCustomAttribute()?.IsIgnore ?? false)).ToList(); for (var i = 0; i < propertyList.Count; i++) { if (propertyList[i].Name != nameof(EntityBaseId.Id) || !(propertyList[i].GetCustomAttribute()?.IsPrimaryKey ?? true)) continue; var temp = propertyList[i]; for (var j = i; j > 0; j--) propertyList[j] = propertyList[j - 1]; propertyList[0] = temp; } // 拼接种子数据 var recordList = records.Select(obj => string.Join(", ", propertyList.Select(prop => { var propType = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType; object value = prop.GetValue(obj); if (value == null) value = "null"; else if (propType == typeof(string)) { value = (value as string)?.Replace("\"", "\"\""); value = $"@\"{value}\""; } else if (propType.IsEnum) { value = $"{propType.Name}.{value}"; } else if (propType == typeof(bool)) { value = (bool)value ? "true" : "false"; } else if (propType == typeof(DateTime)) { value = $"DateTime.Parse(\"{((DateTime)value):yyyy-MM-dd HH:mm:ss.fff}\")"; } return $"{prop.Name}={value}"; }))).ToList(); var engine = new TableSeedDataEngine { NameSpace = $"{input.Position}.SeedData", EntityName = input.EntityName, Description = tableInfo.Description, RecordList = recordList }; var result = await RenderAsync(_template.DeepCopy(), (context, builder) => _viewEngine.RunCompile(context, engine, builder)); result.OutPath = Path.Combine(Path.Combine(new DirectoryInfo(App.WebHostEnvironment.ContentRootPath).Parent!.FullName, input.Position), result.OutPath.Replace("{ModuleName}", input.EntityName)); return [result]; } /// /// 获取库表信息 /// /// private async Task> GetEntityInfos() { var type = typeof(SugarTable); var types = new List(); var entityInfos = new List(); if (_codeGenOption.EntityAssemblyNames != null) foreach (var asm in _codeGenOption.EntityAssemblyNames.Select(Assembly.Load)) types.AddRange(asm.GetExportedTypes().ToList()); Type[] cosType = types.Where(u => IsMyAttribute(Attribute.GetCustomAttributes(u, true))).ToArray(); foreach (var c in cosType) { var sugarAttribute = c.GetCustomAttributes(type, true).FirstOrDefault(); var des = c.GetCustomAttributes(typeof(DescriptionAttribute), true); var description = des.Length > 0 ? ((DescriptionAttribute)des[0]).Description : ""; entityInfos.Add(new EntityInfo { EntityName = c.Name, DbTableName = sugarAttribute == null ? c.Name : ((SugarTable)sugarAttribute).TableName, TableDescription = description, Type = c }); } return await Task.FromResult(entityInfos); bool IsMyAttribute(Attribute[] o) { return o.Any(a => a.GetType() == type); } } }