记一次dotnet拆分包,并希望得大佬指点

之前做了一个用于excel导入导出的包, 定义了一些接口, 然后基于 NPOI EPPlus MiniExcel 做了三种实现

接口大概长下面这样(现在可以在接口里面写静态函数了!)

public interface IExcelReader
{
// 根据一些条件返回下面的实现
public static IExcelReader GetExcelReader(string filePath, <params>)
{
}
}

然后有对应三种实现

public class NPOIReader: IExcelReader
{} public class EPPlusReader: IExcelReader
{} public class MiniExcel: IExcelReader
{}

在使用时

using var reader = IExcelReader.GetExcelReader("ExcelReader.xlsx", <一堆杂七杂八的条件>)

根据需要获取实例, 而不必去管什么 NPOI EPPlus MiniExcel

用起来可以极大的降低心智负担, 也可以使用我认为比较 "人性化" 的操作

这一堆东西都是写在一起的, 然后碰到了一些我比较在意的问题

  1. 如果我只是更新了NPOI包的实现, 然后push了一个新的版本, 这就相当于其他的实现也被"升级"了, 尽管另外的实现没有任何变化, 我认为这样是不好的
  2. 如果我只想使用 MiniExcel 的内容, 但由于三个实现写在了一起, NPOI 和 EPPlus 会被一起引入, 我认为这样是不好的
  3. 如果我修改了接口 IExcelReader, 那我必定需要同时修改对应的三个实现, 但是由于三个实现写在一起, 我必须将三个实现都改完测完, 然后才能push发包, 我认为这样是不好的

因为这样那样的问题, 我开始考虑拆包了

初步构想

一开始的想法是

先把统一的接口和操作什么的东西抽出来, 做成一个Core包

然后 NPOI EPPlus MiniExcel 相关的实现做成三个包, 都引用这个 Core

如果代码中只用 IExcelReader 这样的接口进行操作, 可以在不改变代码的前提下, 通过更换包引用(比如NPOI的包改为MiniExcel的包)轻松改变实现, 达成不同的效果

但由于Core包是被引用的, 所以理论上来说 IExcelReader 并不能像之前那样直接创建这三种实例

碰到这种"我知道, 但是身不由己"的情况, 我想到了用委托来做

// (大概是这么个感觉, 实际上我现在用的是字典)
public static List<Func<string, IExcelReader>> Selector;

在Core中搞一个静态委托集合, 然后在那三个包中将创建对象的委托加到这个集合里, 之后在使用 IExcelReader.GetExcelReader("**.xlsx") 时, 就可以通过这个委托集合获取到对应的实现了

以上是我的大致思路

第一种尝试-静态构造函数

我最先想到的就是静态构造函数

毕竟微软的文档上说了

静态构造函数用于初始化任何静态数据,或执行仅需执行一次的特定操作。 将在创建第一个实例或引用任何静态成员之前自动调用静态构造函数。

看描述还挺符合我的想法, 然后就有了如下代码

public class NPOIExcelReader : IExcelReader
{
static NPOIExcelReader()
{
Selector.Add((path) =>
{
// 假装下面做了一堆事情
// ......
return new NPOIExcelReader(path);
});
}
}

看着好像还行, 试了一下结果GG

如果我只是使用 IExcelReader.GetExcelReader("**.xlsx"), 则无法触发这个构造函数, 除非我在这之前调用一次 NPOIExcelReader, 但这与我的设想差挺多的, 所以暂时放弃了这个方案, 另寻他法

第二种尝试-ModuleInitializer

我觉得可能是因为 class 太 "低" 了, 所以才无法触发静态构造函数

然后我又想到了 ModuleInitializer, 感觉这个总比 class "高"一些, 不知道能不能实现我的想法

internal class Init
{
[ModuleInitializer]
public static void InitSelector()
{
Selector.Add((path) =>
{
// 假装下面做了一堆事情
// ......
return new NPOIExcelReader(path);
});
}
}

