.NET6使用MiniExcel根据数据源横向导出头部标题及数据

.NET6+MiniExcel根据数据源横向导出头部标题

MiniExcel简单、高效避免OOM的.NET处理Excel查、写、填充数据工具。
特点:

低内存耗用,避免OOM、频繁 Full GC 情况

支持即时操作每行数据

兼具搭配 LINQ 延迟查询特性,能办到低消耗、快速分页等复杂查询

轻量,不需要安装 Microsoft Office、COM+,DLL小于150KB

简便操作的 API 风格

github地址: MiniExcel
gitee地址: MiniExcel

本案例实现的功能是使用Miniexcel横向导出指标编码、指标名称,医院类型及指标对应的数据值,
要求导出效果如下所示:

  1. 第一列展示医院
  2. 头部两行动态展示指标编码、指标名称,下面展示每家医院所对应指标的值
  3. 安装NuGet程序包SqlSugarCore、MiniExcel、Furion

代码如下:

结合实际情况,可以适当改下

csharp 复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MiniExcelLibs;
using MiniExcelLibs.Attributes;
using MiniExcelLibs.OpenXml;
using SqlSugar;

namespace DJPSMS.Application.Service
{
    [AllowAnonymous]
    public class MiniexcelTest : IDynamicApiController
    {
        private readonly ISqlSugarClient _db;
        private readonly SqlSugarRepository<TDU_HOSPITALTEST> _tduHospitalRepository;

        /// <summary>
        /// 构造函数注入SqlSugar
        /// </summary>
        /// <param name="db"></param>
        public MiniexcelTest(ISqlSugarClient db, SqlSugarRepository<TDU_HOSPITALTEST> tduHospitalRepository)
        {
            _db = db;
            _tduHospitalRepository = tduHospitalRepository;
        }


