首页 > 代码库 > NPOI操作excel——利用反射机制,NPOI读取excel数据准确映射到数据库字段

NPOI操作excel——利用反射机制,NPOI读取excel数据准确映射到数据库字段

>

其实需求很明确,就是一大堆不一样的excel,每张excel对应数据库的一张表,我们需要提供用户上传excel,我们解析数据入库的功能实现。

那么,这就涉及到一个问题:我们可以读出excel的表头,但是怎么知道每个表头具体对应数据库里面的字段呢?

博主经过一段时间的思考与构思,想到一法:现在的情况是我们有excel表A,对应数据库表B,但是A与B具体属性字段的映射关系我们不知。那我们是不是可以有一个A到B的映射文件C呢?

我想,说到这,大家就很明了了...

第一步:为每张excel创建一个与数据库表对应的映射文件(xml配置文件),我们称之为规则集,当然,我们的需求是excel的列表头不变(顺序可换---原因请继续往下看)

<?xml version="1.0" encoding="utf-8" ?>
<module>
  <add firstHeaderRow="5"                lastHeaderRow="7"    sheetCount="1"   Supplementary="0"   />
  <add headerText="年份"                 propertyName="Year"                           dataType="System.Int32"/>
  <add headerText="国内生产总值"         propertyName="GDPValue"                       dataType="System.double"/>
  <add headerText="第一产业"             propertyName="PrimaryIndustryAmount"          dataType="System.double"/>
  <add headerText="第二产业"             propertyName="SecondaryIndustry_TotalAmount"  dataType="System.double"/>
  <add headerText="工业"                 propertyName="SecondaryIndustry_Industry"     dataType="System.double"/>
  <add headerText="建筑业"               propertyName="SecondaryIndustry_Construction" dataType="System.double"/>
  <add headerText="第三产业"             propertyName="ThirdIndustryAmount"            dataType="System.double"/>
  <add headerText="人均国内生产总值(元)" propertyName="GDPPerPerson"                   dataType="System.double"/>
</module>

当然,这个xml文件由于是我们自己定义的节点,所以每个单词的意义就不详细解释了。

附:excel原表如下:

其中【剔除行关键字】在后续详细代码中介绍,请继续关注微博。

表头含有层级结构,具体读取方法也在后续详细代码中介绍...今天之讲述设计思想与部分基础代码。

第二步:新建一个读取我们自己定义的xml文件的类 RegularXMLReaderService.cs:

public class RegularXMLReaderService : IRegularXMLReaderService
    {
        public List<Regular> GetXMLInfo(string xmlpath)
        {
            //xmlpath为xml的路径名称
            var reader = new XmlTextReader(xmlpath);
            var doc = new XmlDocument();
            //从指定的XMLReader加载XML文档
            doc.Load(reader);

            var headerList = new List<Regular>();
            foreach (XmlNode node in doc.DocumentElement.ChildNodes)
            {
                var header = new Regular();

                if (node.Attributes["firstHeaderRow"] != null)
                    header.HeaderRegular.Add("firstHeaderRow", int.Parse(node.Attributes["firstHeaderRow"].Value));
                if (node.Attributes["lastHeaderRow"] != null)
                    header.HeaderRegular.Add("lastHeaderRow", int.Parse(node.Attributes["lastHeaderRow"].Value));
                if (node.Attributes["sheetCount"] != null)
                    header.HeaderRegular.Add("sheetCount", int.Parse(node.Attributes["sheetCount"].Value));
                if (node.Attributes["ExcelType"] != null)
                    header.HeaderRegular.Add("ExcelType", int.Parse(node.Attributes["ExcelType"].Value));
                if (node.Attributes["Supplementary"] != null)
                    header.HeaderRegular.Add("Supplementary", int.Parse(node.Attributes["Supplementary"].Value));

                if (node.Attributes["headerText"] != null)
                    header.HeaderText = node.Attributes["headerText"].Value;
                if (node.Attributes["propertyName"] != null)
                    header.PropertyName = node.Attributes["propertyName"].Value;
                if (node.Attributes["dataType"] != null)
                    header.DataType = node.Attributes["dataType"].Value;

                headerList.Add(header);
            }
            return headerList;
        }
    }

这段代码相信大家都能看懂,不做详细解释。其中设计到一个规则集类Regular.cs

/// <summary>
    /// 模板规则类
    /// </summary>
    public class Regular
    {
        /// <summary>
        /// 表头文本   --对应excel表头名称
        /// </summary>
        public string HeaderText { set; get; }
        /// <summary>
        /// 属性名称    --对应数据库字段名称
        /// </summary>
        public string PropertyName { set; get; }
        /// <summary>
        /// 数据类型   ---对应数据库字段类型(用作判断excel数据是否合法用)
        /// </summary>
        public string DataType { set; get; }

        private Dictionary<string, int> _regular = new Dictionary<string, int>();

        /// <summary>
        /// 表头规则  --我们所定义的具体规则,如我们文中例子的起始行、结束行、表单数等等
        /// </summary>
        public Dictionary<string, int> HeaderRegular
        {
            get { return _regular; }
            set { _regular = value; }
        }
    }

这个类根据具体需求可能会有所不同,具体随设计而改变。

第三步:创建一个解析excel数据的类(excel越多越复杂,实现就越困难),此处先假设已建好(详细代码请关注后续博客)

/// <summary>
    /// Excel表格检查与数据读取接口
    /// </summary>
    public interface IExcelImportService
    {
        /// <summary>
        /// 初始化Excel数据及配置文件
        /// </summary>
        /// <param name="filePath">Excel文件</param>
        /// <param name="xmlPath">配置文件</param>
        /// <param name="nullable">可以为空</param>
        void InitDataAndConfig(string filePath, string xmlPath, bool nullable);

        /// <summary>
        /// 综合验证Excel表格符合性
        /// </summary>
        /// <param name="customValidate">某单元项自定义检验接口</param>
        /// <returns></returns>
        UploadExcelFileResult ValidateExcel(ISpecification<KeyValuePair<string, string>> customValidate);

        /// <summary>
        /// 导入EXCEL文件
        /// </summary>
        /// <typeparam name="TableDTO">数据对象DTO</typeparam>
        /// <returns>EXCEL数据集合</returns>
        List<TableDTO> Import<TableDTO>();

        /// <summary>
        /// 导入EXCEL 文件------矩阵类模板
        /// 19.6-长江干线货物流量流向
        /// 20.5-西江航运干线货物流量流向
        /// </summary>
        /// <typeparam name="TableDTO"></typeparam>
        /// <returns></returns>
        List<TableDTO> ImportForMatrix<TableDTO>();
    }

此处是已建好的解析excel接口。(具体实现请参考后续博客)

博主的设计理念是:用户上传一个excel后我们后台分三步走:

    1、初始化数据(excel文件流、对应配置文件--我们的规则集xml文件、以及一些其他的配置参数)

    2、验证excel里面的数据合格(通过验证表头可以判定用户是否错误上传excel文件,验证里面数据是否违规(格式错误等)等等需要验证的方面)。验证成功后返回基础表头、附加信息等数据让用户确认并提交;失败后提示具体失败原因,准确定位到失败数据行列数。

    3、用户看到验证通过的返回结果,确认无误后点击提交,此时读取数据入库。



至此,我们整个解析读取入库excel的流程就完成了,重在设计规则类这个设计思想,其中涉及到很多具体实现的代码,请参考后续博客专题。