LinqToExcel.Extend 源码分析

简介: 废话不多说,我们直接来分析源码,首先我们查看目录结构目录结构.png目录结构功能Extend 通用扩展方法Parameter 公共实体类Parser 解析器Validate 验证工具集目录结构展开.png展开目录结构,我们能够更加请详细的分析出每个目录所完成的功能模块。

废话不多说,我们直接来分析源码,首先我们查看目录结构

img_dc73b3f5d95f8bfee14af05bcc53cdcd.png
目录结构.png

目录结构功能

  • Extend 通用扩展方法
  • Parameter 公共实体类
  • Parser 解析器
  • Validate 验证工具集
img_81d4ed6e37e9e63b789fd2870c5a05fd.png
目录结构展开.png

展开目录结构,我们能够更加请详细的分析出每个目录所完成的功能模块。
这里主要讲解工具集中最重要的一个模块Validate


要设计,我们就一定要知道自己想怎么做。
如果我对外提供接口调用,怎么样的方式是最方便,让人容易理解的,我就是朝着这个方向做的。
我希望的结果是
实例化验证对象,参数是验证文件的路径
调用验证方法,可以区分工作表验证,可以选择添加或不添加逻辑验证
验证成功或失败都返回一个对象,如果验证失败,返回的对象中要包含出错的信息(尽可能细化)


基于上述的设计理念
我定义了三个对象
RowValidate 行验证
WorkSheetValidate 工作表验证
WorkBookValidate 工作簿验证

RowValidate 行验证