在NPOI包里写完上面的初始化之后我又尝试了一次, 结果还是GG......

碰到了类似的问题, 如果不调用NPOI包内的东西, 则无法初始化

第三种尝试-AppDomain.CurrentDomain.Load

后来查看了AppDomain.CurrentDomain.GetAssemblies(), 发现程序运行时并没有加载 NPOI包 的程序集, 我觉得可能是因为这个原因才导致扑街的

所以尝试在Core中用反射获取程序集(因为在代码中使用了IExcelReader.GetExcelReader, 所以可以触发Core包的ModuleInitializer初始化), 然后使用 AppDomain.CurrentDomain.load 来加载

public static class Init
{
[ModuleInitializer]
public static void InitCellReader()
{
var files = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "我那几个包的实现.dll");
if (files.IsEmpty())
return;
var newAsses = files.Select(item => Assembly.LoadFrom(item)).ToList();
newAsses.ForEach(item => AppDomain.CurrentDomain.Load(item.FullName));
}
}

运行之后打个断点, 确实执行了, 也确实加载到 AppDomain.CurrentDomain 中了, 但是...还是没用, 全都木大木大了

绝望的尝试-反射+Activator

既然发现问题出在 "不调用就不初始化" 上, 那我就调用一下...

基于上面的第三种尝试, 尝试创建NPOI包中的实现, 能不能创建无所谓, 重要的是摆出一副 "我要调你" 的感觉, 然后初始化自己动起来

还是写在Core包中


public static class Init
{
[ModuleInitializer]
public static void InitCellReader()
{
var files = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "我那几个包的实现.dll");
if (files.IsEmpty())
return;
var newAsses = files.Select(item => Assembly.LoadFrom(item)).ToList();
newAsses.ForEach(item => AppDomain.CurrentDomain.Load(item.FullName));
var types = newAsses.SelectMany(s => s.GetTypes().Where(item => item.HasInterface(typeof(IExcelReader))));
types.ForEach(item =>
{
try
{
Activator.CreateInstance(item);
}
catch { }
});
}
}

然后配合其他包的 Init, 最后终于算是实现了我想要的效果

但是实现的方式太丑陋了...不知道有没有更好, 更优雅的方式