        /// <summary>
        /// Miniexcel导出
        /// </summary>
        /// <param name="Chapter">章节(案例中未使用)</param>
        /// <returns></returns>
        [HttpGet("DownLoadExcel")]
        public void DownLoadExcel([FromQuery][Required] string Chapter = "1")
        {
            try
            {
                // 导出数据源总集合
                var quotaAssemble = new List<Dictionary<string, object>>();
                // 构建横向指标标题
                var quotaPairy = new Dictionary<string, object>();

                #region 构造excel模板及数据源
                Log.Information($"构造excel横向指标标题开始--------");
                // 使用SqlSugar查询数据库
                // var quotaArray = _db.Queryable<DW_QUOTATEST>()
                //    .Where(x => x.Q_CODE.StartsWith(Chapter))
                //    .GroupBy(x => new { x.Q_CODE, x.Q_NAME })
                //    .OrderBy(x => x.Q_CODE)
                //    .Select(g => new { g.Q_CODE, g.Q_NAME })
                //    .ToList();
                // 所有的指标类型
                var quotaArray = new List<DW_QUOTATEST>()
                {
                   new DW_QUOTATEST{ Q_ID = "1", Q_CODE = "1.01.01",Q_NAME ="指标1"},
                   new DW_QUOTATEST{ Q_ID = "2", Q_CODE = "1.01.02",Q_NAME ="指标2"},
                   new DW_QUOTATEST{ Q_ID = "3", Q_CODE = "1.01.03",Q_NAME ="指标3"},
                   new DW_QUOTATEST{ Q_ID = "4", Q_CODE = "1.01.04",Q_NAME ="指标4"},
                   new DW_QUOTATEST{ Q_ID = "5", Q_CODE = "1.01.05",Q_NAME ="指标5"},
                   new DW_QUOTATEST{ Q_ID = "6", Q_CODE = "1.01.06",Q_NAME ="指标6"},
                   new DW_QUOTATEST{ Q_ID = "7", Q_CODE = "1.01.07",Q_NAME ="指标7"},
                   new DW_QUOTATEST{ Q_ID = "8", Q_CODE = "1.01.08",Q_NAME ="指标8"},
                   new DW_QUOTATEST{ Q_ID = "9", Q_CODE = "1.01.09",Q_NAME ="指标9"},
                   new DW_QUOTATEST{ Q_ID = "10", Q_CODE = "1.01.10",Q_NAME ="指标10"},
                   new DW_QUOTATEST{ Q_ID = "11", Q_CODE = "1.01.11",Q_NAME ="指标11"},
                   new DW_QUOTATEST{ Q_ID = "12", Q_CODE = "1.01.12",Q_NAME ="指标12"},
                   new DW_QUOTATEST{ Q_ID = "13", Q_CODE = "1.01.13",Q_NAME ="指标13"}
                };

                // 設置列宽
                var config = new OpenXmlConfiguration
                {
                    DynamicColumns = CreateDynamicColumns(quotaArray.GroupBy(x => x.Q_CODE).Select(x => x.Key).ToList())
                };

                // 构建横向指标标题
                quotaPairy["医院名称"] = "";
                for (int i = 0; i < quotaArray.Count; i++)
                {
                    if (quotaPairy.ContainsKey(quotaArray[i].Q_CODE)) continue;
                    var propertyCode = quotaArray[i].Q_CODE;
                    var propertyName = quotaArray[i].Q_NAME;
                    quotaPairy[propertyCode] = propertyName;
                }
                quotaAssemble.Add(quotaPairy);
                Log.Information($"构造excel横向指标标题结束--------");


                // 构建第一列医院类型
                var hospitalList = new List<TDU_HOSPITALTEST>()
                {
                       new TDU_HOSPITALTEST{ FJGDM ="1",FDESC ="测试医院1" },
                       new TDU_HOSPITALTEST{ FJGDM ="2",FDESC ="测试医院2" },
                       new TDU_HOSPITALTEST{ FJGDM ="3",FDESC ="南京第一" },
                       new TDU_HOSPITALTEST{ FJGDM ="3",FDESC ="测试医院4" },
                       new TDU_HOSPITALTEST{ FJGDM ="4",FDESC ="测试医院5" },
                       new TDU_HOSPITALTEST{ FJGDM ="5",FDESC ="测试医院6" },
                       new TDU_HOSPITALTEST{ FJGDM ="6",FDESC ="测试医院7" },
                       new TDU_HOSPITALTEST{ FJGDM ="7",FDESC ="测试医院8" },
                       new TDU_HOSPITALTEST{ FJGDM ="8",FDESC ="测试医院9" },
                       new TDU_HOSPITALTEST{ FJGDM ="9",FDESC ="测试医院10" },
                       new TDU_HOSPITALTEST{ FJGDM ="10",FDESC ="测试医院11" },
                       new TDU_HOSPITALTEST{ FJGDM ="11",FDESC ="测试医院12" },
                };


                //每家医院对应的指标的值
                var quotaValuePairy = new Dictionary<string, object>();

                // 总数据源,一般来说是从数据库联表中查询的数据,这边是声明的测试数据
                List<HospitalViewCodeDetailTest> resultList = new List<HospitalViewCodeDetailTest>()
                {
                    // 测试医院1的数据
                    new HospitalViewCodeDetailTest { Code = "1.01.01", CodeName = "指标1",HospitalCode ="1",HospitalName ="测试医院1" ,QValue = "11.8"},
                    new HospitalViewCodeDetailTest { Code = "1.01.02", CodeName = "指标2",HospitalCode ="1",HospitalName ="测试医院2" ,QValue = "12.8"},
                    // 测试医院2的数据
                    new HospitalViewCodeDetailTest { Code = "1.01.01", CodeName = "指标1",HospitalCode ="2",HospitalName ="测试医院1" ,QValue = "22.6"},
                    new HospitalViewCodeDetailTest { Code = "1.01.02", CodeName = "指标2",HospitalCode ="2",HospitalName ="测试医院1" ,QValue = "23.2"} ,
                    // 测试医院3的数据
                    new HospitalViewCodeDetailTest { Code = "1.01.01", CodeName = "指标1",HospitalCode ="3",HospitalName ="测试医院3" ,QValue = "65.8"} ,
                    new HospitalViewCodeDetailTest { Code = "1.01.02", CodeName = "指标2",HospitalCode ="3",HospitalName ="测试医院4" ,QValue = "25.1"}
                };

                // 填充对应的指标值
                for (int i = 0; i < hospitalList.Count; i++)
                {
                    quotaValuePairy = new Dictionary<string, object>(); // 在每次迭代中创建新的字典对象
                    var hospitalCodeDetails = resultList.Where(x => x.HospitalCode == hospitalList[i].FJGDM)
                        .Select(x => new
                        {
                            x.Code,
                            x.QValue
                        }).ToList();
                    quotaValuePairy["医院名称"] = hospitalList[i].FDESC;

                    for (int o = 0; o < quotaArray.Count; o++)
                    {
                        if (quotaValuePairy.ContainsKey(quotaArray[o].Q_CODE)) continue;
                        quotaValuePairy[quotaArray[o].Q_CODE] = hospitalCodeDetails.FirstOrDefault(x => x.Code == quotaArray[o].Q_CODE)?.QValue; //指标值
                    }
                    quotaAssemble.Add(quotaValuePairy);
                }
                #endregion



                #region 导出excel
                if (quotaAssemble.Count > 0)
                {
                    Log.Information("正在导出......");
                    // 读取json文件中的自定义保存路径
                    // App.GetConfig官网介绍地址:http://furion.baiqian.ltd/docs/global/app?_highlight=getconfig#12-%E8%8E%B7%E5%8F%96%E9%85%8D%E7%BD%AE%E5%AF%B9%E8%B1%A1
                    //可以改成自己地址
                    string savePath = $"{App.GetConfig<string>("GenerateExcelOfHospitalFillingJobConfig:SavePath")}\\DownLoadExcel\\";
                    if (!Directory.Exists(savePath))
                    {
                        Directory.CreateDirectory(savePath);
                    }
                    string filename = $"{DateTime.Now:yyyyMMddHHmmss}.xlsx";
                    var absoluteFilePath = Path.Combine(savePath, filename);

                    // 保存
                    MiniExcel.SaveAs(absoluteFilePath, quotaAssemble.ToArray(),
                         configuration: config);

                    Log.Information($"{filename}导出成功!");
                }
                #endregion
            }
            catch (Exception ex)
            {
                // 异常处理逻辑
                Log.Error($"发生异常: {ex.Message}");
            }
        }


