使用 EPPlus 封裝的 excel 表格導入功能 Func--小試牛刀


使用 EPPlus 封裝的 excel 表格導入功能

使用 EPPlus 封裝的 excel 表格導入功能 (二)

前言

最近做系統的時候有很多 excel導入 的功能,以前我前后端都做的時候是在前端解析,然后再做個批量插入的接口

我覺着這樣挺好的,后端部分可以做的很簡單(很偷懶的)

但是因為各種各樣的原因,最終還是需要做個專門的 excel導入 接口

遇到的問題

由於之前從來沒有在后端部分處理過表格,所以我選擇看一下同事的代碼是怎么寫的

雖然我之前沒寫過相關的業務,但是直覺的認為這樣寫非常麻煩,那個 ExcelHelper 好像也沒干什么事,我希望一套操作下來可以把 excel 轉成能夠直接傳入 AddRange 進行批量新增的實體集合

所以我就決定自己封裝。

最終代碼

結果展示(略)

public ICollection<TestDto> ExcelImport(IFormFile file)
{
    var config = ExcelCellOption<TestDto>
    .GenExcelOption("姓名", item => item.Name)
    .Add("年齡", item => item.Age, item => int.Parse(item))
    .Add("性別", item => item.Gender, item => item == "男")
    .Add("身高", item => item.Height, item => double.Parse(item));

    ICollection<TestDto> result = ExcelOperation.ExcelToEntity(file.OpenReadStream(), config);

    return result;
}

最終可以直接生成"初始化"數據的 result

代碼/設計/想法

我希望使用的時候通過傳入 表格字段數據實體.屬性關系集合

實現解析表格的同時生成對應的 實體對象

然后我對上述 關系 的定義如下

public class ExcelCellOption<T>
{
    /// <summary>
    /// 對應excel中的header表頭(title)
    /// </summary>
    public string ExcelField { get; set; }
    /// <summary>
    /// 對應字段的屬性(實際上包含PropName)
    /// </summary>
    public PropertyInfo Prop { get; set; }
    /// <summary>
    /// 就是一個看起來比較方便的標識
    /// </summary>
    public string PropName { get; set; }
    /// <summary>
    /// 轉換 表格 數據的方法(委托)
    /// </summary>
    public Func<string, object> Action { get; set; }
}

之后給他加了個靜態方法 GenExcelOption<E> 生成關系集合 ICollection<ExcelCellOption<T>>

public static ICollection<ExcelCellOption<T>> GenExcelOption<E>(string field,
            Expression<Func<T, E>> prop, Func<string, object> action = null)
{
    var member = prop.GetMember();
    return new List<ExcelCellOption<T>>{
        new ExcelCellOption<T>
        {
            PropName = member.Name,
            Prop = (PropertyInfo)member,
            ExcelField = field,
            Action = action
        }
    };
}

為了方便之后加新的配置項

給返回類型 ICollection<ExcelCellOption<T>> 搞個擴展方法 Add

public static class ExcelOptionExt
{
    public static ICollection<ExcelCellOption<T>> Add<T, E>(this ICollection<ExcelCellOption<T>> origin,
    string field, Expression<Func<T, E>> prop, Func<string, object> action = null)
    {
        var member = prop.GetMember();
        origin.Add(new ExcelCellOption<T>
        {
            PropName = member.Name,
            Prop = (PropertyInfo)member,
            ExcelField = field,
            Action = action
        });
        return origin;
    }
}

使用的時候就可以根據excel表格生成對應的 關系集合 (配置)

var config = ExcelCellOption<TestDto>
.GenExcelOption("姓名", item => item.Name)
.Add("年齡", item => item.Age, item => int.Parse(item))
.Add("性別", item => item.Gender, item => item == "男")
.Add("身高", item => item.Height, item => double.Parse(item));

有了配置之后需要根據配置解析excel生成數據實體了

寫了個方法如下

public class ExcelOperation
{
    /// <summary>
    /// 將表格數據轉換為指定的數據實體
    /// </summary>
    public static ICollection<T> ExcelToEntity<T>(Stream excelStream, ICollection<ExcelCellOption<T>> options)
    {
        using ExcelPackage pack = new(excelStream);
        var sheet = pack.Workbook.Worksheets[1];
        int rowCount = sheet.Dimension.Rows, colCount = sheet.Dimension.Columns;
        // 獲取對應設置的 表頭 以及其 column下標
        var header = sheet.Cells[1, 1, 1, colCount ]
        .Where(item => options.Any(opt => opt.ExcelField == item.Value?.ToString().Trim()))
        .ToDictionary(item => item.Value?.ToString().Trim(), item => item.End.Column);
        List<T> data = new();
        // 將excel 的數據轉換為 對應實體
        for (int r = 2; r <= rowCount; r++)
        {
            // 將單行數據轉換為 表頭:數據 的鍵值對
            var rowData = sheet.Cells[r, 1, r, colCount]
            .Where(item => header.Any(title => title.Value == item.End.Column))
            .Select(item => new KeyValuePair<string, string>(header.First(title => title.Value == item.End.Column).Key, item.Value?.ToString().Trim()))
            .ToDictionary(item => item.Key, item => item.Value);
            var obj = Activator.CreateInstance(typeof(T));
            // 根據對應傳入的設置 為obj賦值
            foreach (var option in options)
            {
                if (!string.IsNullOrEmpty(option.ExcelField))
                {
                    var value = rowData.ContainsKey(option.ExcelField) ? rowData[option.ExcelField] : string.Empty;
                    if (!string.IsNullOrEmpty(value))
                        option.Prop.SetValue(obj, option.Action == null ? value : option.Action(value));
                }
                // 可以用來初始化與表格無關的字段 如 創建時間 Guid主鍵 之類的東西
                else
                    option.Prop.SetValue(obj, option.Action == null ? null : option.Action(string.Empty));
            }
            data.Add((T)obj);
        }
        return data;
    }
}

