做网站的国标有哪些网站开发实施方案
web/
2025/10/5 3:20:02/
文章来源:
做网站的国标有哪些,网站开发实施方案,软件开发服务外包,公司网站怎么做才能吸引人大概所有的程序员应该都接触过批量插入的场景#xff0c;我也相信任何的程序员都能写出可正常运行的批量插入的代码。但怎样实现一个高效、快速插入的批量插入功能呢#xff1f;由于每个人的工作履历#xff0c;工作年限的不同#xff0c;在实现这样的一个需求时#xff0… 大概所有的程序员应该都接触过批量插入的场景我也相信任何的程序员都能写出可正常运行的批量插入的代码。但怎样实现一个高效、快速插入的批量插入功能呢由于每个人的工作履历工作年限的不同在实现这样的一个需求时可能技术选型各有不同有直接生成insert语句的有用EF的或者其他的orm框架的。其实不管是手写insert还是使用EF最终交给数据库执行的还是insert语句。下面是EF批量插入的示例代码var list new ListStudent();for (int i 0; i 100; i){list.Add(new Student { CreateTime DateTime.Now, Name zjjjjjj });}await _context.Students.AddRangeAsync(list);await _context.SaveChangesAsync();生成的脚本截图如下这种实现方式在数据量100以内时耗时还算可以。但如果要批量导入的数据达到万级的时候那耗时简直是灾难。我测试的数据如下测试数据库为mysql具体配置不详数据量耗时(s)100.0281w3.92910w31.28010w的数据已经耗时超过了30s我没有勇气测试100w数据的耗时有兴趣的可以自行测试下。下面就应该进入正题了对于较大数据量1000以上场景下的批量插入各个数据库应该都提供了相关的解决方案由于工作所限目前笔者仅接触过mysql和mssql。mysql的实现方案是LOAD DATA命令此命令接收一个csv文件然后将文件上传到数据库服务器后解析数据后插入。好在MySqlConnector提供了相关的封装不用咱们去熟悉那么复杂的命令参数。mssql实现的方案是使用SqlBulkCopy类不过此类仅接收DataTable类型的数据所以在批量插入的时候需要将数据源转换成DataTable。综上所示不管是mysql还是mssql均需要将数据源转换成指定的格式才可以使用批量导入的功能所以这一块的主要核心就是转换数据源格式。mysql需要转换成csvmssql需要转换成DataTable。下面就来一起看看具体的转换的方法。以下代码是转换csv和DataTable相关方法namespace FL.DbBulk{public static class Extension{/// summary/// 获取实体影射的表名/// /summary/// param nametype/param/// returns/returnspublic static string GetMappingName(this System.Type type){var key $batch{type.FullName};var tableName CacheService.Get(key);if (string.IsNullOrEmpty(tableName)){var tableAttr type.GetCustomAttributeTableAttribute();if (tableAttr ! null){tableName tableAttr.Name;}else{tableName type.Name;}CacheService.Add(key, tableName);}return tableName;}public static ListEntityInfo GetMappingProperties(this System.Type type){var key $ICH.King.DbBulk{type.Name};var list CacheService.GetListEntityInfo(key);if (list null){list new ListEntityInfo();foreach (var propertyInfo in type.GetProperties()){if (!propertyInfo.PropertyType.IsValueType propertyInfo.PropertyType.Name ! Nullable1 propertyInfo.PropertyType ! typeof(string)) continue;var temp new EntityInfo();temp.PropertyInfo propertyInfo;temp.FieldName propertyInfo.Name;var attr propertyInfo.GetCustomAttributeColumnAttribute();if (attr ! null){temp.FieldName attr.Name;}temp.GetMethod propertyInfo.CreateGetter();list.Add(temp);}CacheService.Add(key, list);}return list;}/// summary/// 创建cvs字符串/// /summary/// typeparam nameT/typeparam/// param nameentities/param/// param nameprimaryKey/param/// returns/returnspublic static string CreateCsvT(this IEnumerableT entities, string primaryKey ){var sb new StringBuilder();var properties typeof(T).GetMappingProperties().ToArray();foreach (var entity in entities){for (int i 0; i properties.Length; i){var ele properties[i];if (i ! 0) sb.Append(,);var value ele.Get(entity);if (ele.PropertyInfo.PropertyType.Name Nullable1){if (ele.PropertyInfo.PropertyType.GenericTypeArguments[0] typeof(DateTime)){if (value null){sb.Append(NULL);}else{sb.Append(Convert.ToDateTime(value).ToString(yyyy-MM-dd HH:mm:ss));}continue;}}if (ele.PropertyInfo.PropertyType typeof(DateTime)){sb.Append(Convert.ToDateTime(value).ToString(yyyy-MM-dd HH:mm:ss));continue;}//如果是主键string类型且值不为空if (ele.FieldName primaryKey ele.PropertyInfo.PropertyType typeof(string)){sb.Append(Guid.NewGuid().ToString());continue;}if (value null){continue;}if (ele.PropertyInfo.PropertyType typeof(string)){var vStr value.ToString();if (vStr.Contains(\)){vStr vStr.Replace(\, \\);}if (vStr.Contains(,) || vStr.Contains(\r\n) || vStr.Contains(\n)){vStr $\{vStr}\;}sb.Append(vStr);}else sb.Append(value);}sb.Append(IsWin() ? \r\n : \n);//sb.AppendLine();}return sb.ToString();}public static bool IsWin(){return RuntimeInformation.IsOSPlatform(OSPlatform.Windows);}public static string CreateCsv(this DataTable table){StringBuilder sb new StringBuilder();DataColumn colum;foreach (DataRow row in table.Rows){for (int i 0; i table.Columns.Count; i){colum table.Columns[i];if (i ! 0) sb.Append(,);if (colum.DataType typeof(string)){var vStr row[colum].ToString();if (vStr.Contains(\)){vStr vStr.Replace(\, \\);}if (vStr.Contains(,) || vStr.Contains(\r\n) || vStr.Contains(\n)){vStr $\{vStr}\;}sb.Append(vStr);}else sb.Append(row[colum]);}sb.Append(IsWin() ? \r\n : \n);}return sb.ToString();}public static DataTable ToDataTableT(this IEnumerableT list, string primaryKey ){var type typeof(T);//获取实体映射的表名var mappingName type.GetMappingName();var dt new DataTable(mappingName);//获取实体映射的属性列表var columns type.GetMappingProperties();dt.Columns.AddRange(columns.Select(x new DataColumn(x.FieldName)).ToArray());foreach (var data in list){var row dt.NewRow();foreach (var entityInfo in columns){var value entityInfo.Get(data);if (primaryKey entityInfo.FieldName entityInfo.PropertyInfo.PropertyType typeof(string)){row[entityInfo.FieldName] value ?? Guid.NewGuid().ToString();}else{row[entityInfo.FieldName] value;}}dt.Rows.Add(row);}return dt;}}}转换成DataTable方法相对简单但这里我做了个优化下当判断主键是string类型且值为空时会自动生成一个GUID并给其赋值这样做的目的是为了和EF原生的插入功能兼容。生成Csv的相对比较麻烦因为Csv是用逗号以及其他符号来区分每一行、每一列数据但经常会存在要插入的数据包含了csv的特殊符号这样情况下就需要做转义。另外还有一个需要考虑的问题linux和windows默认的换行符是有区别的windows的换行符为\r\n而linux默认的是\n所以在生成csv时需要根据不同的系统进行处理。下面来看下具体怎么调用相关的插入方法首先看下mysql的主要代码如下所示private async Task InsertCsvAsync(string csv, string tableName, Liststring columns){var fileName Path.GetTempFileName();await File.WriteAllTextAsync(fileName, csv);var conn _context.Database.GetDbConnection() as MySqlConnection;var loader new MySqlBulkLoader(conn){FileName fileName,Local true,LineTerminator Extension.IsWin() ? \r\n : \n,FieldTerminator ,,TableName tableName,FieldQuotationCharacter ,EscapeCharacter ,CharacterSet UTF8};loader.Columns.AddRange(columns);await loader.LoadAsync();}在上述的代码中首先创建一个临时文件然后将其他数据源转换的csv内容写入到文件中获取数据库连接再然后创建MySqlBulkLoader类的实例将相关参数进行复制后还需要配置字段列表最后执行LoadAsync命令。下面是mssql的批量插入的核心代码public async Task InsertAsync(DataTable table){if (table null){throw new ArgumentNullException();}if (string.IsNullOrEmpty(table.TableName)){throw new ArgumentNullException(DataTable的TableName属性不能为空);}var conn (SqlConnection)_context.Database.GetDbConnection();await conn.OpenAsync();using (var bulk new SqlBulkCopy(conn)){bulk.DestinationTableName table.TableName;foreach (DataColumn column in table.Columns){bulk.ColumnMappings.Add(column.ColumnName, column.ColumnName);}await bulk.WriteToServerAsync(table);}}以上方法相对简单在此不做更多解释。至此mysql和mssql批量的导入的方案已经介绍完毕但可能就会有人说了这跟EF好像也没什么关系呀。其实如果你有仔细看的话或许能发现我在代码中使用了一个名为_context字段此字段其实就是EF的DbContext的实例。但文章内容到此时也没有完全的和EF结合下面就来介绍下如何更优雅的将此功能集成到EF中。在.net core中接入EF的时候其实已经指定了使用的数据库类型实例代码如下services.AddDbContextMyDbContext(opt opt.UseMySql(server10.0.0.146;Databasedemo;Uidroot;Pwd123456;Port3306;AllowLoadLocalInfiletrue))既然以及指定了数据库类型那么在调用批量插入的时候应该就不需要让调用者判断是使用mysql的方法还是mssql的方法。具体怎么设计呢且耐心往下看。首先分别定义接口ISqlBulkIMysqlBulkISqlServerBulk代码如下namespace FL.DbBulk{public interface ISqlBulk{/// summary/// 批量导入数据/// /summary/// param nametable数据源/paramvoid Insert(DataTable table);/// summary/// 批量导入数据/// /summary/// param nametable数据源/paramTask InsertAsync(DataTable table);void InsertT(IEnumerableT enumerable) where T : class;Task InsertAsyncT(IEnumerableT enumerable) where T : class;}}IMysqlBulkISqlServerBulk接口继承ISqlBulk代码如下namespace FL.DbBulk{public interface IMysqlBulk : ISqlBulk{Task InsertAsyncT(string csvPath, string tableName ) where T : class;}}namespace FL.DbBulk{public interface ISqlServerBulk:ISqlBulk{}}然后创建ISqlBulk实现类namespace FL.DbBulk{public class SqlBulk : ISqlBulk{private ISqlBulk _bulk;public SqlBulk(DbContext context, IServiceProvider provider){if (context.Database.IsMySql()){_bulk provider.GetServiceIMysqlBulk();}else if (context.Database.IsSqlServer()){_bulk provider.GetServiceISqlServerBulk();}}public void Insert(DataTable table){_bulk.Insert(table);}public async Task InsertAsync(DataTable table){await _bulk.InsertAsync(table);}public void InsertT(IEnumerableT enumerable) where T : class{_bulk.Insert(enumerable);}public async Task InsertAsyncT(IEnumerableT enumerable) where T : class{await _bulk.InsertAsync(enumerable);}}}在SqlBulk的构造函数中通过context.Database的扩展方法判断数据库的类型然后再获取相应的接口的实例。再然后就是实现IMysqlBulk和ISqlServerBulk的实现类。上文已经把核心代码贴出再此为了篇幅就不贴完整代码了。再然后就是提供一个注入services的方法代码如下namespace Microsoft.Extensions.DependencyInjection{public static class ServiceCollectionExtension{public static IServiceCollection AddBatchDBT(this IServiceCollection services) where T:DbContext{services.TryAddScopedIMysqlBulk, MysqlBulk();services.TryAddScopedISqlServerBulk, SqlServerBulk();services.TryAddScopedISqlBulk, SqlBulk();services.AddScopedDbContext, T();return services;}}}有了以上代码我们就可以通过在Startup中很方便的启用批量插入的功能了。最后贴出两种插入方式对比的测试数据数据量EF默认耗时(s)ISqlBulk耗时(s)100.0280.0301w3.9291.58110w31.28015.408以上测试数据均是使用同一个mysql数据库不同配置以及网络环境下测试的数据会有差异有兴趣的可以自己试试。至此本人内容已完毕。最后贴出git地址如果思路或代码可以帮到你欢迎点赞点starhttps://github.com/fuluteam/FL.DbBulk.git
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/87132.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!