记一次dotnet拆分包,并希望得大佬指点的更多相关文章

  1. 配置中心的设计-nacos vs apollo

    简介 前面我们分析了携程的 apollo(见 详解apollo的设计与使用),现在再来看看阿里的 nacos. 和 apollo 一样,nacos 也是一款配置中心,同样可以实现配置的集中管理.分环境 ...

  2. Json操作问题总结

    大家都知道,Json是一种轻量级的数据交换格式,对JS处理数据来说是很理想滴! 熟练写过xxx.json文件和操作的小伙伴来说,我说的问题都不是什么大问题啦,可以忽略本宝宝的文章,更希望各位大佬指点一 ...

  3. .Net Core建站(3):搭建三层架构

    啊,终于到写三层架构的时候了,老实说,我都不知道自己这个算不算三层架构,姑且就当它是吧,具体属于哪一个体系,希望有大佬指点一下(^o^)/ 不晓得有人注意到没有,我写了三篇博客,然后就改了三次标题ヽ( ...

  4. 分享我在 vue 项目中关于 api 请求的一些实现及项目框架

    本文主要简单分享以下四点 如何使用 axios 如何隔离配置 如何模拟数据 分享自己的项目框架 本文主要目的为以下三点 希望能够帮到一些人 希望能够得到一些建议 奉上一个使用Vue的模板框架 我只是把 ...

  5. jsp里面不能使用${pageContext.request.contextPath}解决方案

    1.在jsp中使用${pageContext.request.contextPath}获取相对路径,可是最后路径变为:http://localhost:8080/oneself/$%7BpageCon ...

  6. 利用顺序栈解决括号匹配问题(c++)-- 数据结构

    题目: 7-1 括号匹配 (30 分)   给定一串字符,不超过100个字符,可能包括括号.数字.字母.标点符号.空格,编程检查这一串字符中的( ) ,[ ],{ }是否匹配. 输入格式: 输入在一行 ...

  7. 调试.NET CORE代码

    前言 core也用了很长一段时间了,发现很多小伙伴不知道如何调试core的代码. 可想而知,以前使用mvc的时候,不需要发布代码,直接iis地址指向项目源码,然后附加到进程w3wp.exe就可以调试了 ...

  8. NETCore 调试

    https://www.cnblogs.com/MingQiu/p/8227644.html https://www.cnblogs.com/shumin/p/9967854.html 前言 core ...

  9. 【技术累积】【点】【sql】【15】MySQL的TEXT和SELECT问题

    说明 只是TEXT和SELECT两个东西相关的问题,并不是两者之间的关系. TEXT TEXT类型,大文本类型,细分起来还有BIGTEXT,TINYTEXT等: 总体而言,就是处理mysql中存储大文 ...

随机推荐

  1. Navicat v15 破解

    特别注意: 1.断网,否则在安装过程中会失败 2.关闭防火墙及杀毒软件 3.选择对应版本:mysql版就选择mysql 4.如果出现 就卸载,删除注册表,重新安装,出现rsa public key n ...

  2. php 23种设计模式 - 设计模式简介以及分类

    一. Introduction[介绍] 设计模式:提供了一种广泛的可重用的方式来解决我们日常编程中常常遇见的问题.设计模式并不一定就是一个类库或者第三方框架,它们更多的表现为一种思想并且广泛地应用在系 ...

  3. Excel用Power Query把文件夹下所有文件合并为一个与合并多个表格

    Excel用Power Query把文件夹下所有文件合并为一个与合并多个表格 在 Excel Power Query 未出来之前,把文件夹下所有文件合并为一个与合并同一工作表中的多个工作表,需要用 V ...

  4. ASP.NET Core 6框架揭秘实例演示[26]:跟踪应用接收的每一次请求

    很多人可能对ASP.NET Core框架自身记录的诊断日志并不关心,其实这些日志对纠错排错和性能监控提供了很有用的信息.如果需要创建一个APM(Application Performance Mana ...

  5. RENIX 软件RAW流发送——网络测试仪实操

    本文主要介绍了RENIX软件如何进行RAW流发送操作.文章通过预约端口.添加RAW流.修改负载.发送流量.查看流统计.数据包捕获六个步骤详细介绍了操作过程. 步骤一:预约端口.1.先安装RENIX软件 ...

  6. 字节跳动社会招聘&内推-帮助你更快加入字节跳动

    字节跳动社会招聘&内推「[内推码]:4J8CA3W」 内推时间:一直有效 招聘对象:根据招聘要求而定 社招投递链接: https://job.toutiao.com/s/de5teaA 应届生 ...

  7. 16经典的SPI Flash的扇区擦除flash_se功能

    一设计功能 对SPI_flash进行扇区擦除,分为写指令和扇区擦除两个时序部分. 二设计知识点 我简单理解flash,第一它是掉电不丢失数据的存储器,第二它每次写入新数据前首先得擦除数据,分为扇区擦除 ...

  8. golang实现WebSocket的商业化使用的开发逻辑(1)

    WebSocket是什么 WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议.其最大特点之一就是:服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对 ...

  9. Python集成开发工具(IDE)推荐

    1.7 Python集成开发工具(IDE)推荐 1.7.1 Notepad++ Notepad++是Windows操作系统下的一套文本编辑器(软件版权许可证: GPL),有完整的中文化接口及支持多国语 ...

  10. Oracle问题解决记录

    一.前言 oracle这么一个庞大的东西,出点问题真是太常见了.开个博客,用于记录遇到的问题吧. 持续更新. 二.问题列表 归档日志满,引起的问题. 一台服务器,用了很久了,某天,出现了磁盘空间占满的 ...