最終調用

ExcelOperation.ExcelToEntity(file.OpenReadStream(), config)

傳入文件流和配置集合即可

完整代碼

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using AutoMapper.Internal;
using OfficeOpenXml;

namespace XXX.XXX.XXX.XXX
{
    public class ExcelOperation
    {
        /// <summary>
        /// 將表格數據轉換為指定的數據實體
        /// </summary>
        public static ICollection<T> ExcelToEntity<T>(Stream excelStream, ICollection<ExcelCellOption<T>> options)
        {
            using ExcelPackage pack = new(excelStream);
            var sheet = pack.Workbook.Worksheets[1];
            int rowCount = sheet.Dimension.Rows, colCount = sheet.Dimension.Columns;
            // 獲取對應設置的 表頭 以及其 column
            var header = sheet.Cells[1, 1, 1, sheet.Dimension.Columns]
            .Where(item => options.Any(opt => opt.ExcelField == item.Value.ToString()))
            .ToDictionary(item => item.Value.ToString(), item => item.End.Column);
            List<T> data = new();
            // 將excel 的數據轉換為 對應實體F
            for (int r = 2; r <= rowCount; r++)
            {
                // 將單行數據轉換為 表頭:數據 的鍵值對
                var rowData = sheet.Cells[r, 1, r, colCount]
                .Where(item => header.Any(title => title.Value == item.End.Column))
                .Select(item => new KeyValuePair<string, string>(header.First(title => title.Value == item.End.Column).Key, item.Value?.ToString()))
                .ToDictionary(item => item.Key, item => item.Value);
                var obj = Activator.CreateInstance(typeof(T));
                // 根據對應傳入的設置 為obj賦值
                foreach (var option in options)
                {
                    if (!string.IsNullOrEmpty(option.ExcelField))
                    {
                        var value = rowData.ContainsKey(option.ExcelField) ? rowData[option.ExcelField] : string.Empty;
                        if (!string.IsNullOrEmpty(value))
                            option.Prop.SetValue(obj, option.Action == null ? value : option.Action(value));
                    }
                    // 可以用來初始化與表格無關的字段 如 創建時間 Guid主鍵 之類的東西
                    else
                        option.Prop.SetValue(obj, option.Action == null ? null : option.Action(string.Empty));
                }
                data.Add((T)obj);
            }
            return data;
        }
    }

    public class ExcelCellOption<T>
    {
        /// <summary>
        /// 對應excel中的header字段
        /// </summary>
        public string ExcelField { get; set; }
        /// <summary>
        /// 對應字段的屬性(實際上包含PropName)
        /// </summary>
        public PropertyInfo Prop { get; set; }
        /// <summary>
        /// 就是一個看起來比較方便的標識
        /// </summary>
        public string PropName { get; set; }
        /// <summary>
        /// 轉換 表格 數據的方法
        /// </summary>
        public Func<string, object> Action { get; set; }
        public static ICollection<ExcelCellOption<T>> GenExcelOption<E>(string field, Expression<Func<T, E>> prop, Func<string, object> action = null)
        {
            var member = prop.GetMember();
            return new List<ExcelCellOption<T>>{
                new ExcelCellOption<T>
                {
                    PropName = member.Name,
                    Prop = (PropertyInfo)member,
                    ExcelField = field,
                    Action = action
                }
            };
        }
    }

    public static class ExcelOptionAdd
    {
        public static ICollection<ExcelCellOption<T>> Add<T, E>(this ICollection<ExcelCellOption<T>> origin, string field, Expression<Func<T, E>> prop, Func<string, object> action = null)
        {
            var member = prop.GetMember();
            origin.Add(new ExcelCellOption<T>
            {
                PropName = member.Name,
                Prop = (PropertyInfo)member,
                ExcelField = field,
                Action = action
            });
            return origin;
        }
    }
}

其實這已經是舊版本了

新的版本過幾天大概會發

未完待續


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM