系列文章
C#提高编程效率专辑--自动代码生成器
https://blog.csdn.net/youcheng_ge/article/details/126890673
目录
前言
本专辑主要介绍C#开发提高效率篇,平时我们开发过程中,遇到的一些重复性、机械性、简单性的问题,采用工具代替人工;编程中弯弯绕绕、复杂的逻辑,如何简化调用。总之就是给软件开发者减负,使得写代码越快越开心。
但由于篇幅有限,不能一一详细地展开,如果你对本专辑感兴趣,持续关注吧。大家有任何问题,可以评论区反馈,私信我。
一、创作背景
今天,我完成了【Excel导入工具】第一版,测试成功。本文主要和大家分享一下,我为什么要开发这个工具?
我司推进【条码管理】,一来提高信息化水平,二来方便录入单据,此时就需要有个强大的后台库,能联想到用户想要录入的数据,或者说能够让用户“以选代输”,更加快捷、准确制单。
我们是没有基础库的,但是公司有使用用友U8产品,所有U8中的基础数据可行。经协商,他们只肯开放API数据接口给我们。所以,我决定新建数据库,将数据导入我们的库中,为啥不直连呢?一是,我们为了明确职责,不希望扯皮,不影响原U8服务器性能;二是,用友物理库还是不肯开放给我们的,涉及保密可以理解。
好了,上面是我遇见的情况,其实基础数据建档工作,你们肯定也有做过,我觉得会有如下几种情况:
- 基础数据初始化工作,做企业ERP管理常会遇到,需协助业务部门创建基础档案,员工档案、部门档案、存货档案、供应商档案、仓库档案、财务科目等,往往业务人员提供一个Excel表,需要我们将数据导入系统。
- 与外部系统对接,第三方软件公司对接,对方开放了API数据接口,你需要将获得的数据存储到自己的数据库中,这是我遇到的情况。
- 同步表数据工作,当我们研发要修复一个隐蔽的bug,但是测试环境,缺乏真实业务数据,无法重现问题,需要还原某张表的数据,却不想还原整个库。
- 跨平台数据转换工作,发生在新老系统更替,原先公司用的oracle数据库,现在要换Sql Server数据库,业务数据希望转换过去。
二、解决方案
我的想法是开发一个工具,可以将Excel表格转化成SQL插入语句,这样我们拿SQL语句放数据库里一执行就成功了。
我暗自定了几点要求:
- 工具通用型,支持任何表,不能说一个业务,开发一个工具,不得累死。
- 支持Excel 97-2003 工作簿(*.xls)、Excel 工作簿(.xlsx)
- 贴合使用需要,界面友好化,功能完善,乱输瞎输不宕机。
三、程序代码
3.1 Excel表格读取
实现将Excel中sheet数据读取,并转换转换成DataTable格式。
参数说明:
fileName:文件全路径
sheetName:sheet名称(可选),不填默认第一个sheet
isFirstRowColumn:第一行(可选)
返回DataTable
using System;
using System.IO;
using System.Data;
using System.Diagnostics;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using NPOI.HSSF.UserModel;
using Microsoft.Office.Interop.Excel;
namespace ExcelImportTool
{
/// <summary>
/// Excel操作类
/// </summary>
/// Microsoft Excel 11.0 Object Library
public class ExcelHelper
{
private static Stopwatch wath = new Stopwatch();
/// <summary>
/// 读取Excel文件(文件加密,性能低)
/// </summary>
/// <param name="excelFilePath"></param>
/// <returns></returns>
public System.Data.DataTable excelToDataTable(string excelFilePath)
{
Application app = new Application();
Sheets sheets;
Workbook workbook = null;
object oMissiong = System.Reflection.Missing.Value;
System.Data.DataTable dt = new System.Data.DataTable();
wath.Start();
try
{
if (app == null)
{
return null;
}
workbook = app.Workbooks.Open(excelFilePath, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong);
//将数据读入到DataTable中——Start
sheets = workbook.Worksheets;
Worksheet worksheet = (Worksheet)sheets.get_Item(1);//读取第一张表
if (worksheet == null)
return null;
string cellContent;
int iRowCount = worksheet.UsedRange.Rows.Count;
int iColCount = worksheet.UsedRange.Columns.Count;
Range range;
//负责列头Start
DataColumn dc;
int ColumnID = 1;
range = (Range)worksheet.Cells[1, 1];
while (!string.IsNullOrEmpty(range.Text.ToString().Trim()))
{
dc = new DataColumn();
dc.DataType = System.Type.GetType("System.String");
dc.ColumnName = range.Text.ToString().Trim();
dt.Columns.Add(dc);
range = (Range)worksheet.Cells[1, ++ColumnID];
}
for (int iRow = 2; iRow <= iRowCount; iRow++)
{
DataRow dr = dt.NewRow();
for (int iCol = 1; iCol <= iColCount; iCol++)
{
range = (Range)worksheet.Cells[iRow, iCol];
cellContent = (range.Value2 == null) ? "" : range.Text.ToString();
dr[iCol - 1] = cellContent;
}
dt.Rows.Add(dr);
}
wath.Stop();
TimeSpan ts = wath.Elapsed;
//将数据读入到DataTable中——End
return dt;
}
catch
{
return null;
}
finally
{
workbook.Close(false, oMissiong, oMissiong);
System.Runtime.InteropServices.Marshal.ReleaseComObject(workbook);
workbook = null;
app.Workbooks.Close();
app.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(app);
app = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
/// <summary>
/// 读取Excel文件(未加密)
/// </summary>
/// <param name="fileName"></param>
/// <param name="sheetName"></param>
/// <param name="isFirstRowColumn"></param>
/// <returns></returns>
public System.Data.DataTable ExcelToDataTable(string fileName, string sheetName = null, bool isFirstRowColumn = true)
{
FileStream fs = null;
ISheet sheet = null;
System.Data.DataTable data = new System.Data.DataTable();
IWorkbook workbook = null;
int startRow = 1;
try
{
fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
if (fileName.IndexOf(".xlsx") > 0) // 2007版本
workbook = new XSSFWorkbook(fs);
else if (fileName.IndexOf(".xls") > 0) // 2003版本
workbook = new HSSFWorkbook(fs);
if (sheetName != null)
{
sheet = workbook.GetSheet(sheetName);
if (sheet == null) //如果没有找到指定的sheetName对应的sheet,则尝试获取第一个sheet
{
sheet = workbook.GetSheetAt(0);
}
}
else
{
sheet = workbook.GetSheetAt(0);
}
if (sheet != null)
{
IRow firstRow = sheet.GetRow(0);//读取标题
int cellCount = firstRow.LastCellNum; //一行最后一个cell的编号 即总的列数
if (isFirstRowColumn)
{
for (int i = firstRow.FirstCellNum; i < cellCount; ++i)
{
NPOI.SS.UserModel.ICell cell = firstRow.GetCell(i);
if (cell != null)
{
// cell.SetCellType(CellType.String);
string cellValue = cell.StringCellValue;
if (cellValue != null)
{
DataColumn column = new DataColumn(cellValue);
data.Columns.Add(column);
}
}
}
startRow = sheet.FirstRowNum + 1;
}
else
{
startRow = sheet.FirstRowNum;
}
//最后一列的标号
int rowCount = sheet.LastRowNum;
for (int i = startRow; i <= rowCount; ++i)
{
IRow row = sheet.GetRow(i);
if (row == null) continue; //没有数据的行默认是null
DataRow dataRow = data.NewRow();
for (int j = row.FirstCellNum; j < cellCount; ++j)
{
if (row.GetCell(j) != null) //同理,没有数据的单元格都默认是null
{
dataRow[j] = row.GetCell(j).ToString();
}
}
data.Rows.Add(dataRow);
}
}
return data;
}
catch (Exception ex)
{
Console.WriteLine("异常: " + ex.Message);
return null;
}
}
}
}
为啥我们要写成这样,为啥有些人读取表格就一个方法,循环写在一起?刚刚说了,我们要做工具,要做通用化产品,编程宗旨:要高内聚低耦合,函数方法功能单一、可扩展。写成转DataTable,以后我们某个项目有需要读表的,这个类直接拷贝进去都不带改的,这时间不就省下来和小姐姐吹牛了嘛!
3.2 生成插入脚本
根据DataTable生成插入脚本,采用最通用T-SQL脚本,目前支持Oracle、MySQL、MS SQLServer。
参数:
a_dtSource:DataTable
a_strTableName:插入表名称
返回字符串
public static class CodeFactory
{
//创建Insert语句
public static string CreateInsertSQL(DataTable a_dtSource, string a_strTableName)
{
StringBuilder l_sb = new StringBuilder();
l_sb.AppendLine($"INSERT INTO {a_strTableName} ({GetColumnsByDataTable(a_dtSource)})");
int l_intRowCount = a_dtSource.Rows.Count;
//select语句
for (int i = 0; i < l_intRowCount; i++)
{
DataRow dr = a_dtSource.Rows[i];
l_sb.Append("SELECT ");
for (int j = 0; j < a_dtSource.Columns.Count; j++)
{
if (j == 0) //第一个字段不需要逗号
{
l_sb.Append($"'{dr[j].ToString()}'");
}
else
{
l_sb.Append($",'{dr[j].ToString()}'");
}
}
l_sb.AppendLine();
if (i != l_intRowCount - 1) //最后一行不需要union all
{
l_sb.AppendLine("UNION ALL");
}
}
return l_sb.ToString();
}
//表格列
private static string GetColumnsByDataTable(DataTable dt)
{
string strColumns = null;
if (dt.Columns.Count > 0)
{
int columnNum = 0;
columnNum = dt.Columns.Count;
for (int i = 0; i < dt.Columns.Count; i++)
{
if (i == 0)
{
strColumns += dt.Columns[i].ColumnName;
}
else
{
strColumns += "," + dt.Columns[i].ColumnName;
}
}
}
return strColumns;
}
3.3 脚本写入文件
没啥好讲解的,都可以看得懂吧
public static void FileWrite(string a_TextContext, string a_FileFullPath)
{
string dir = Path.GetDirectoryName(a_FileFullPath);
if (!Directory.Exists(dir)) //目录不存在创建目录
{
Directory.CreateDirectory(dir);
}
using (FileStream fs = new FileStream(a_FileFullPath, FileMode.Create))
{
using (StreamWriter sw = new StreamWriter(fs, Encoding.UTF8))
{
sw.Write(a_TextContext);
}
}
}
3.4 调用样例
我这里就直接演示,我是如何使用上面的函数,你们根据自己需要来就OK。
//选择路径
private void BTN_Select_Click(object sender, EventArgs e)
{
OpenFileDialog dialog = new OpenFileDialog();
dialog.Title = "请选择文件";
dialog.Filter = "Excel 工作簿|*.xlsx|Excel 97-2003 工作簿|*.xls|所有文件(*.*)|*.*";
dialog.FileName = AppDomain.CurrentDomain.BaseDirectory;
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
this.text_ExcelDir.Text = dialog.FileName;
string l_strFileName = Path.GetFileNameWithoutExtension(this.text_ExcelDir.Text);
this.richTextBox1.Clear();
ExcelHelper excelHelper = new ExcelHelper();
DataTable l_dtTemp = excelHelper.ExcelToDataTable(this.text_ExcelDir.Text);
richTextBox1.AppendText(CodeFactory.CreateInsertSQL(l_dtTemp, l_strFileName));
}
}
四、成果展示
这里主要展示我开发完的程序,你们也可以在我的基础上进行个性化的修改。
主界面
选择按钮没有做UI,原生的窗体控件,选择表格路径就好了。
选择完了表格,自动出脚本。如果是采用拷贝文件路径的方式,需要点击“转换”按钮出脚本,如果清空了页面,也可以点“转换”按钮出脚本。
点击保存,自动保存为文本文件,弹出提示,并自动打开文本文件,方便处理。
到了最关键的一步,插入数据执行,完美。
喜欢本工具的可以联系我哦。
工具下载链接
链接:https://pan.baidu.com/s/1SFQRj7Rjm7MJKJYn0KBZwg?pwd=y9q9
提取码:y9q9