RowValidate对象执行的调用方是WorkSheetValidate
Validate<T>执行返回值为 得到当前行的的出错信息集合

    /// <summary>
    /// 行验证
    /// </summary>
    public class RowValidate
    {
        public static string GetCellStation(int rowIndex, int columnIndex)
        {
            int i = columnIndex % 26;
            string cellRef = Convert.ToChar(65 + i).ToString() + (rowIndex + 1);
            return cellRef;
        }

        public static List<ErrCell> Validate<T>(int rowIndex, List<string> colNames, List<int> colIndexs, List<string> rowCellValues)
        {
            List<ErrCell> errCells = new List<ErrCell>();
            T singleT = Activator.CreateInstance<T>();

            foreach (PropertyInfo pi in singleT.GetType().GetProperties())
            {
                var propertyAttribute = (Attribute.GetCustomAttribute(pi, typeof(ExcelColumnAttribute)));
                if (propertyAttribute == null)
                {
                    continue;
                }
                var proName = ((ExcelColumnAttribute)propertyAttribute).ColumnName;
                for (int colIndex = 0; colIndex < colNames.Count; colIndex++)
                {
                    try
                    {
                        if (proName.Equals(colNames[colIndex], StringComparison.OrdinalIgnoreCase))
                        {
                            string fieldName = pi.PropertyType.GetUnderlyingType().Name;
                            string cellValue = rowCellValues[colIndex];

                            if (!String.IsNullOrWhiteSpace(cellValue))
                            {
                                //如果是日期类型,特殊判断
                                if (fieldName.Equals("DateTime"))
                                {
                                    string data = "";
                                    try
                                    {
                                        data = cellValue.ToDateTimeValue();
                                    }
                                    catch (Exception)
                                    {
                                        data = DateTime.Parse(cellValue).ToString();
                                    }
                                }
                                cellValue.CastTo(pi.PropertyType);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        errCells.Add(new ErrCell()
                        {
                            RowIndex = rowIndex,
                            ColumnIndex = colIndexs[colIndex],
                            Name = GetCellStation(rowIndex, colIndexs[colIndex]),
                            ErrMsg = ex.Message
                        });
                    }
                }
            }
            return errCells;
        }
    }

WorkBookValidate 工作簿验证

WorkBookValidate是根的验证对象。我们首先看构造函数,参数为filePath,在构造函数中,我们做的操作是:实例化N个WorkSheetValidate对象。
定义索引器,这样可以通过外部调用WorkSheetValidate的验证方法

   /// <summary>
    /// 工作簿验证
    /// </summary>
    public class WorkBookValidate
    {
        public string FilePath { get; set; }

        private List<WorkSheetValidate> _workSheetList = new List<WorkSheetValidate>();

        public List<WorkSheetValidate> WorkSheetList
        {
            get { return _workSheetList; }
            set { _workSheetList = value; }
        }

        public WorkSheetValidate this[string sheetName]
        {
            get
            {
                foreach (WorkSheetValidate sheetParameterContainer in _workSheetList)
                {
                    if (sheetParameterContainer.SheetName.Equals(sheetName))
                    {
                        return sheetParameterContainer;
                    }
                }
                throw new Exception("工作表不存在");
            }
        }

        public WorkSheetValidate this[int sheetIndex]
        {
            get
            {
                foreach (WorkSheetValidate sheetParameterContainer in _workSheetList)
                {
                    if (sheetParameterContainer.SheetIndex.Equals(sheetIndex))
                    {
                        return sheetParameterContainer;
                    }
                }
                throw new Exception("工作表不存在");
            }
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="filePath">路径</param>
        public WorkBookValidate(string filePath)
        {
            FilePath = filePath;
            var excel = new ExcelQueryFactory(filePath);
            List<string> worksheetNames = excel.GetWorksheetNames().ToList();

            int sheetIndex = 0;
            foreach (var sheetName in worksheetNames)
            {
                WorkSheetList.Add(new WorkSheetValidate(filePath, sheetName, sheetIndex++));
            }
        }
    }

WorkSheetValidate 工作表验证

这是这三个验证模块中最复杂的一个,代码就不贴全部的图了,主要讲解一下重要的地方。
首先也是构造函数,这个构造函数主要是给WorkBookVaidate调用

      public WorkSheetValidate(string filePath, string sheetName, int sheetIndex)
        {
            FilePath = filePath;
            SheetName = sheetName;
            SheetIndex = sheetIndex;

            TootarIndex = 0;
        }

验证方法说明
这是一个泛型方法,方法逻辑很简单
首先验证数据有效性 ValidateParameter
如果返回的错误集合为空,验证逻辑有效性ValidateMatching
最后返回验证集合

    public Verification StartValidate<T>(List<CellMatching<T>> rowValidates = null)
        {
            List<ErrCell> errCells = this.ValidateParameter<T>(TootarIndex);
            if (!errCells.Any())
            {
                TootarIndex += 1;
                errCells.AddRange(this.ValidateMatching<T>(rowValidates, TootarIndex));
            }

            Verification validate = new Verification();

            if (errCells.Any())
            {
                validate = new Verification()
                {
                    IfPass = false,
                    ErrCells = errCells
                };
            }
            else
            {
                validate = new Verification()
                {
                    IfPass = true,
                    ErrCells = errCells
                };
            }

            return validate;
        }

验证数据有效性

这个模块相对复杂,看不懂的小伙伴可以多看几遍理解消化吸收下。
首先调用LinqToExcel的WorksheetNoHeader方法获得除了标题的集合数据
然后得到当前标题行和Excel列的映射关系
调用GetErrCellByParameter方法进行验证

GetErrCellByParameter说明
得到所有列名称集合,得到所有列名称索引
遍历行数据,调用RowValidate的静态方法RowValidate.Validate<T>
传递的参数是,行索引,列名称集合,列索引集合,行数据集合

  private List<ErrCell> GetErrCellByParameter<T>(List<RowNoHeader> rows, int startRowIndex)
        {
            List<string> colNames = _propertyCollection.Values.Select(u => u.ColName).ToList();
            List<int> colIndexs = _propertyCollection.Values.Select(u => u.ColIndex).ToList();

            List<ErrCell> errCells = new List<ErrCell>();
            for (int rowIndex = startRowIndex; rowIndex < rows.Count; rowIndex++)
            {
                List<string> rowValues = rows[rowIndex].Where((u, index) => colIndexs.Any(p => p == index)).Select(u => u.ToString()).ToList();
                errCells.AddRange(RowValidate.Validate<T>(rowIndex, colNames, colIndexs, rowValues));
            }
            return errCells;
        }
    private List<ErrCell> ValidateParameter<T>(int startRowIndex)
        {
            //第一步 得到集合
            var excel = new ExcelQueryFactory(FilePath);
            var rows = (from c in excel.WorksheetNoHeader(SheetIndex)
                        select c).ToList();
            //第二步 获得标题行和Excel列的映射关系
                 方法体省略

            //第二步 调用验证方法
            return GetErrCellByParameter<T>(rows, startRowIndex);
        }
目录
相关文章
|
26天前
|
JavaScript 前端开发 调度
async/await和Generators的底层实现原理有什么不同?
总体而言,async/await 和 Generators 虽然都用于处理异步操作,但它们的底层实现原理有着不同的侧重点和方式。理解这些差异有助于我们更好地运用它们,并在不同的场景中选择合适的方式来处理异步编程。
113 63
|
4月前
|
Java Spring
@Async 的实现原理是什么?
【8月更文挑战第17天】@Async 的实现原理是什么?
112 3
|
1月前
|
前端开发 JavaScript
JS-instanceof 的实现原理
`instanceof` 运算符在前端 JavaScript 中用于检测对象的原型链是否包含指定构造函数的 `prototype` 属性。它通过遍历对象的原型链来实现。每个对象都有一个内部链接 `[[Prototype]]` 指向其原型对象,当访问属性或方法时,JavaScript 引擎会沿着原型链查找。`instanceof` 的具体实现是通过比较对象的原型链中的原型与构造函数的 `prototype` 属性,直到找到匹配的原型或到达原型链的顶端。示例代码展示了如何使用 `instanceof` 检查对象的继承关系。此外,`instanceof` 可用于验证继承关系和类型检查,支持多态性。
|
2月前
|
Java
Optional源码分析(涉及Objects源码和Stream源码)
本文分析了Java中Optional类的源码,包括其内部的Objects.requireNonNull方法、EMPTY定义、构造方法、ofNullable方法、isEmpty方法以及如何与Stream类交互,展示了Optional类如何避免空指针异常并提供流式操作。
41 0
Optional源码分析(涉及Objects源码和Stream源码)
|
7月前
|
JavaScript
jQuery 的属性拷贝(extend)的实现原理是什么,如何实现深拷贝?
jQuery 的属性拷贝(extend)的实现原理是什么,如何实现深拷贝?
72 2
|
7月前
|
存储 JavaScript
【ES6系列第二篇】适用JS初学者的Set对象和Map对象学习笔记
【ES6系列第二篇】适用JS初学者的Set对象和Map对象学习笔记
52 0
|
7月前
|
存储 JavaScript 前端开发
面试题:问js的forEach和map的区别
面试题:问js的forEach和map的区别
|
存储 开发工具 C++
08-OC底层原理之Category实现原理
08-OC底层原理之Category实现原理
58 0
|
JavaScript
热点面试题:JS 中 call, apply, bind 概念、用法、区别及实现?
热点面试题:JS 中 call, apply, bind 概念、用法、区别及实现?
|
Python
Python语言中extend和append的区别
Python语言中extend和append的区别
181 0