说明
承接上一篇博客,本篇博文将提供一种实现Excel数据导入数据库表的具体C#实现,其实,如果只针对单纯的一个业务开发,不用想太多内容的,只要将Excel数据转换为DataTable后具体操作,也就完成了,但如果要想写出能够应对多个需求业务的程序的时候就不得不想一些能够复用的方法了。废话少说,还是具体看一下具体的实现过程:
再次列一下我们的功能需求点:
- 换列名——中文—属性名称(字段名称)
- 必要性——必要列、行数据
- 去重复——Excel数据的重复、Excel与DataTable数据的重复
- 默认列——Excel中没有,需要程序另外添加列、并赋予值的(导入题库:试卷Id—外键-参数传递)
- 换数据——汉字换为数字(男/女—1/0、是/否—1/0)
- 换外键——将外键列的数据更换为外键所在表的主键
- 关联表——第三张表:目标表的主键、关联表的主键、…
……
上面这些是项目中实际要使用的,当然还有其他的需求,暂时先不予考虑,针对于此,在这里也仅提供一种实现方式。
实现
首先,为了方便方法中的变量封装传递,我们定义了四个实体,这四个实体,主要是为了方法中参数的传递方便,不需要映射到数据库。
//Sheet节点配置对应的实体
public class ImportSheetConfigEntity
{
//表单名称(默认Shhet1)
public string Name { get; set; }
//对应数据库表表名
public string TableName { get; set; }
//数据库表主键
public string PrimaryKey { get; set; }
//数据库表主键类型
public string PkType { get; set; }
}
//Column节点配置对应实体
public class ImportColumnConfigEntity
{
//Excel中的列名称
public string Name { get; set; }
//Excel列对应数据库的字段名(使用映射的话也为实体属性名)
public string Field { get; set; }
//是否是必要列
public string IsNecessary { get; set; }
//是否验证重复性
public string IsVerifyRepeat { get; set; }
//是否为外键
public string IsForeignKey { get; set; }
//是否与其他表存在多对多关系
public string IsM2N { get; set; }
//是否为有效列
public string IsActive { get; set; }
//是否为默认数据列
public string IsDefault { get; set; }
//默认数据列类型
public string DataType { get; set; }
//关联外键配置的属性实体
public ImportForeignKeyConfigEntity enForeignKey { get; set; }
}
//外键配置相关实体
public class ImportForeignKeyConfigEntity
{
//外键字段所在表
public string TableName { get; set; }
//Excel中对应外键表的列名
public string FieldName { get; set; }
//外键表主键
public string PrimaryKey { get; set; }
//关联是否可用字段相关的配置实体
public ImportAvailableFieldConfigEntity enAvaliableFieldConfig{ get; set; }
}
//是否可用对应字段的配置实体
public class ImportAvailableFieldConfigEntity
{
//是否可用
public string IsExistAvailableField { get; set; }
//数据库字段名称
public string Field { get; set; }
//为可用时的数据存储值
public string Value { get; set; }
}
//第三张表配置信息
public class ImportThirdTableConfigEntity
{
//第三张表表名
public string ThirdTable { get; set; }
//第三张表主键
public string PrimaryKey { get; set; }
//第三张表主键类型
public string PrimaryKeyType { get; set; }
//存储于第三张表中的目标表主键
public string FirstPK { get; set; }
//存储于第三张表中的目标表主键的类型
public string FirstPKType { get; set; }
//存储于第三张表中的多关联表的主键
public string SecondPK { get; set; }
//存储于第三张表中的多关联表的主键类型
public string SecondPKType { get; set; }
//与目标表关联的表表名
public string RelatedTable { get; set; }
//与目标表关联的表主键
public string RelatedKey { get; set; }
//与目标表关联的表字段(Excel中相关数据列)
public string RelatedName { get; set; }
//是否存在默认数值列
public string isDefaultRelated { get; set; }
//是否可用对应字段的配置实体关联
public ImportAvailableFieldConfigEntity enAvailableField { get; set; }
//多对多关系分隔符
public string Separator { get; set; }
//其他数据列:field--datatype
public Dictionary<string, string> dicField { get; set; }
//其他列数据值
public Dictionary<string, string> dicDefaultField { get; set; }
}
看一下我们的操作类图:
1.导入Excel对外开放类:ExcelImportOperator
/// <summary>
/// 导入Excel操作入口类
/// </summary>
public class ExcelImportOperator
{
private ExcelImportHelper excelImportHelper;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="isBasicOnXML">是否是基于XML的Excel解析(默认为true)</param>
public ExcelImportOperator(Boolean isBasicOnXML = true)
{
if (isBasicOnXML)
{
excelImportHelper = new ExcelImportHelperOfXML();
}
else
{
excelImportHelper = new ExcelImportHelperOfExcel();
}
}
/// <summary>
/// 导入Excel数据至DB的方法
/// </summary>
/// <param name="strPath">导入Excel文件全路径</param>
/// <param name="strXMLName">相关XML名称</param>
/// <param name="dicDefaultColumn">默认列数据</param>
/// <param name="strDBKey">数据库连接WebConfig配置键值</param>
/// <returns>过程中出现的问题数据</returns>
public Dictionary<int, DataTable> ImportExcel(string strPath, string strXMLName, Dictionary<string, string> dicDefaultColumn, string strDBKey)
{
//得到导入目标表的DataTable
Dictionary<int, DataTable> dicTargetTable = this.GetImportTable(strPath, strXMLName, dicDefaultColumn, strDBKey);
//得到导入第三张表的DataTable
Dictionary<int, DataTable> dicThirdTable = this.GetThirdTable();
//得到过程中出现的问题表
Dictionary<int, DataTable> dicErrorTable = this.GetErrorTable();
SQLHelper sqlHelper = new SQLHelper();
//执行隐式事务
try
{
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
for (int intTableIndex = 0; intTableIndex < dicTargetTable.Count; intTableIndex++)
{
if (dicTargetTable[intTableIndex].Rows.Count > 0)
{
DataTable dtTarget = dicTargetTable[intTableIndex];
sqlHelper.InsertTable(strDBKey, dtTarget, dtTarget.TableName, dtTarget.Columns);
}
}
for (int intTableIndex = 0; intTableIndex < dicThirdTable.Count; intTableIndex++)
{
if (dicThirdTable[intTableIndex].Rows.Count > 0)
{
DataTable dtThirdTable = dicThirdTable[intTableIndex];
sqlHelper.InsertTable(strDBKey, dtThirdTable, dtThirdTable.TableName, dtThirdTable.Columns);
}
}
scope.Complete();
}
}
catch (Exception e)
{
throw new Exception(e.Message);
}
Boolean bolIsExistErrorData = false;
foreach (int intErrorTableIndex in dicErrorTable.Keys)
{
if (dicErrorTable[intErrorTableIndex].Rows.Count > 1)
{
bolIsExistErrorData = true;
}
}
if (bolIsExistErrorData)
{
return dicErrorTable;
}
return null;
}
/// <summary>
/// 获得解析后的导入Excel文件
/// </summary>
/// <param name="strPath">上传到服务器端Excel文件的相对路径</param>
/// <param name="strXMLName">配置文件相对路径</param>
/// <param name="dicDefaultColumn">默认列数据:列名-数据值</param>
/// <param name="strDBConn">调用SQLHelper的数据库连接配置文件键值</param>
/// <returns>返回转换后的DataTable数据:TableName属性为导入的表名称</returns>
public Dictionary<int, DataTable> GetImportTable(string strPath, string strXMLName, Dictionary<string, string> dicDefaultColumn, string strDBConn)
{
return excelImportHelper.GetImportTable(strPath, strXMLName, dicDefaultColumn, strDBConn);
}
/// <summary>
/// 得到解析、验证过程中出现的问题数据
/// </summary>
/// <returns>dtError的键值对</returns>
public Dictionary<int, DataTable> GetErrorTable()
{
return excelImportHelper.dicErrorTable;
}
/// <summary>
/// 得到解析、验证过程中生成的第三张表数据
/// </summary>
/// <returns></returns>
public Dictionary<int, DataTable> GetThirdTable()
{
return excelImportHelper.dicThirdTable;
}
}
2.导入Excel操作抽象类
/// <summary>
/// 导入Excel的抽象类
/// </summary>
public abstract class ExcelImportHelper
{
/// <summary>
/// 存储解析Excel过程中出现的错误数据
/// </summary>
public Dictionary<int, DataTable> dicErrorTable = new Dictionary<int, DataTable>();
/// <summary>
/// 存储解析Excel过程中生成的第三张表数据
/// </summary>
public Dictionary<int, DataTable> dicThirdTable = new Dictionary<int, DataTable>();
/// <summary>
/// 从Excel中读取数据到DataTable的方法
/// </summary>
/// <param name="strSavePath">文件保存路径</param>
/// <param name="strSheetName">Sheet名称</param>
/// <returns></returns>
protected DataTable ExcelToDataTable(string strSavePath, string strSheetName)
{
#region 读取文件Sheet,转换为DataTable
string strConn;
string strFileType = System.IO.Path.GetExtension(strSavePath);
if (string.IsNullOrEmpty(strFileType)) return null;
if (strFileType == ".xls")
strConn = "Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=" + strSavePath+ ";" + ";Extended Properties=\"Excel 8.0;HDR=YES;IMEX=1\"";
else
strConn = "Provider=Microsoft.ACE.OLEDB.12.0;" + "Data Source=" + strSavePath+ ";" + ";Extended Properties=\"Excel 12.0;HDR=YES;IMEX=1\"";
OleDbConnection conn = new OleDbConnection(strConn);
conn.Open();
OleDbDataAdapter myCommand = new OleDbDataAdapter("select * from [" + strSheetName + "$]", strConn);
DataSet myDataSet = new DataSet();
try
{
myCommand.Fill(myDataSet, "ExcelInfo");
}
catch
{
throw new Exception("配置文件的Sheet名称配置错误!");
}
finally
{
if (conn.State == ConnectionState.Open)
{
conn.Close();
myCommand.Dispose();
conn.Dispose();
}
}
DataTable table = myDataSet.Tables["ExcelInfo"].DefaultView.ToTable();
return table;
#endregion 读取文件Sheet,转换为DataTable
}
#region 列处理及准备数据
//列名操作:验证必要性以及替换列名
protected string ColumnNameOperate(ImportColumnConfigEntity enColumnConfig, ref DataTable dtExcel)
{
if (dtExcel.Columns.Contains(enColumnConfig.Name) && enColumnConfig.IsActive != "false")
{
dtExcel.Columns[enColumnConfig.Name].ColumnName = enColumnConfig.Field;
}
else if (enColumnConfig.IsNecessary == "true")
{
return "\"" + enColumnConfig.Name + "\"";
}
return "";
}
//验证Excel中是否有重复数据
protected string VerifyExcelRepeatData(ImportColumnConfigEntity enColumnConfig, ref DataTable dtExcel)
{
DataView dvExcel = new DataView(dtExcel);
if (dvExcel.Count != dvExcel.ToTable(true, enColumnConfig.Field).Rows.Count)
{
return enColumnConfig.Name;
}
return "";
}
//查询出验证唯一性的数据
protected Dictionary<string, int> QueryRepeatDataInfo(ImportColumnConfigEntity enColumnConfig, ImportAvailableFieldConfigEntity enAvailable, string strSheetTable, string strDBConnKey)
{
//验证重复的字段名称
String strFieldName = enColumnConfig.Field;
String strSql;
//导入目标表如果不存在“是否可用”的标志字段
if (enAvailable != null && enAvailable.IsExistAvailableField == "true")
{
strSql = "select " + enColumnConfig.Field + " from " + strSheetTable + " where " + enAvailable.Field + "=" + enAvailable.Value;
}
else
{
strSql = "select " + enColumnConfig.Field + " from " + strSheetTable;
}
//调用SQLHelper,查询需要验证重复的该列的所有值
DataTable dtVerifyBasicData = new SQLHelper(strDBConnKey).ExecuteQuery(strSql, CommandType.Text);
Dictionary<string, int> dicVerifyBasicData = new Dictionary<string, int>();
//验证重复字段--查询一次得出所有验证重复所需要的数据
for (int i = 0; i < dtVerifyBasicData.Rows.Count; i++)
{
dicVerifyBasicData.Add(dtVerifyBasicData.Rows[i][strFieldName].ToString(), i);
}
return dicVerifyBasicData;
}
//查询出外键数据
protected Dictionary<string, Guid> QueryForeignInfo(ImportForeignKeyConfigEntity enForeignConfig, ImportAvailableFieldConfigEntity enAvailable, string strDBConnKey)
{
String strSql;
//外键所在表如果不存在“是否可用”的标志字段
if (enAvailable != null && enAvailable.IsExistAvailableField == "true")
{
strSql = "select " + enForeignConfig.FieldName + ", " + enForeignConfig.PrimaryKey + " from " + enForeignConfig.TableName + " where " + enAvailable.Field + "=" + enAvailable.Value;
}
else
{
strSql = "select " + enForeignConfig.FieldName + ", " + enForeignConfig.PrimaryKey + " from " + enForeignConfig.TableName;
}
DataTable dtForeignKey = new DataTable();
//调用SQLHelper,查询出外键列的所有数据
dtForeignKey = new SQLHelper(strDBConnKey).ExecuteQuery(strSql, CommandType.Text);
Dictionary<string, Guid> dicForeignKey = new Dictionary<string, Guid>();
//外键字段--查询一次得出所有外键所需要的数据
for (int i = 0; i < dtForeignKey.Rows.Count; i++)
{
string strDRFieldName = dtForeignKey.Rows[i][enForeignConfig.FieldName].ToString();
if (!dicForeignKey.ContainsKey(strDRFieldName))
{
dicForeignKey.Add(strDRFieldName, new Guid(dtForeignKey.Rows[i][enForeignConfig.PrimaryKey].ToString()));
}
}
return dicForeignKey;
}
//查询出关联表数据
protected Dictionary<string, Guid> QueryThirdRelateInfo(ImportThirdTableConfigEntity enThirdTable, ImportAvailableFieldConfigEntity enAvailable, string strDBConnKey)
{
//获取关联表数据
String strSql;
//关联表如果不存在“是否可用”的标志字段
if (enAvailable != null && enAvailable.IsExistAvailableField == "true")
{
strSql = "select " + enThirdTable.RelatedName + ", " + enThirdTable.RelatedKey + " from " + enThirdTable.RelatedTable + " where " + enAvailable.Field + "=" + enAvailable.Value;
}
else
{
strSql = "select " + enThirdTable.RelatedName + ", " + enThirdTable.RelatedKey + " from " + enThirdTable.RelatedTable;
}
Dictionary<String, Guid> dicRelatedData = new Dictionary<string, Guid>();
DataTable dtRelatedTable = new DataTable();
dtRelatedTable = new SQLHelper(strDBConnKey).ExecuteQuery(strSql, CommandType.Text);
for (int i = 0; i < dtRelatedTable.Rows.Count; i++)
{
dicRelatedData.Add(dtRelatedTable.Rows[i][enThirdTable.RelatedName].ToString(), new Guid(dtRelatedTable.Rows[i][enThirdTable.RelatedKey].ToString()));
}
return dicRelatedData;
}
#endregion 列处理及准备数据
#region 向错误列表中添加当前行
/// <summary>
/// 向错误列表中添加当前行
/// </summary>
/// <param name="dtErrorRow">代表错误列表的datatable</param>
protected void AddErrorRow(ref DataTable dtError, string reason, DataRow drError)
{
//新建数据行
DataRow drAddErrorRow = dtError.NewRow();
//填充数据行
for (int i = 0; i < dtError.Columns.Count - 1; i++)
{
drAddErrorRow[i] = drError[i];
}
drAddErrorRow["错误原因"] = reason;
dtError.Rows.Add(drAddErrorRow);
}
#endregion 向错误列表中添加当前行
/// <summary>
/// 通过字符串得到相对应的类型
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
protected Type GetTypeByString(string type)
{
switch (type.ToLower())
{
case "bool":
return Type.GetType("System.Boolean", true, true);
case "byte":
return Type.GetType("System.Byte", true, true);
case "sbyte":
return Type.GetType("System.SByte", true, true);
case "char":
return Type.GetType("System.Char", true, true);
case "decimal":
return Type.GetType("System.Decimal", true, true);
case "double":
return Type.GetType("System.Double", true, true);
case "float":
return Type.GetType("System.Single", true, true);
case "int":
return Type.GetType("System.Int32", true, true);
case "uint":
return Type.GetType("System.UInt32", true, true);
case "long":
return Type.GetType("System.Int64", true, true);
case "ulong":
return Type.GetType("System.UInt64", true, true);
case "object":
return Type.GetType("System.Object", true, true);
case "short":
return Type.GetType("System.Int16", true, true);
case "ushort":
return Type.GetType("System.UInt16", true, true);
case "string":
return Type.GetType("System.String", true, true);
case "date":
case "datetime":
return Type.GetType("System.DateTime", true, true);
case "guid":
return Type.GetType("System.Guid", true, true);
default:
return Type.GetType(type, true, true);
}
}
/// <summary>
/// 抽象方法:得到解析后的DataTable数据
/// </summary>
/// <param name="strPath">文件保存路径</param>
/// <param name="strXMLName">XML文件名称</param>
/// <param name="dicDefaultColumn">默认列数据</param>
/// <param name="strDBConn">配置文件中数据库连接键</param>
/// <returns>DataTable键值对-键为int类型,仅为索引值,每个DataTable的TableName属性被赋值为导入数据库的对应表名</returns>
public abstract Dictionary<int, DataTable> GetImportTable(string strPath, string strXMLName, Dictionary<string, string> dicDefaultColumn, string strDBConn);
}
3.具体实现操作类
/// <summary>
/// 根据Excel读取XML配置解析导入Excel文件
/// </summary>
/// <param name="strPath">Excel文件路径</param>
/// <param name="strXMLName">XML文件名</param>
/// <param name="dicDefaultColumn">默认列数据</param>
/// <param name="strDBConn">调用SQLHelper的数据库连接配置键值</param>
/// <returns>返回转换后的DataTable数据:TableName属性为导入的表名称</returns>
public override Dictionary<int, DataTable> GetImportTable(string strPath, string strXMLName, Dictionary<string, string> dicDefaultColumn, string strDBConn)
{
{
//由于DataSet内table名称不能重复,因此,返回值选择用Dictionary类型
Dictionary<int, DataTable> dicImportTable = new Dictionary<int, DataTable>();
int intImportIndex = 0;
int intErrorIndex = 0;
IWorkbook workbook = null;
//NPOI方式读取Excel中数据,根据Excel文件读取多个Sheet数据
using (FileStream file = new FileStream(strPath, FileMode.Open, FileAccess.Read))
{
workbook = new HSSFWorkbook(file);
file.Close();
}
//加载对应的XML配置文件
XmlDocument doc = new XmlDocument();
string strRelativeXMLPath = ConfigurationManager.AppSettings["ExcelImportXMLPath"].ToString();
string strXMLPath = AppDomain.CurrentDomain.BaseDirectory + strRelativeXMLPath + "/" + strXMLName + ".xml";
doc.Load(strXMLPath); //加载Xml文件
XmlElement rootElem = doc.DocumentElement; //获取根节点
//根据对应的XML配置文件下的Sheet数目遍历Excel
//for (int intSheetIndex = 0; intSheetIndex < rootElem.ChildNodes.Count; intSheetIndex++)
for (int intSheetIndex = 0; intSheetIndex < workbook.NumberOfSheets; intSheetIndex++)
{
//始终读一个Sheet节点
XmlNode xnSheet = rootElem.SelectSingleNode("./Sheet");
ImportSheetConfigEntity enSheet = new ImportSheetConfigEntity();
XmlNodeList xnlColumns = xnSheet.SelectNodes("./Column");
//得到组合列节点
XmlNodeList xnlCombinationColumns = xnSheet.SelectNodes("./CombinationColumn");
#region 读取Sheet对应的数据表的信息
enSheet.Name = xnSheet.Attributes["name"] != null ? xnSheet.Attributes["name"].Value : workbook.GetSheetAt(intSheetIndex).SheetName;
enSheet.TableName = xnSheet.Attributes["table"] != null ? xnSheet.Attributes["table"].Value : null;
enSheet.PrimaryKey = xnSheet.Attributes["primaryKey"] != null ? xnSheet.Attributes["primaryKey"].Value : null;
enSheet.PkType = xnSheet.Attributes["pkType"] != null ? xnSheet.Attributes["pkType"].Value : null;
#endregion 读取Sheet对应的数据表的信息
DataTable dtExcel = ExcelToDataTable(strPath, enSheet.Name);
DataTable dtError = dtExcel.Clone();
dtExcel.TableName = enSheet.TableName;
dtError.TableName = enSheet.Name;
dtError.Columns.Add("错误原因");
string strNecessaryError = "";
string strRepeatError = "";
Dictionary<string, string> dicNecessaryColumns = new Dictionary<string, string>();
Dictionary<string, string> dicReapeatColumns = new Dictionary<string, string>();
//字段名-列名,遍历行时能够给予提示信息。
//Dictionary<string, string> dicForeignColumns = new Dictionary<string, string>();
IList<ImportColumnConfigEntity> listForeignColumnConfig = new List<ImportColumnConfigEntity>();
Dictionary<string, string> dicThirdTableColumns = new Dictionary<string, string>();
//组合几列内容,存放到一个字段内容中
Dictionary<string, IList<string>> dicListCombinationColumns = new Dictionary<string, IList<string>>();
//存放组合列分隔符:列名-分隔符
Dictionary<string, string> dicCombinationSeparator = new Dictionary<string, string>();
//数据库已有的数据
IList<Dictionary<string, int>> listDicRepeatBasicData = new List<Dictionary<string, int>>();
//与dicForeignColumns对应,内部Dic中存放具体外键值,外部IList为所有需要外键的序列。
IList<Dictionary<string, Guid>> listDicForeignKey = new List<Dictionary<string, Guid>>();
IList<Dictionary<string, Guid>> listDicThirdRelated = new List<Dictionary<string, Guid>>();
Dictionary<ImportColumnConfigEntity, Dictionary<string, string>> dicConversionColumn = new Dictionary<ImportColumnConfigEntity, Dictionary<string, string>>();
//定义存储第三张表数据的DataSet
//key:目标表字段名,value:第三张表实体
Dictionary<string, ImportThirdTableConfigEntity> dicThirdEntityMap = new Dictionary<string, ImportThirdTableConfigEntity>();
//存储默认列的键值对 列名-默认值
Dictionary<string, string> dicDefaultData = new Dictionary<string, string>();
if (enSheet.PrimaryKey == null)
{
throw new Exception("配置文件错误,请联系系统管理员!");
}
#region 添加主键列
string targetPKParm = string.Format("Excel/Sheet/Column[@field='{0}']", enSheet.PrimaryKey);
XmlNode xnPK = doc.SelectSingleNode(targetPKParm);
if (xnPK == null)
{
Type typePkType;
try
{
typePkType = this.GetTypeByString(enSheet.PkType);
}
catch (Exception)
{
throw new Exception("配置文件错误,请联系系统管理员!");
}
dtExcel.Columns.Add(enSheet.PrimaryKey, typePkType);
}
#endregion 添加主键列
#region 遍历列
for (int intColumnIndex = 0; intColumnIndex < dtExcel.Columns.Count; intColumnIndex++)
{
string strColumnName = dtExcel.Columns[intColumnIndex].ColumnName;
string targetParm = string.Format("Excel/Sheet/Column[@name='{0}']", strColumnName);//生成目标获取节点的参数
XmlNode xnColumn = doc.SelectSingleNode(targetParm);
if (xnColumn == null)
{
continue;
}
//XmlNode xnColumn = xnlColumns[intColumnIndex];
ImportColumnConfigEntity enColumnConfig = new ImportColumnConfigEntity();
enColumnConfig.Name = xnColumn.Attributes["name"] != null ? xnColumn.Attributes["name"].Value : "";
enColumnConfig.Field = xnColumn.Attributes["field"] != null ? xnColumn.Attributes["field"].Value : "";
enColumnConfig.IsNecessary = xnColumn.Attributes["isNecessary"] != null ? xnColumn.Attributes["isNecessary"].Value : "";
enColumnConfig.IsVerifyRepeat = xnColumn.Attributes["isVerifyRepeat"] != null ? xnColumn.Attributes["isVerifyRepeat"].Value : "";
enColumnConfig.IsForeignKey = xnColumn.Attributes["isForeignKey"] != null ? xnColumn.Attributes["isForeignKey"].Value : "";
enColumnConfig.IsM2N = xnColumn.Attributes["isM2N"] != null ? xnColumn.Attributes["isM2N"].Value : "";
enColumnConfig.IsActive = xnColumn.Attributes["isActive"] != null ? xnColumn.Attributes["isActive"].Value : "";
enColumnConfig.IsDefault = xnColumn.Attributes["isDefault"] != null ? xnColumn.Attributes["isDefault"].Value : "";
enColumnConfig.DataType = xnColumn.SelectSingleNode("./DataType") != null ? xnColumn.SelectSingleNode("./DataType").InnerText : "";
#region 转换数据列处理
if (xnColumn.Attributes["conversionValue"] != null)
{
string strConversionValue = xnColumn.Attributes["conversionValue"].Value;
string[] strArraySecond;
string[] strArrayFirst = strConversionValue.Split(';');
Dictionary<string, string> dicDefaultValue = new Dictionary<string, string>();
for (int i = 0; i < strArrayFirst.Length; i++)
{
strArraySecond = strArrayFirst[i].Split('-');
dicDefaultValue.Add(strArraySecond[0], strArraySecond[1]);
}
dicConversionColumn.Add(enColumnConfig, dicDefaultValue);
}
#endregion 转换数据列处理
//调用列名操作方法:验证必要性以及更换列名
strNecessaryError += this.ColumnNameOperate(enColumnConfig, ref dtExcel);
if (strNecessaryError != "")
{
throw new Exception("导入Excel文件数据中必须存在" + strNecessaryError + "列!");
}
if (enColumnConfig.IsActive == "false")
{
dtExcel.Columns.Remove(enColumnConfig.Name);
continue;
}
#region 默认数据值的列处理
if (enColumnConfig.IsDefault == "true")
{
dicDefaultData.Add(enColumnConfig.Field, dicDefaultColumn[enColumnConfig.Field]);
}
#endregion 默认数据值的列处理
#region 调用验证Excel中是否存在必要条件
if (enColumnConfig.IsNecessary == "true")
{
dicNecessaryColumns.Add(enColumnConfig.Field, enColumnConfig.Name);
}
#endregion 调用验证Excel中是否存在必要条件
#region 唯一性要求存在验证数据准备
if (enColumnConfig.IsVerifyRepeat == "true")
{
strRepeatError += this.VerifyExcelRepeatData(enColumnConfig, ref dtExcel);
if (strRepeatError != "")
{
throw new Exception("Excel中有相同的" + strRepeatError);
}
//准备验证唯一性的数据库数据
XmlNode xnAvailable = xnColumn.SelectSingleNode("./AvailableField");
ImportAvailableFieldConfigEntity enAvailable = null;
if (xnAvailable != null && xnAvailable.Attributes["isExistAvailableField"].Value == "true")
{
enAvailable = new ImportAvailableFieldConfigEntity();
enAvailable.IsExistAvailableField = "true";
enAvailable.Field = xnAvailable.InnerText;
enAvailable.Value = xnAvailable.Attributes["enableValue"] != null ? xnAvailable.Attributes["enableValue"].Value : "";
}
dicReapeatColumns.Add(enColumnConfig.Field, enColumnConfig.Name);
listDicRepeatBasicData.Add(this.QueryRepeatDataInfo(enColumnConfig, enAvailable, enSheet.TableName, strDBConn));
}
#endregion 唯一性要求存在验证数据准备
#region 如果“是否外键”属性为true,则准备外键关联数据
if (enColumnConfig.IsForeignKey == "true")
{
//准备外键数据
ImportForeignKeyConfigEntity enForeign = new ImportForeignKeyConfigEntity();
XmlNode xnForeign = xnColumn.SelectSingleNode("./ForeignKey");
enForeign.TableName = xnForeign.SelectSingleNode("./TableName").InnerText;
enForeign.FieldName = xnForeign.SelectSingleNode("./FieldName").InnerText;
enForeign.PrimaryKey = xnForeign.SelectSingleNode("./PrimaryKey").InnerText;
//查询是否存在“是否可用”字段
XmlNode xnAvailable = xnForeign.SelectSingleNode("./AvailableField");
ImportAvailableFieldConfigEntity enAvailable = null;
if (xnAvailable != null && xnAvailable.Attributes["isExistAvailableField"].Value == "true")
{
enAvailable = new ImportAvailableFieldConfigEntity();
enAvailable.IsExistAvailableField = "true";
enAvailable.Field = xnAvailable.InnerText;
enAvailable.Value = xnAvailable.Attributes["enableValue"] != null ? xnAvailable.Attributes["enableValue"].Value : "";
}
//dicForeignColumns.Add(enColumnConfig.Field, enColumnConfig.Name);
enColumnConfig.enForeignKey = enForeign;
listForeignColumnConfig.Add(enColumnConfig);
listDicForeignKey.Add(this.QueryForeignInfo(enForeign, enAvailable, strDBConn));
//TODO:更改外键列的数据类型
dtExcel.Columns.Add("F" + enColumnConfig.Field, this.GetTypeByString(enColumnConfig.DataType));
}
#endregion 如果“是否外键”属性为true,则准备外键关联数据
#region 如果“是否多对多关系”属性为true,则准备关联表数据
int intThirdIndex = 0;
if (enColumnConfig.IsM2N == "true")
{
#region 读取配置文件信息
ImportThirdTableConfigEntity enThirdTableConfig = new ImportThirdTableConfigEntity();
enThirdTableConfig.ThirdTable = xnColumn.SelectSingleNode("./ThirdTable").InnerText;
enThirdTableConfig.PrimaryKey = xnColumn.SelectSingleNode("./ThirdTablePrimaryKey").InnerText;
enThirdTableConfig.PrimaryKeyType = xnColumn.SelectSingleNode("./ThirdTablePrimaryKey").Attributes["dataType"].Value.ToLower();
enThirdTableConfig.FirstPK = xnColumn.SelectSingleNode("./ThirdTableFirstPK").InnerText;
enThirdTableConfig.FirstPKType = xnColumn.SelectSingleNode("./ThirdTableFirstPK").Attributes["dataType"].Value.ToLower();
enThirdTableConfig.SecondPK = xnColumn.SelectSingleNode("./ThirdTableSecondPK").InnerText;
enThirdTableConfig.SecondPKType = xnColumn.SelectSingleNode("./ThirdTableSecondPK").Attributes["dataType"].Value.ToLower();
enThirdTableConfig.isDefaultRelated = xnColumn.SelectSingleNode("./RelatedTable").Attributes != null ? xnColumn.SelectSingleNode("./RelatedTable").Attributes["isDefault"].Value : "";
enThirdTableConfig.RelatedTable = xnColumn.SelectSingleNode("./RelatedTable").InnerText;
enThirdTableConfig.RelatedKey = xnColumn.SelectSingleNode("./RelatedKey").InnerText;
enThirdTableConfig.RelatedName = xnColumn.SelectSingleNode("./RelatedName").InnerText;
enThirdTableConfig.Separator = xnColumn.Attributes["separator"] != null ? xnColumn.Attributes["separator"].Value : "";
//enThirdTableConfig.listOtherFiled = new List<string>();
enThirdTableConfig.dicField = new Dictionary<string, string>();
//判断第三张表是否存在其他行
XmlNode xnThirdNodes = xnColumn.SelectSingleNode("./ThirdColumns");
if (xnThirdNodes != null)
{
enThirdTableConfig.dicDefaultField = new Dictionary<string, string>();
enThirdTableConfig.dicField = new Dictionary<string, string>();
for (int i = 0; i < xnThirdNodes.ChildNodes.Count; i++)
{
XmlNode xnThirdColumn = xnThirdNodes.ChildNodes[i];
if (xnThirdColumn.Attributes["isDefault"] != null && xnThirdColumn.Attributes["isDefault"].Value == "true")
{
string strDefaultField = xnThirdColumn.Attributes["field"].Value;
string strDefaultFieldType = xnThirdColumn.Attributes["dataType"].Value;
enThirdTableConfig.dicDefaultField.Add(strDefaultField, strDefaultFieldType);
}
else
{
string strField = xnThirdColumn.Attributes["field"].Value;
string strFieldType = xnThirdColumn.Attributes["dataType"].Value;
enThirdTableConfig.dicField.Add(strField, strFieldType);
}
}
}
#region 如果关联表存在“是否可用”字段的处理
XmlNode xnAvailable = xnColumn.SelectSingleNode("./AvailableField");
ImportAvailableFieldConfigEntity enAvailable = null;
if (xnAvailable != null && xnAvailable.Attributes["isExistAvailableField"].Value == "true")
{
enAvailable = new ImportAvailableFieldConfigEntity();
enAvailable.IsExistAvailableField = "true";
enAvailable.Field = xnAvailable.InnerText;
enAvailable.Value = xnAvailable.Attributes["enableValue"] != null ? xnAvailable.Attributes["enableValue"].Value : "";
}
#endregion 如果关联表存在“是否可用”字段的处理
#endregion 读取配置文件信息
#region 准备第三张表需要的列信息以及关联数据
//标记第三张表关联列
dicThirdTableColumns.Add(enColumnConfig.Field, enColumnConfig.Name);
DataTable dtThirdTable = new DataTable(enThirdTableConfig.ThirdTable);
//第三张表(DataTable)中添加主键、关联表相关键列
dtThirdTable.Columns.Add(enThirdTableConfig.PrimaryKey, this.GetTypeByString(enThirdTableConfig.PrimaryKeyType));
dtThirdTable.Columns.Add(enThirdTableConfig.FirstPK, this.GetTypeByString(enThirdTableConfig.FirstPKType));
dtThirdTable.Columns.Add(enThirdTableConfig.SecondPK, this.GetTypeByString(enThirdTableConfig.SecondPKType));
//准备关联表数据
if (enThirdTableConfig.isDefaultRelated == "true")
{
dtThirdTable.Columns[enThirdTableConfig.SecondPK].DefaultValue = dicDefaultColumn[enThirdTableConfig.SecondPK];
}
else
{
listDicThirdRelated.Add(this.QueryThirdRelateInfo(enThirdTableConfig, enAvailable, strDBConn));
}
//第三张表添加其他列
foreach (string strField in enThirdTableConfig.dicField.Keys)
{
dtThirdTable.Columns.Add(strField).DataType = this.GetTypeByString(enThirdTableConfig.dicField[strField]);
}
//默认数据列
foreach (string strDefaultField in enThirdTableConfig.dicDefaultField.Keys)
{
dtThirdTable.Columns.Add(strDefaultField).DataType = this.GetTypeByString(enThirdTableConfig.dicDefaultField[strDefaultField]);
dtThirdTable.Columns[strDefaultField].DefaultValue = dicDefaultColumn[strDefaultField];
}
dicThirdTable.Add(intThirdIndex, dtThirdTable);
//map中添加数据
dicThirdEntityMap.Add(enColumnConfig.Field, enThirdTableConfig);
#endregion 准备第三张表需要的列信息以及关联数据
}
#endregion 如果“是否多对多关系”属性为true,则准备关联表数据
}
#endregion 遍历列
#region 遍历需要组合的列
if (xnlCombinationColumns != null)
{
//多个组合列的情况:需要循环
for (int intComColumnIndex = 0; intComColumnIndex < xnlCombinationColumns.Count; intComColumnIndex++)
{
XmlNode xnComColumn = xnlCombinationColumns[intComColumnIndex];
//判断对应字段是否存在
if (xnComColumn.Attributes["field"] != null && xnComColumn.Attributes["field"].Value != "")
{
//dtExcel中添加组合列字段
dtExcel.Columns.Add(xnComColumn.Attributes["field"].Value);
//判断分隔符是否存在
if (xnComColumn.Attributes["separator"] != null && xnComColumn.Attributes["separator"].Value != "")
{
dicCombinationSeparator.Add(xnComColumn.Attributes["field"].Value, xnComColumn.Attributes["separator"].Value);
}
XmlNodeList xnColumns = xnComColumn.SelectNodes("./Column");
IList<string> listColumnName = new List<string>();
for (int intColumnIndex = 0; intColumnIndex < xnColumns.Count; intColumnIndex++)
{
listColumnName.Add(xnColumns[intColumnIndex].Attributes["name"].Value);
}
dicListCombinationColumns.Add(xnComColumn.Attributes["field"].Value, listColumnName);
}
}
}
#endregion 遍历需要组合的列
#region 遍历行
int intRowsCount = dtExcel.Rows.Count;
//foreach (DataRow drExcel in dtExcel.Rows)
for (int intRowIndex = 0; intRowIndex < intRowsCount; intRowIndex++)
{
Boolean bolIsContinueRun = true;
//添加目标表主键列数据
//drExcel[enSheet.PrimaryKey] = Guid.NewGuid();
if (xnPK == null)
{
if (enSheet.PkType.ToLower() == "string")
{
dtExcel.Rows[intRowIndex][enSheet.PrimaryKey] = Guid.NewGuid().ToString();
}
else if (enSheet.PkType.ToLower() == "guid")
{
dtExcel.Rows[intRowIndex][enSheet.PrimaryKey] = Guid.NewGuid();
}
}
#region 验证必填数据列
//遍历需要验证唯一性的列名(listNecessaryColumns中)
foreach (string strNecessaryColumn in dicNecessaryColumns.Keys)
{
//判断必填内容是否存在数据
if (dtExcel.Rows[intRowIndex][strNecessaryColumn].ToString().Trim() == "")
{
//TODO:提示语
AddErrorRow(ref dtError, dicNecessaryColumns[strNecessaryColumn] + "为必填数据!", dtExcel.Rows[intRowIndex]);
//移除一行
dtExcel.Rows.Remove(dtExcel.Rows[intRowIndex]);
//遍历个数减去1
intRowsCount -= 1;
// 遍历索引1
intRowIndex -= 1;
bolIsContinueRun = false;
break;
}
}
#endregion 验证必填数据列
#region 验证重复数据
//遍历需要验证重复的列名
if (bolIsContinueRun)
{
int intColumnRepeatIndex = 0;
foreach (string strRepeatColumnName in dicReapeatColumns.Keys)
{
//验证重复性
if (listDicRepeatBasicData[intColumnRepeatIndex].ContainsKey(dtExcel.Rows[intRowIndex][strRepeatColumnName].ToString().Trim()))
{
AddErrorRow(ref dtError, "该行的" + dicReapeatColumns[strRepeatColumnName] + "值在系统中已经存在!", dtExcel.Rows[intRowIndex]);
dtExcel.Rows.Remove(dtExcel.Rows[intRowIndex]);
//遍历个数减去1
intRowsCount -= 1;
// 遍历索引1
intRowIndex -= 1;
bolIsContinueRun = false;
break;
}
++intColumnRepeatIndex;
}
}
#endregion 验证重复数据
#region 默认数据值的列处理
foreach (string columnName in dicDefaultData.Keys)
{
dtExcel.Rows[intRowIndex][columnName] = dicDefaultData[columnName];
}
#endregion 默认数据值的列处理
#region 处理外键的数据
if (bolIsContinueRun)
{
int intColumnForeignIndex = 0;
//foreach (string strForeignColumnName in dicForeignColumns.Keys)
foreach (ImportColumnConfigEntity enColumnConfig in listForeignColumnConfig)
{
//获取外键列的值
string strForeignValue = dtExcel.Rows[intRowIndex][enColumnConfig.Field].ToString().Trim();
//判断Dic中是否存在对应的外键数据
if (listDicForeignKey[intColumnForeignIndex].ContainsKey(strForeignValue))
{
dtExcel.Rows[intRowIndex]["F" + enColumnConfig.Field] = listDicForeignKey[intColumnForeignIndex][strForeignValue];
}
else if (enColumnConfig.IsNecessary != "true")
{
continue;
}
else
{
//TODO:外键列,没有匹配值--提示语
AddErrorRow(ref dtError, "指定的" + enColumnConfig.Name + "在系统中不存在!", dtExcel.Rows[intRowIndex]);
//外键数据不存在则从导入数据中移除
dtExcel.Rows.Remove(dtExcel.Rows[intRowIndex]);
//遍历个数减去1
intRowsCount -= 1;
// 遍历索引1
intRowIndex -= 1;
bolIsContinueRun = false;
break;
}
++intColumnForeignIndex;
}
}
#endregion 处理外键的数据
#region 转换处理简单数据
if (bolIsContinueRun)
{
foreach (ImportColumnConfigEntity enColumn in dicConversionColumn.Keys)
{
StringBuilder sbAllowValue = new StringBuilder();
foreach (string strAllowValue in dicConversionColumn[enColumn].Keys)
{
sbAllowValue.Append(strAllowValue + " ");
}
string strCellValue = dtExcel.Rows[intRowIndex][enColumn.Field].ToString();
if (!dicConversionColumn[enColumn].ContainsKey(strCellValue))
{
AddErrorRow(ref dtError, "列" + enColumn.Name + "的值只允许填入" + sbAllowValue.ToString(), dtExcel.Rows[intRowIndex]);
dtExcel.Rows.Remove(dtExcel.Rows[intRowIndex]);
//遍历个数减去1
intRowsCount -= 1;
// 遍历索引1
intRowIndex -= 1;
bolIsContinueRun = false;
break;
}
dtExcel.Rows[intRowIndex][enColumn.Field] = dicConversionColumn[enColumn][strCellValue];
}
}
#endregion 转换处理简单数据
#region 第三张表添加关联数据
if (bolIsContinueRun)
{
int intThirdColumnIndex = 0;
foreach (string strThirdRelatedColumnName in dicThirdTableColumns.Keys)
{
ImportThirdTableConfigEntity enThirdTable = dicThirdEntityMap[strThirdRelatedColumnName];
//与关联表相关的数据,Excel中暂定由逗号分隔
String[] strRelateField = dtExcel.Rows[intRowIndex][strThirdRelatedColumnName].ToString().Split(enThirdTable.Separator.ToCharArray());
//循环Excel中一个字段中的分隔数据
for (int i = 0; i < strRelateField.Length; i++)
{
DataRow drThirdTable = dicThirdTable[intThirdColumnIndex].NewRow();
#region 添加主键
if (enThirdTable.PrimaryKeyType == "guid")
{
drThirdTable[enThirdTable.PrimaryKey] = Guid.NewGuid();
}
else if (enThirdTable.PrimaryKeyType == "string")
{
drThirdTable[enThirdTable.PrimaryKey] = Guid.NewGuid().ToString();
}
#endregion 添加主键
#region 添加关联目标表主键
if (enThirdTable.FirstPKType == "guid")
{
drThirdTable[enThirdTable.FirstPK] = new Guid(dtExcel.Rows[intRowIndex][enSheet.PrimaryKey].ToString());
}
else if (enThirdTable.FirstPKType == "string")
{
drThirdTable[enThirdTable.FirstPK] = dtExcel.Rows[intRowIndex][enSheet.PrimaryKey].ToString();
}
#endregion 添加关联目标表主键
#region 添加第三张表中关联表的主键
if (enThirdTable.isDefaultRelated != "true")
{
drThirdTable[enThirdTable.SecondPK] = listDicThirdRelated[intThirdColumnIndex][strRelateField[i]];
}
//添加第三张表数据
//dsThirdTable.Tables[enThirdTable.ThirdTable].Rows.Add(drThirdTable);
dicThirdTable[intThirdColumnIndex].Rows.Add(drThirdTable);
}
++intThirdColumnIndex;
}
}
#endregion 添加第三张表中关联表的主键
#endregion 第三张表添加关联数据
#region 合并相关数据列的数据
if (bolIsContinueRun)
{
foreach (string strKeyIndex in dicListCombinationColumns.Keys)
{
IList<string> listCombinationColumn = dicListCombinationColumns[strKeyIndex];
StringBuilder sbCombinationValue = new StringBuilder();
//循环组合列中的各列
for (int intListIndex = 0; intListIndex < listCombinationColumn.Count; intListIndex++)
{
string strColumnName = listCombinationColumn[intListIndex];
string strCellValue = "";
if (dtExcel.Columns.Contains(strColumnName))
{
strCellValue = dtExcel.Rows[intRowIndex][strColumnName] != null ? dtExcel.Rows[intRowIndex][strColumnName].ToString().Trim() : "";
}
if (strCellValue != "")
{
if (intListIndex != 0)
{
sbCombinationValue.Append(dicCombinationSeparator[strKeyIndex]);
}
sbCombinationValue.Append(strCellValue);
}
}
dtExcel.Rows[intRowIndex][strKeyIndex] = sbCombinationValue;
}
}
#endregion 合并相关数据列的数据
}
#endregion 遍历行
foreach (ImportColumnConfigEntity enColumnForeign in listForeignColumnConfig)
{
dtExcel.Columns.Remove(enColumnForeign.Field);
dtExcel.Columns["F" + enColumnForeign.Field].ColumnName = enColumnForeign.Field;
}
foreach (string strThirdRelateColumnName in dicThirdTableColumns.Keys)
{
dtExcel.Columns.Remove(strThirdRelateColumnName);
}
foreach (string strKeyIndex in dicListCombinationColumns.Keys)
{
IList<string> listCombinationColumn = dicListCombinationColumns[strKeyIndex];
//循环组合列中的各列
for (int intListIndex = 0; intListIndex < listCombinationColumn.Count; intListIndex++)
{
string strColumnName = listCombinationColumn[intListIndex];
if (dtExcel.Columns.Contains(strColumnName))
{ dtExcel.Columns.Remove(strColumnName); }
}
}
dicImportTable.Add(intImportIndex, dtExcel);
++intImportIndex;
dicErrorTable.Add(intErrorIndex, dtError);
++intErrorIndex;
}
return dicImportTable;
}
}
类图中的ExcelImportHelperOfXML可看作解析Excel的另一种实现,可自行扩展。
总结
以上内容仅是对开头所提功能的一种实现,当然也是建立在上篇博客的思路上的实现,完成了基本的Excel导入解析、验证功能。其中也有需要完善的地方:1.没有加入组合列验证重复的情况;2.最后的实现方法过长,灵活性较差;
可以扩展的地方:
1.一般导入前需要提供下载模板的功能,我们可以读取之前配置的XML文件,自动生成模版,这样Excel模版也达到了复用的效果
2.可以针对XML做一些专门维护的界面,直接对XML修改也容易出现问题,这样也更大的解放了程序员。