C#上传excel,解析主从表,1W数据快速插入数据库,5s完成

news/2024/9/17 22:48:53/文章来源:https://www.cnblogs.com/cnblogsName/p/18368189

参考文章
net core天马行空系列-各大数据库快速批量插入数据方法汇总
ExcelMapper

Controller核心代码

[HttpPost]
public async Task<IActionResult> ImportToDoItems(IFormFile file)
{if (file == null || file.Length == 0){return BadRequest("File is empty");}using var stream = new MemoryStream();using (MiniProfiler.Current.Step("ToStram")){await file.CopyToAsync(stream);stream.Position = 0;}IEnumerable<ImportToDoItemModel> importModels;using (MiniProfiler.Current.Step("Convert")){//解析文件为强类型集合importModels = ExcelMapperConvertor.Convert<ImportToDoItemModel, ImportToDoItemModelValidator>(stream);}//插入数据库using (MiniProfiler.Current.Step("DataBase")){//导入的主表名称var importMasterNames = importModels.Select(x => x.Name).Distinct();//数据库中存在的主表var existMasters = await _dbContext.Set<ToDoMaster>().Where(x => importMasterNames.Contains(x.Name)).ToListAsync();//数据库中存在的主表名称var existMasterNames = existMasters.Select(x => x.Name);//需要插入的主表名称(数据库中不存在)var insertMasterNames = importMasterNames.Where(x => !existMasterNames.Contains(x));//插入主表,直接用dbContext插入var insertMasters = insertMasterNames.Select(name => new ToDoMaster(){Id = YitIdInitHelper.NextId(),Name = name});await _dbContext.AddRangeAsync(insertMasters);//插入从表,从表用SqlBulkCopyvar creationTime = DateTime.Now;var insertToDoItems = importModels.Select(x => new ToDoItem(){Id = YitIdInitHelper.NextId(),ToDoMasterId = allMasterNames[x.Name].Id,Text = x.Text,Count = x.Count,IsDeleted = false,CreationTime = creationTime,});var connectionString = "Server=localhost; Database=MyABP7NET6Db; Trusted_Connection=True;TrustServerCertificate=True;Integrated Security=True;";using (var dbConnection = new SqlConnection(connectionString)){dbConnection.Open();using var sqlBulkCopy = new SqlBulkCopy(dbConnection, SqlBulkCopyOptions.KeepIdentity, null);sqlBulkCopy.BatchSize = 20000;//表名sqlBulkCopy.DestinationTableName = "ToDoItems_202408";//针对列名做一下映射sqlBulkCopy.ColumnMappings.Add("Id", "Id");sqlBulkCopy.ColumnMappings.Add("ToDoMasterId", "ToDoMasterId");sqlBulkCopy.ColumnMappings.Add("Text", "Text");sqlBulkCopy.ColumnMappings.Add("Count", "Count");sqlBulkCopy.ColumnMappings.Add("IsDeleted", "IsDeleted");sqlBulkCopy.ColumnMappings.Add("CreationTime", "CreationTime");//将实体类列表转换成dataTablevar table = insertToDoItems.ToDataTable();sqlBulkCopy.WriteToServer(table);}//await _dbContext.AddRangeAsync(insertToDoItems);await _dbContext.SaveChangesAsync();}return Ok(new object[] { importModels.Count() });
}

MiniProfile监控数据

浏览器监控数据

Model及其校验类

public class ImportToDoItemModel
{// 主表字段public string Name { get; set; }// 从表字段public string Text { get; set; }// 从表字段public int Count { get; set; }
}public class ImportToDoItemModelValidator : AbstractValidator<ImportToDoItemModel>
{public ImportToDoItemModelValidator(){RuleFor(x => x.Name).NotEmpty();RuleFor(x => x.Count).ExclusiveBetween(0, 10001).WithMessage("Count 不符合要求");}
}

ExcelMapperConvertor封装

public class ExcelMapperConvertor
{/// <summary>/// ExcelMapper 将文件流(内存流)转为强类型集合/// FluentValidation校验转换后的数据是否符合业务要求/// 如果校验失败直接报错/// </summary>public static IEnumerable<T> Convert<T, TValidator>(Stream stream) where TValidator : AbstractValidator<T>, new(){var importer = new ExcelMapper(stream);var validator = new TValidator();try{//此处如果转换出错,会继续执行,直到遍历到错误一行时,才会报错var results = importer.Fetch<T>();// 遍历到错误一行时,才会报错foreach (var result in results){var validationResult = validator.Validate(result);if (!validationResult.IsValid){foreach (var error in validationResult.Errors){throw new Exception($"{error.PropertyName}:{error.AttemptedValue} {error.ErrorMessage}");}}}return results;}catch (Exception ex){throw new Exception(ex.Message);}}
}

DataTableHelper封装

public static class DataTableHelper
{public static ConcurrentDictionary<string, object> CacheDictionary = new ConcurrentDictionary<string, object>();/// <summary>/// 构建一个object数据转换成一维数组数据的委托/// </summary>/// <param name="objType"></param>/// <param name="propertyInfos"></param>/// <returns></returns>public static Func<T, object[]> BuildObjectGetValuesDelegate<T>(List<PropertyInfo> propertyInfos) where T : class{var objParameter = Expression.Parameter(typeof(T), "model");var selectExpressions = propertyInfos.Select(it => BuildObjectGetValueExpression(objParameter, it));var arrayExpression = Expression.NewArrayInit(typeof(object), selectExpressions);var result = Expression.Lambda<Func<T, object[]>>(arrayExpression, objParameter).Compile();return result;}/// <summary>/// 构建对象获取单个值得/// </summary>/// <param name="modelExpression"></param>/// <param name="propertyInfo"></param>/// <returns></returns>public static Expression BuildObjectGetValueExpression(ParameterExpression modelExpression, PropertyInfo propertyInfo){var propertyExpression = Expression.Property(modelExpression, propertyInfo);var convertExpression = Expression.Convert(propertyExpression, typeof(object));return convertExpression;}public static DataTable ToDataTable<T>(this IEnumerable<T> source, List<PropertyInfo> propertyInfos = null, bool useColumnAttribute = false) where T : class{var table = new DataTable("template");if (propertyInfos == null || propertyInfos.Count == 0){propertyInfos = typeof(T).GetProperties().Where(it => it.CanRead).ToList();}foreach (var propertyInfo in propertyInfos){var columnName = useColumnAttribute ? (propertyInfo.GetCustomAttribute<ColumnAttribute>()?.Name ?? propertyInfo.Name) : propertyInfo.Name;table.Columns.Add(columnName, ChangeType(propertyInfo.PropertyType));}Func<T, object[]> func;var key = typeof(T).FullName + string.Join("", propertyInfos.Select(it => it.Name).ToList());//propertyInfos.Select(it => it.Name).ToList().StringJoin(); if (CacheDictionary.TryGetValue(key, out var cacheFunc)){func = (Func<T, object[]>)cacheFunc;}else{func = BuildObjectGetValuesDelegate<T>(propertyInfos);CacheDictionary.TryAdd(key, func);}foreach (var model in source){var rowData = func(model);table.Rows.Add(rowData);}return table;}private static Type ChangeType(Type type){if (type.IsNullable()){type = Nullable.GetUnderlyingType(type);}return type;}public static bool IsNullable(this Type type){// 检查类型是否是System.Nullable<T>的实例return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/784010.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux scp 文件传输

scp将本服务器的文件传输到远程服务器 基本语法 scp `[源路径]` `[目标服务器]`:`[目标路径]`样例 将本服务器123.txt文件传输到远程服务器并重命名为456.txt scp 123.txt user@remote_server:/home/tabu/456.txt使用-r选项复制整个目录 scp -r tabu/* user@remote_server:/hom…

Android libusb

一、环境:配置NDK环境 1、下载libusb源码: https://github.com/libusb/libusb/releases,如下图所示2、删除一些和Android平台无关的文件,删除后的文件如下图所示:思考问题:Android是怎么获取usb设备?如上图所示:连接adb shell,然后cd到/sys/bus/usb/devices/目录,命令…

《花100块做个摸鱼小网站! 》第三篇—热搜表结构设计和热搜数据存储

⭐️基础链接导航⭐️ ☁️ 阿里云活动地址 🐟 上班摸鱼小网站地址 💻 源码库地址一、前言 大家好呀,我是summo,第一篇已经教会大家怎么去阿里云买服务器,以及怎么搭建JDK、Redis、MySQL这些环境。第二篇我们把后端的应用搭建好了,并且完成了第一个爬虫(抖音)。那么这一…

关于STM32H750打破flash--2M限制的简单办法

STM32H750VBTx的flash官方规定只能使用128K的flash,但是其实是可以绕过限制,使用其片内2M的flash空间。 这里介绍一种较为简单的实现的办法,这个办法不同网络上介绍的办法,可以在keil上较轻松地实现。因为它可以使用较高STM32CubeMX(6.12.0)和keil(5.29)的版本。 首先按…

c语言中读入整型数据和浮点型数据

001、读入整型数据[root@PC1 test]# ls test.c [root@PC1 test]# cat test.c ## 测试脚本 #include <stdio.h>int main(void) {int i; //声明整型变量puts("please input an integer.");printf("input an i…

CSP24

学了些DP 学校题库有\(BUG\)首先要满足条件\(x,y\)的二进制有1的位必然包含\(a\),然后让\(s-2a\),也就是除去二进制包含\(a\)有1的位,然后\(<0\)肯定无解,其次是如果有与\(a\)同一级的含\(1\)二进制位也不合法点击查看代码 #include <bits/stdc++.h> #define speed()…

用for循环输出数组与初识增强for循环

1.定义一个数组2.使用for循环设置编码3.输出带有编码的数组使用增强for循环输出数组 1.依旧是定义数组 2.设置一个新的变量x用于替代数组 3.直接输出变量x即可

线程不安全问题实例

package com.shujia.day19.sellTickets;/*使用Runnable的方式实现为了模拟更加真实的售票情况,我们加入延迟问题:我们加入了延迟之后,发现a. 有重复售卖同一张票的情况(原因1)b. 还出现了一个不该出现的票数据,比如第0张票,第-1张票(原因2)原因:1. cpu小小的时间片,…

24年首批!上海通管通报违规app涉及欧莱雅、玛莎拉蒂

8月16日上海市通信管理局官方微信公众号“上海通信圈”发布《上海市通信管理局关于侵害用户权益行为app的通报(2024年第一批)》。本次app通报为2024年第一批。内容显示本次共通报26款移动互联网应用程序涉及app和小程序。 应用来源:本次检测的应用来源均为主流分发平台如应用…

ControlNeXt: Powerful and Efficient Control for Image and Video Generation(2024,8)

ControlNeXt: Powerful and Efficient Control for Image and Video Generation(2024,8) paper Github 进一步在ControlNet上进行了改进,主要针对一下两点对于每一个模块添加一个Zero-Conv也会占用很多显存. Zero-Conv两个模态的输出的mean、var具有差异,导致收敛很慢.针对1,使…

033、Vue3+TypeScript基础,路由传参时候把层级脱掉

01、Datail.vue代码如下:<template><ul class="news-list"><li>编号:{{ route.query.id }}</li><li>编号:{{ route.query.title }}</li><li>编号:{{ route.query.content }}</li></ul> </template>…

Tarjan 之 SCC 与 缩点

这篇文章将讲述作者对 Tarjan求SCC与缩点(不是割点)的理解 让我们开始吧! Tarjan SCC 与 缩点 既然要求 \(SCC\) 那我们先要弄明白 什么是 SCC SCC 指的是强连通分量 强连通指的是若一张有向图的节点两两互相可达,则这张图是强连通的 而强连通分量 指的是一个极大的连通子图…