        /// <summary>
        /// 设置行宽
        /// </summary>
        /// <returns></returns>
        private DynamicExcelColumn[] CreateDynamicColumns(List<string> dwQuota)
        {
            var dynamicColumns = new List<DynamicExcelColumn>
            {
                new DynamicExcelColumn("医院名称") { Index = 0, Width = 30 }
            };
            dynamicColumns.AddRange(dwQuota.Select((codeTitle, codeIndex) =>
            {
                if (string.IsNullOrEmpty(codeTitle))
                {
                    // 处理空值的情况,例如使用默认列名或跳过该列
                    return null; // 返回 null 或者其他处理方式
                }
                else
                {
                    return new DynamicExcelColumn(codeTitle) { Index = codeIndex + 1, Width = 25 };
                }
            }).Where(c => c != null).ToArray());
            return dynamicColumns.ToArray();
        }


    }



    /// <summary>
    /// 指标实体
    /// </summary>
    public class DW_QUOTATEST
    {

        /// <summary>
        /// 主键guid
        /// </summary>
        [SugarColumn(ColumnDescription = "主键id", Length = 32, IsPrimaryKey = true)]
        public string Q_ID { get; set; }
        /// <summary>
        /// 编码
        /// </summary>
        public string Q_CODE { get; set; }
        /// <summary>
        /// 指标名称
        /// </summary>
        public string Q_NAME { get; set; }
    }




    /// <summary>
    /// 医院实体
    /// </summary>
    public class TDU_HOSPITALTEST
    {
        public string FJGDM { get; set; }
        public string FSEQ { get; set; }
        public string FDESC { get; set; }
    }



    public class HospitalViewCodeDetailTest
    {
        /// <summary>
        /// 医院编码
        /// </summary>
        public string HospitalCode { get; set; }
        /// <summary>
        /// 医院名称
        /// </summary>
        public string HospitalName { get; set; }
        /// <summary>
        /// 指标编码
        /// </summary>
        public string Code { get; set; }
        /// <summary>
        /// 指标名称
        /// </summary>
        public string CodeName { get; set; }
        /// <summary>
        /// QValue指标值
        /// </summary>
        public string QValue { get; set; }
    }
}

最后效果图如下所示:

写的不好,如有错误还请指正

相关推荐
surfirst3 小时前
举例说明 .Net Core 单元测试中 xUnit 的 [Theory] 属性的用法
单元测试·.netcore·xunit
百锦再3 小时前
基于依赖注入技术的.net core WebApi框架创建实例
.netcore
时光追逐者1 天前
WaterCloud:一套基于.NET 8.0 + LayUI的快速开发框架,完全开源免费!
前端·microsoft·开源·c#·.net·layui·.netcore
@Unity打怪升级2 天前
【C#】CacheManager:高效的 .NET 缓存管理库
开发语言·后端·机器学习·缓存·c#·.net·.netcore
csdn_aspnet2 天前
.NET Core 高性能并发编程
.netcore
三天不学习2 天前
.NET Core 集成 MiniProfiler性能分析工具
.netcore
AitTech2 天前
构建.NET Core Web API为Windows服务安装包
windows·.netcore
yufei-coder3 天前
掌握 C# 文件和输入输出操作
windows·c#·.netcore·visual studio
友恒3 天前
C#单元测试(一):用 NUnit 和 .NET Core 进行单元测试
单元测试·c#·.netcore
湛江小浪石(峰)4 天前
.NetCore 8 SwaggerGen 显示接口注析
.netcore