Coder Social home page Coder Social logo

ldqk / masuit.tools Goto Github PK

View Code? Open in Web Editor NEW
5.4K 144.0 1.2K 10.07 MB

全龄段友好的C#万能工具库,码数吐司库,包含一些常用的操作类,大都是静态类,加密解密,反射操作,权重随机筛选算法,分布式短id,表达式树,linq扩展,文件压缩,多线程下载,硬件信息,字符串扩展方法,日期时间扩展操作,**农历,大文件拷贝,图像裁剪,验证码,断点续传,集合扩展、Excel导出等常用封装。诸多功能集一身,代码量不到2MB!

Home Page: https://www.masuit.tools/

License: MIT License

C# 100.00%
lambda reflection datetime expression linq multithreading encryption excel-export excel net5 net6

masuit.tools's Introduction

Masuit.Tools(码数吐司库)

许可证 nuget nuget codeSize 编程语言

全龄段友好的C#.NET万能工具库,不管你是菜鸟新手还是骨灰级玩家都能轻松上手,这个库包含一些常用的操作类,大都是静态类,加密解密,反射操作,树结构,文件探测,权重随机筛选算法,分布式短id,表达式树,linq扩展,文件压缩,多线程下载,硬件信息,字符串扩展方法,日期时间扩展操作,**农历,大文件拷贝,图像裁剪,验证码,断点续传,集合扩展、Excel导出等常用封装。

诸多功能集一身,代码量不到2MB! 官网教程 Masuit Tools

项目开发模式:日常代码积累+网络搜集

⭐⭐⭐喜欢这个项目的话就Star、Fork、Follow素质三连关♂注一下吧⭐⭐⭐

关于本项目,如果你有任何不懂的地方或使用过程中遇到任何问题,可以直接提issue或私信联系我,我会为你提供完全免费的技术指导,当然,如果你觉得不好意思接受免费的指导,想适当打赏我也是不会拒绝的!🤣🤣🤣

本项目已得到JetBrains的支持!

Star趋势

请注意:

一旦使用本开源项目以及引用了本项目或包含本项目代码的公司因为违反劳动法(包括但不限定非法裁员、超时用工、雇佣童工等)在任何法律诉讼中败诉的,一经发现,本项目作者有权利追讨本项目的使用费(公司工商注册信息认缴金额的2-5倍作为本项目的授权费),或者直接不允许使用任何包含本项目的源代码! 人力外包公司007公司需要使用本类库,请联系作者进行商业授权!其他企业或个人可随意使用不受限。007那叫用人,也是废人。8小时工作制才可以让你有时间自我提升,将来有竞争力。反对007,人人有责!

建议开发环境

操作系统:Windows 11 23H2及以上版本

开发工具:VisualStudio2022 v17.8及以上版本

SDK:.Net Core 2.1.0及以上所有版本

安装程序包

基础功能包

.NET Framework ≥ 4.6.2

PM> Install-Package Masuit.Tools.Net

.NET Standard ≥ 2.1 或只想使用一些基本功能

通用项目推荐首选包

PM> Install-Package Masuit.Tools.Abstraction

.NET Core ≥ 2.1

.NET Core项目推荐首选包

PM> Install-Package Masuit.Tools.Core

.NET Framework 4.5特供版

请注意:这是.NET Framework 4.5的专用版本,相比4.6.2及.NET Core的版本,阉割了Redis、HTML、文件压缩、ASP.NET扩展、硬件监测、Session扩展等一些功能。如果你的项目版本高于4.6.2,请务必使用上述版本的包,以享受完整的功能体验!

PM> Install-Package Masuit.Tools.Net45

增值包

Masuit.Tools.AspNetCore

AspNetCore项目推荐首选包 ASP.NET Core Web专用包,包含Masuit.Tools.Core的全部功能,并且增加了一些对ASP.NET Core Web功能的额外支持。

Masuit.Tools.Excel

Excel导入导出的专用独立包

Masuit.Tools.NoSQL.MongoDBClient

mongodb的封装操作类独立包

为工具库注册配置(可选的,按需配置)

工具库需要用到外部配置节,.NET Framework项目配置在web.config/app.config的AppSettings配置节中,.NET Core项目配置在appsettings.json中:

  1. EmailDomainWhiteList,邮箱校验需要用到的白名单域名,英文逗号分隔,每个元素支持正则表达式,若未配置,则不启用邮箱校验白名单,示例: "^\\w{1,5}@qq.com,^\\w{1,5}@163.com,^\\w{1,5}@gmail.com,^\\w{1,5}@outlook.com"
  2. EmailDomainBlockList,邮箱校验需要用到的黑名单域名,英文逗号分隔,每个元素支持正则表达式,且黑名单优先级高于白名单,若未配置,则不启用邮箱校验黑白名单
public Startup(IConfiguration configuration)
{
    configuration.AddToMasuitTools(); // 若未调用,则默认自动尝试加载appsettings.json
}

特色功能示例代码

在线体验

https://replit.com/@ldqk/MasuitToolsDemo?v=1#main.cs

0. 一些创意类型

DisposableDictionary:可被Disposable的字典类型,用于存放Value是Disposable类型的数据,用法和普通字典一致

NullableConcurrentDictionary/NullableDictionary:Key可为null的字典类型,用法和普通字典一致

ConcurrentHashSet:并发HashSet,用法和HashSet一致

ConcurrentLimitedQueue:定长并发队列,特点是长度是固定的,用法与ConcurrentQueue一致

LimitedQueue:定长队列,特点是长度是固定的,用法与Queue一致

LargeMemoryStream:超大内存流,最大可支持1TB数据,推荐当数据流大于2GB时使用,用法与MemoryStream一致

PooledMemoryStream:池化内存流,可内存复用,用法与MemoryStream一致,性能比MemoryStream好

ITree<T>:树形实体接口约束,实现该接口可让类型实现一些树形操作

ChineseCalendar:**农历类型,可以实现天干地支节气等数据的获取

Clay/DynamicFactory:粘土动态类型,可实现类似js的弱类型编程

RadarChart:雷达图类型,可用于做数据分析或用户行为画像

Circle:圆形类型,可实现⚪的相交相切相离的判断

Sphere:球体类型,可实现计算球体上两点的弧长计算,相交相切相离的判断

MimeMapper:mime类型映射

VersionNumber:版本号类型,比System.Version功能更多一点,用法一致

具体用法,可参阅后文详细示例:

1. 检验字符串是否是Email、手机号、URL、IP地址、身份证号等

var (isMatch, match) = "[email protected]".MatchEmail(); // 可在appsetting.json中添加EmailDomainWhiteList和EmailDomainBlockList配置邮箱域名黑白名单,逗号分隔,如"EmailDomainBlockList": "^\\w{1,5}@qq.com,^\\w{1,5}@163.com,^\\w{1,5}@gmail.com,^\\w{1,5}@outlook.com",
bool isInetAddress = "114.114.114.114".MatchInetAddress(); // 匹配IP地址
bool isUrl = "http://masuit.org/20/history".MatchUrl(); // 匹配url
bool isPhoneNumber = "15205201520".MatchPhoneNumber(); // 匹配手机号
bool isLandline = "01088888888".MatchLandline(); // 匹配座机号
bool isIdentifyCard = "312000199502230660".MatchIdentifyCard();// 校验**大陆身份证号
bool isCNPatentNumber = "200410018477.9".MatchCNPatentNumber(); // 校验**专利申请号或专利号,是否带校验位,校验位前是否带“.”,都可以校验,待校验的号码前不要带CN、ZL字样的前缀
bool isUSCC = "200410018477.9".MatchUSCC(); // 校验企业统一社会信用代码

2.硬件监测(需要管理员权限,仅支持Windows,部分函数仅支持物理机模式)

float load = SystemInfo.CpuLoad;// 获取CPU占用率
long physicalMemory = SystemInfo.PhysicalMemory;// 获取物理内存总数
long memoryAvailable = SystemInfo.MemoryAvailable;// 获取物理内存可用率
double freePhysicalMemory = SystemInfo.GetFreePhysicalMemory();// 获取可用物理内存
double temperature = SystemInfo.GetCPUTemperature();// 获取CPU温度
int cpuCount = SystemInfo.GetCpuCount();// 获取CPU核心数
var ipAddress = SystemInfo.GetLocalIPs();// 获取本机所有IP地址
string localUsedIp = SystemInfo.GetLocalUsedIP();// 获取本机当前正在使用的IP地址
IList<string> macAddress = SystemInfo.GetMacAddress();// 获取本机所有网卡mac地址
string osVersion = Windows.GetOsVersion();// 获取操作系统版本
RamInfo ramInfo = SystemInfo.GetRamInfo();// 获取内存信息
var cpuSN=SystemInfo.GetCpuInfo()[0].SerialNumber; // CPU序列号
var driveSN=SystemInfo.GetDiskInfo()[0].SerialNumber; // 硬盘序列号

// 快速方法
var cpuInfos = CpuInfo.Locals; // 快速获取CPU的信息
var ramInfo = RamInfo.Local; // 快速获取内存的信息
var diskInfos = DiskInfo.Locals; // 快速获取硬盘的信息
var biosInfo = BiosInfo.Local; // 快速获取主板的信息

3.html的防XSS处理:

string html = @"<link href='/Content/font-awesome/css' rel='stylesheet'/>
        <!--[if IE 7]>
        <link href='/Content/font-awesome-ie7.min.css' rel='stylesheet'/>
        <![endif]-->
        <script src='/Scripts/modernizr'></script>
        <div id='searchBox' role='search'>
        <form action='/packages' method='get'>
        <span class='user-actions'><a href='/users/account/LogOff'>退出</a></span>
        <input name='q' id='searchBoxInput'/>
        <input id='searchBoxSubmit' type='submit' value='Submit' />
        </form>
        </div>";
string s = html.HtmlSanitizerStandard();// 清理后:<div><span><a href="/users/account/LogOff">退出</a></span></div>
string s = html.HtmlSanitizerCustom(); // 自定义清理

4.整理Windows系统的内存:

类似于各大系统优化软件的加速球功能

Windows.ClearMemorySilent();

5.任意进制转换/中文数字

大写数字

var num=123.45.ToChineseMoney(); // 壹佰贰拾叁元肆角伍分
var num=123.45.ToChineseNumber(); // 一百二十三点四五

进制转换

可用于生成短id,短hash,随机字符串等操作,纯数学运算。

NumberFormater nf = new NumberFormater(36);//内置2-95进制的转换
//NumberFormater nf = new NumberFormater("0123456789abcdefghijklmnopqrstuvwxyz");// 自定义进制字符,可用于生成验证码,自定义字符可支持任意进制,你传1w个字符进去那就支持一万进制(手动狗头)
string s36 = nf.ToString(12345678);
long num = nf.FromString("7clzi");
Console.WriteLine("12345678的36进制是:" + s36); // 7clzi
Console.WriteLine("36进制的7clzi是:" + num); // 12345678
var s = new NumberFormater(91).ToString(new Random().Next(100000, int.MaxValue)); //配合随机数生成随机字符串
//扩展方法形式调用
var bin=12345678.ToBase(36);// 10进制转36进制:7clzi
var num="7clzi".FromBase(36);// 36进制转10进制:12345678
//超大数字的进制转换
var num = "e6186159d38cd50e0463a55e596336bd".FromBaseBig(16); // 大数字16进制转10进制
Console.WriteLine(num); // 十进制:305849028665645097422198928560410015421
Console.WriteLine(num.ToBase(64)); // 64进制:3C665pQUPl3whzFlVpoPqZ,22位长度
Console.WriteLine(num.ToBase(36)); // 36进制:dmed4dkd5bhcg4qdktklun0zh,25位长度
Console.WriteLine(num.ToBase(7)); // 7进制:2600240311641665565300424545154525131265221035,46位长度
Console.WriteLine(num.ToBase(12)); // 12进制:5217744842749978a756b22135b16a5998a5,36位长度
Console.WriteLine(num.ToBase(41)); // 41进制:opzeBda2aytcEeudEquuesbk,24位长度

如果你想让进制符支持emoji,NumberFormater是不支持的,不过如果你确实有这么*的需求,我还准备了UnicodeFormater类,用于支持emoji,用法和NumberFormater一模一样,并且,UnicodeFormater的功能包含NumberFormater的功能,但是,性能比NumberFormater差了许多。

var formater = new UnicodeFormater("😀😁😂🤣😃😄😅😆😉😊😋😎😍😘🥰😗😙🥲😚🙂🤗🤩🤔🤨😑😶😶‍🌫🙄😏😣😥😮");
var s = formater.ToString(1234567890); // 😄🌫😶😋😋
var num = formater.FromString(s); // 1234567890

6.纳秒级性能计时器

HiPerfTimer timer = HiPerfTimer.StartNew();
for (int i = 0; i < 100000; i++)
{
    //todo
}
timer.Stop();
Console.WriteLine("执行for循环100000次耗时"+timer.Duration+"s");
double time = HiPerfTimer.Execute(() =>
{
    for (int i = 0; i < 100000; i++)
    {
        //todo
    }
});
Console.WriteLine("执行for循环100000次耗时"+time+"s");

7.产生分布式唯一有序短id(雪花id)

// 实例调用
var sf = SnowFlake.GetInstance();
string id = sf.GetUniqueId();// rcofqodori0w
var sfn = SnowFlakeNew.GetInstance(); // 改良版雪花id,对时间回拨不敏感
string id = sfn.GetUniqueId();// vmbq8q3s3zul

// 静态调用
string id = SnowFlake.NewId;// rcofqodori0w
string shortId = sf.GetUniqueShortId(8);// qodw9728
string id = SnowFlakeNew.NewId;// 改良版雪花id,对时间回拨不敏感

// 全局设置
SnowFlake.SetMachienId(1); // 设置机器id
SnowFlake.SetInitialOffset(4219864516915105792); // 设置起始偏移量
SnowFlake.SetNumberFormater(new NumberFormater("0123456789abcdefghijklmnopqrstuvwxyz._-!")); // 设置数制格式化器

SnowFlakeNew.SetMachienId(1); // 设置机器id
SnowFlakeNew.SetInitialOffset(4219864516915105792); // 设置起始偏移量
SnowFlakeNew.SetNumberFormater(new NumberFormater("0123456789abcdefghijklmnopqrstuvwxyz._-!")); // 设置数制格式化器
var set = new HashSet<string>();
double time = HiPerfTimer.Execute(() =>
{
    for (int i = 0; i < 1000000; i++)
    {
        set.Add(SnowFlake.NewId);
    }
});
Console.WriteLine(set.Count == 1000000); //True
Console.WriteLine("产生100w个id耗时" + time + "s"); //2.6891495s

8.农历转换

ChineseCalendar.CustomHolidays.Add(DateTime.Parse("2018-12-31"),"元旦节");//自定义节假日
ChineseCalendar today = new ChineseCalendar(DateTime.Parse("2018-12-31"));
Console.WriteLine(today.ChineseDateString);// 二零一八年十一月廿五
Console.WriteLine(today.AnimalString);// 生肖:狗
Console.WriteLine(today.GanZhiDateString);// 干支:戊戌年甲子月丁酉日
Console.WriteLine(today.DateHoliday);// 获取按公历计算的节假日
...

9.Linq表达式树扩展

Expression<Func<string, bool>> where1 = s => s.StartsWith("a");
Expression<Func<string, bool>> where2 = s => s.Length > 10;
Func<string, bool> func = where1.And(where2)
    .AndIf(!string.IsNullOrEmpty(name),s=>s==name)
    .Compile(); // And和AndIf可供选择,满足条件再执行And
bool b=func("abcd12345678");//true
Expression<Func<string, bool>> where1 = s => s.StartsWith("a");
Expression<Func<string, bool>> where2 = s => s.Length > 10;
Func<string, bool> func = where1
    .Or(where2)
    .OrIf(!string.IsNullOrEmpty(name),s=>s==name)
    .Compile(); // Or和OrIf可供选择,满足条件再执行Or
bool b=func("abc");// true
queryable.WhereIf(!string.IsNullOrEmpty(name),e=>e.Name==name)
    .WhereIf(()=> age.HasValue,e=>e.Age>=age); // IQueryable的WhereIf扩展函数,满足条件再执行Where

10.模版引擎

var tmp = new Template("{{name}},你好!");
tmp.Set("name", "万金油");
string s = tmp.Render();//万金油,你好!
var tmp = new Template("{{one}},{{two}},{{three}}");
string s = tmp.Set("one", "1").Set("two", "2").Set("three", "3").Render();// 1,2,3
var tmp = new Template("{{name}},{{greet}}!");
tmp.Set("name", "万金油");
string s = tmp.Render();// throw 模版变量{{greet}}未被使用

11.List转Datatable

var list = new List<MyClass>()
{
    new MyClass()
    {
        Name = "张三",
        Age = 22
    },
    new MyClass()
    {
        Name = "李四",
        Age = 21
    },
    new MyClass()
    {
        Name = "王五",
        Age = 28
    }
};
var table = list.Select(c => new{姓名=c.Name,年龄=c.Age}).ToDataTable();// 将自动填充列姓名和年龄

12.文件压缩解压

.NET Framework

MemoryStream ms = SevenZipCompressor.ZipStream(new List<string>()
{
    @"D:\1.txt",
    "http://ww3.sinaimg.cn/large/87c01ec7gy1fsq6rywto2j20je0d3td0.jpg",
});//压缩成内存流
SevenZipCompressor.Zip(new List<string>()
{
    @"D:\1.txt",
    "http://ww3.sinaimg.cn/large/87c01ec7gy1fsq6rywto2j20je0d3td0.jpg",
}, zip);//压缩成zip
SevenZipCompressor.UnRar(@"D:\Download\test.rar", @"D:\Download\");//解压rar
SevenZipCompressor.Decompress(@"D:\Download\test.tar", @"D:\Download\");//自动识别解压压缩包
SevenZipCompressor.Decompress(@"D:\Download\test.7z", @"D:\Download\");

ASP.NET Core

Startup.cs

services.AddSevenZipCompressor();

构造函数注入ISevenZipCompressor

private readonly ISevenZipCompressor _sevenZipCompressor;
public Test(ISevenZipCompressor sevenZipCompressor)
{
    _sevenZipCompressor = sevenZipCompressor;
}

使用方式同.NET Framework版本

13.简易日志组件(又不是不能用.jpg)

LogManager.LogDirectory=AppDomain.CurrentDomain.BaseDirectory+"/logs";
LogManager.Event+=info =>
{
    //todo:注册一些事件操作
};
LogManager.Info("记录一次消息");
LogManager.Error(new Exception("异常消息"));

14.多线程后台下载

var mtd = new MultiThreadDownloader("https://attachments-cdn.shimo.im/yXwC4kphjVQu06rH/KeyShot_Pro_7.3.37.7z",Environment.GetEnvironmentVariable("temp"),"E:\\Downloads\\KeyShot_Pro_7.3.37.7z",8);
mtd.Configure(req =>
 {
     req.Referer = "https://masuit.com";
     req.Headers.Add("Origin", "https://baidu.com");
});
mtd.TotalProgressChanged+=(sender, e) =>
{
    var downloader = sender as MultiThreadDownloader;
    Console.WriteLine("下载进度:"+downloader.TotalProgress+"%");
    Console.WriteLine("下载速度:"+downloader.TotalSpeedInBytes/1024/1024+"MBps");
};
mtd.FileMergeProgressChanged+=(sender, e) =>
{
    Console.WriteLine("下载完成");
};
mtd.FileMergedComplete+=(sender,e)=>{
    Console.WriteLine("文件合并完成");
};
mtd.Start();//开始下载
//mtd.Pause(); // 暂停下载
//mtd.Resume(); // 继续下载

15.加密解密/hash

var enc="123456".MDString();// MD5
var enc="123456".MDString("abc");// MD5加盐
var enc="123456".MDString2();// MD5两次
var enc="123456".MDString2("abc");// MD5两次加盐
var enc="123456".MDString3();// MD5三次
var enc="123456".MDString3("abc");// MD5三次加盐

string aes = "123456".AESEncrypt();// AES加密为密文
string s = aes.AESDecrypt(); //AES解密为明文
string aes = "123456".AESEncrypt("abc");// AES密钥加密为密文
string s = aes.AESDecrypt("abc"); //AES密钥解密为明文

string enc = "123456".DesEncrypt();// DES加密为密文
string s = enc.DesDecrypt(); //DES解密为明文
string enc = "123456".DesEncrypt("abcdefgh");// DES密钥加密为密文
string s = enc.DesDecrypt("abcdefgh"); //DES密钥解密为明文

RsaKey rsaKey = RsaCrypt.GenerateRsaKeys();// 生成RSA密钥对
string encrypt = "123456".RSAEncrypt(rsaKey.PublicKey);// 公钥加密
string s = encrypt.RSADecrypt(rsaKey.PrivateKey);// 私钥解密

string s = "123".Crc32();// 生成crc32摘要
string s = "123".Crc64();// 生成crc64摘要
string s = "123".SHA256();// 生成SHA256摘要

// 零宽字符串,通常用作文章暗水印,以一种看不见的字符插入到文本中,使攻击者无法直接识别文本内容,从而起到保护文章的作用,可通过代码把水印还原出来取证。
string pub="hello,world!";
string hidden="ldqk";
var str = pub.InjectZeroWidthString(hidden); // 扩展函数调用:将"ldqk"以零宽字符串的方式隐藏在"hello,world!"中
var str = ZeroWidthCodec.Encrypt(pub,hidden); // 类调用:将"ldqk"以零宽字符串的方式隐藏在"hello,world!"中
var dec = str.DecodeZeroWidthString(); // 扩展函数调用:将包含零宽字符串的密文解密出隐藏字符串"ldqk"
var dec = ZeroWidthCodec.Decrypt(str); // 类调用:将包含零宽字符串的密文解密出隐藏字符串"ldqk"
var enc = hidden.EncodeToZeroWidthText(); // 扩展函数调用:将字符串编码成零宽字符串
var enc = ZeroWidthCodec.Encode(str); // 类调用:将字符串编码成零宽字符串

16.实体校验

public class MyClass
{
    [IsEmail] //可在appsetting.json中添加EmailDomainWhiteList配置邮箱域名白名单,逗号分隔
    public string Email { get; set; }

    [IsPhone]
    public string PhoneNumber { get; set; }

    [IsLandline]
    public string Landline { get; set; }

    [IsIPAddress]
    public string IP { get; set; }

    [MinValue(0, ErrorMessage = "年龄最小为0岁"), MaxValue(100, ErrorMessage = "年龄最大100岁")]
    public int Age { get; set; }

    [ComplexPassword]//密码复杂度校验,默认最小长度6,最大长度30,必须包含数字、字母、特殊符号
    public string Password { get; set; }
  
    [ComplexPassword(MustNumber=true,MustLetter=true,MustSymbol=true)]//密码复杂度校验,默认最小长度6,最大长度30,手动配置必须包含数字、字母、特殊符号
    public string Password { get; set; }
  
    [ComplexPassword(4,12)]//密码复杂度校验,配置最小长度4,最大长度12
    public string Password { get; set; }
  
    [EnumOf] // 检测是否是有效枚举值
    public MyEnum MyEnum { get; set; }
  
    [MinItemsCount(1)] // 检测集合元素最少1个
    public List<string> Strs { get; set; }
  
    [UnifiedSocialCreditCode] // 校验企业统一社会信用代码
    public string USCC { get; set; }
}

17.HTML操作

List<string> srcs = "html".MatchImgSrcs().ToList();// 获取html字符串里所有的img标签的src属性
var imgTags = "html".MatchImgTags();//获取html字符串里的所有的img标签
var str="html".RemoveHtmlTag(); // 去除html标签
...

18.IP地址和URL

bool inRange = "192.168.2.2".IpAddressInRange("192.168.1.1","192.168.3.255");// 判断IP地址是否在这个地址段里
bool isPrivateIp = "172.16.23.25".IsPrivateIP();// 判断是否是私有地址
bool isExternalAddress = "http://baidu.com".IsExternalAddress();// 判断是否是外网的URL

//以下需要配置baiduAK
string isp = "114.114.114.114".GetISP(); // 获取ISP运营商信息
PhysicsAddress physicsAddress = "114.114.114.114".GetPhysicsAddressInfo().Result;// 获取详细地理信息对象
Tuple<string, List<string>> ipAddressInfo = "114.114.114.114".GetIPAddressInfo().Result;// 获取详细地理信息集合

uint number=ipAddress.ToUInt32(); // IP地址转10进制
uint number="114.114.114.114".IPToID(); // IP地址转10进制

19.对象属性值合并

public class MyClass
{
    public string A { get; set; }

    public bool? B { get; set; }

    public int? C { get; set; }
}

var a = new MyClass()
{
    A = "aa"
};
var b = new MyClass()
{
    B = true
};
var c = new MyClass()
{
    C = 3
};
var merge = a.Merge(b, c); // 合并后对象:A = "aa",B = true,C = 3

20.元素去重

var list = new List<MyClass>()
{
    new MyClass()
    {
        Email = "[email protected]"
    },
    new MyClass()
    {
        Email = "[email protected]"
    },
    new MyClass()
    {
        Email = "[email protected]"
    }
};
List<MyClass> classes = list.DistinctBy(c => c.Email).ToList();
Console.WriteLine(classes.Count==1);//True

21.枚举扩展

[Flags]
public enum MyEnum
{
    [Display(Name = "")]
    [Description("")]
    [EnumDescription("读取操作","","zh-CN")] // 多语言枚举描述
    [EnumDescription("Read","Read","en-US")]
    Read=1,
  
    [Display(Name = "")]
    [Description("")]
    Write=2,

    Delete=4,

    All=8
}
Dictionary<int, string> dic1 = typeof(MyEnum).GetDictionary();// 获取枚举值和字符串表示的字典映射
var dic2 = typeof(MyEnum).GetDescriptionAndValue();// 获取字符串表示和枚举值的字典映射
string desc = MyEnum.Read.GetDescription();// 获取Description标签
string display = MyEnum.Read.GetDisplay();// 获取Display标签的Name属性
var value = typeof(MyEnum).GetValue("Read");//获取字符串表示值对应的枚举值
var op=MyEnum.Read|MyEnum.Write|MyEnum.Delete;
var enums=op.Split(); // 拆分枚举值,得到枚举数组,这个函数建议使用在按位定值的枚举

22.定长队列和ConcurrentHashSet实现

如果是.NET5及以上,推荐使用框架自带的Channel实现该功能

LimitedQueue<string> queue = new LimitedQueue<string>(32);// 声明一个容量为32个元素的定长队列
ConcurrentLimitedQueue<string> queue = new ConcurrentLimitedQueue<string>(32);// 声明一个容量为32个元素的线程安全的定长队列
var set = new ConcurrentHashSet<string>(); // 用法和hashset保持一致

23.反射操作

MyClass myClass = new MyClass();
PropertyInfo[] properties = myClass.GetProperties();// 获取属性列表
myClass.SetProperty("Email","[email protected]");//给对象设置值
myClass.DeepClone(); // 对象深拷贝,带嵌套层级的
myClass.ToDictionary(); // 对象转字典
myClass.ToDynamic(); // 对象转换成动态可扩展类型

24.邮件发送

new Email()
{
    SmtpServer = "smtp.masuit.com",// SMTP服务器
    SmtpPort = 25, // SMTP服务器端口
    EnableSsl = true,//使用SSL
    Username = "[email protected]",// 邮箱用户名
    Password = "123456",// 邮箱密码
    Tos = "[email protected],[email protected]", //收件人
    Subject = "测试邮件",//邮件标题
    Body = "你好啊",//邮件内容
}.SendAsync(s =>
{
    Console.WriteLine(s);// 发送成功后的回调
});// 异步发送邮件

25.图像的简单处理

"base64".SaveDataUriAsImageFile();// 将Base64编码转换成图片

using Image image = Image.Load(@"D:\1.jpg");
image.MakeThumbnail(@"D:\2.jpg", 120, 80,ResizeMode.BoxPad);//生成缩略图

var newBmp = image.BWPic(image.Width, image.Height);//转换成黑白
var newBmp = image.CutImage(new Rectangle(0, 0, 1600, 900));//裁剪
var newBmp = image.CutAndResize(new Rectangle(0, 0, 1600, 900), 160, 90);//裁剪并缩放
var newBmp = image.ResizeImage(160, 90);//改变大小
var newBmp = image.RevPicLR();//左右镜像
var newBmp = image.RevPicUD();//上下镜像
var newBmp =image.LDPic(10); //调整光暗
var newBmp =image.RePic(); //反色处理
var newBmp =image.Relief(); //浮雕处理

var gif = Image.Load(@"D:\1.gif");
gif.GetFrames(@"D:\frames\"); // 解压gif每帧图片

var marker=ImageWatermarker(stream);
stream=maker.AddWatermark("水印文字","字体文件",字体大小,color,水印位置,边距); // 给图片添加水印
stream=maker.AddWatermark(水印图片,水印位置,边距,字体大小,字体); // 给图片添加水印

// 图像相似度对比
var hasher = new ImageHasher();
var hash1 = hasher.DifferenceHash256("图片1"); // 使用差分哈希算法计算图像的256位哈希
var hash2 = hasher.DifferenceHash256("图片2"); // 使用差分哈希算法计算图像的256位哈希
//var hash1 = hasher.AverageHash64("图片1"); // 使用平均值算法计算图像的64位哈希
//var hash2 = hasher.AverageHash64("图片2"); // 使用平均值算法计算图像的64位哈希
//var hash1 = hasher.DctHash("图片1"); // 使用DCT算法计算图像的64位哈希
//var hash2 = hasher.DctHash("图片2"); // 使用DCT算法计算图像的64位哈希
//var hash1 = hasher.MedianHash64("图片1"); // 使用中值算法计算给定图像的64位哈希
//var hash2 = hasher.MedianHash64("图片2"); // 使用中值算法计算给定图像的64位哈希
var sim=ImageHasher.Compare(hash1,hash2); // 图片的相似度,范围:[0,1]

var imageFormat=stream.GetImageType(); // 获取图片的真实格式

26.随机数

Random rnd = new Random();
int num = rnd.StrictNext();//产生真随机数
double gauss = rnd.NextGauss(20,5);//产生正态高斯分布的随机数
var s = new NumberFormater(62).ToString(new Random().Next(100000, int.MaxValue));//生成随机字符串

27.权重随机筛选功能

var data=new List<WeightedItem<string>>()
{
     new WeightedItem<string>("A", 1),
     new WeightedItem<string>("B", 3),
     new WeightedItem<string>("C", 4),
     new WeightedItem<string>("D", 4),
};
var item=data.WeightedItem();//按权重选出1个元素
var list=data.WeightedItems(2);//按权重选出2个元素
var selector = new WeightedSelector<string>(new List<WeightedItem<string>>()
{
    new WeightedItem<string>("A", 1),
    new WeightedItem<string>("B", 3),
    new WeightedItem<string>("C", 4),
    new WeightedItem<string>("D", 4),
});
var item = selector.Select();//按权重选出1个元素
var list = selector.SelectMultiple(3);//按权重选出3个元素
list.WeightedItems(3,e=>e.Price); // 按价格权重选出3个元素
list.WeightedBy(e=>e.Price); // 按价格权重选出1个元素

28.EF Core支持AddOrUpdate方法

/// <summary>
/// 按Id添加或更新文章实体
/// </summary>
public override Post SavePost(Post t)
{
    DataContext.Set<Post>().AddOrUpdate(t => t.Id, t);
    return t;
}

29.敏感信息掩码

"13123456789".Mask(); // 131****5678
"[email protected]".MaskEmail(); // a****[email protected]
// Attribute的方式为json序列化时进行数据脱敏
public class MyClass
{
    [JsonConverter(typeof(MaskEmailConverter))] // 请注意命名空间,使用Newtonsoft.Json请导入Masuit.Tools.Systems命名空间,使用System.Text.Json请导入Masuit.Tools.Systems.Text.Json命名空间
    public string Email { get; set; }

    [JsonConverter(typeof(MaskConverter))] // 请注意命名空间,使用Newtonsoft.Json请导入Masuit.Tools.Systems命名空间,使用System.Text.Json请导入Masuit.Tools.Systems.Text.Json命名空间
    public string PhoneNumber { get; set; }
}

30.集合扩展

var list = new List<string>()
{
    "1","3","3","3"
};
list.AddRangeIf(s => s.Length > 1, "1", "11"); // 将被添加元素中的长度大于1的元素添加到list
list.AddRangeIfNotContains("1", "11"); // 将被添加元素中不包含的元素添加到list
list.RemoveWhere(s => s.Length<1); // 将集合中长度小于1的元素移除
list.InsertAfter(0, "2"); // 在第一个元素之后插入
list.InsertAfter(s => s == "1", "2"); // 在元素"1"后插入

var dic = list.ToDictionarySafety(s => s); // 安全的转换成字典类型,当键重复时只添加一个键
var dic = list.ToConcurrentDictionary(s => s); // 转换成并发字典类型,当键重复时只添加一个键
var dic = list.ToDictionarySafety(s => s, s => s.GetHashCode()); // 安全的转换成字典类型,当键重复时只添加一个键
var v = dic[x=>x.Key.Contains("1")]; // 字典根据条件取值
dic[x=>x.Key.Contains("1")]=2; // 字典根据条件赋值
var v = dic[(key,value)=>key.Contains("1")]; // 字典根据条件取值
dic[(key,value)=>key.Contains("1")]=2; // 字典根据条件赋值
var v = dic[key=>key.Contains("1")]; // 字典根据key条件取值
dic[key=>key.Contains("1")]=2; // 字典根据key条件赋值
var v = dic[value=>value>0]; // 字典根据value条件取值
dic[value=>value>0]=2; // 字典根据value条件赋值
dic.AddOrUpdate("4", 4); // 添加或更新键值对
dic.AddOrUpdate(new Dictionary<string, int>()
{
    ["5"] = 5,["55"]=555
}); // 批量添加或更新键值对
dic.AddOrUpdate("5", 6, (s, i) => 66); // 如果是添加,则值为6,若更新则值为66
dic.AddOrUpdate("5", 6, 666); // 如果是添加,则值为6,若更新则值为666
dic.GetOrAdd("7",77); // 字典获取或添加元素
dic.GetOrAdd("7",()=>77); // 字典获取或添加元素
dic.AsConcurrentDictionary(); // 普通字典转换成并发字典集合

var table=list.ToDataTable(); // 转换成DataTable类型
table.AddIdentityColumn(); //给DataTable增加一个自增列
table.HasRows(); // 检查DataTable 是否有数据行
table.ToList<T>(); // datatable转List
var set = list.ToHashSet(s=>s.Name);// 转HashSet
var cts = new CancellationTokenSource(100); //取消口令
await list.ForeachAsync(async i=>{
    await Task.Delay(100);
    Console.WriteLine(i);
},cts.Token); // 异步foreach

await list.ForAsync(async (item,index)=>{
    await Task.Delay(100);
    Console.WriteLine(item+"_"+index);
},cts.Token); // 异步for,带索引编号
await list.SelectAsync(async i=>{
    await Task.Delay(100);
    return i*10;
}); // 异步Select
await list.SelectAsync(async (item,index)=>{
    await Task.Delay(100);
    return item*10;
}); // 异步Select,带索引编号
string s=list.Join(",");//将字符串集合连接成逗号分隔的单字符串
var max=list.MaxOrDefault(); // 取最大值,当集合为空的时候不会报错
var max=list.MaxOrDefault(selector); // 取最大值,当集合为空的时候不会报错
var max=list.MaxOrDefault(selector,default); // 取最大值,当集合为空的时候不会报错
var max=list.MinOrDefault(); // 取最小值,当集合为空的时候不会报错
var max=list.MinOrDefault(selector); // 取最小值,当集合为空的时候不会报错
var max=list.MinOrDefault(selector,default); // 取最小值,当集合为空的时候不会报错
var stdDev=list.Select(s=>s.ConvertTo<int>()).StandardDeviation(); // 求标准差

var pages=queryable.ToPagedList(1,10); // 分页查询
var pages=await queryable.ToPagedListAsync(1,10); // 分页查询

var nums=Enumerable.Range(1, 10).ExceptBy(Enumerable.Range(5, 10), i => i); // 按字段取差集
var nums=Enumerable.Range(1, 10).IntersectBy(Enumerable.Range(5, 10), i => i); // 按字段取交集
var nums=Enumerable.Range(1, 10).SequenceEqual(Enumerable.Range(5, 10), i => i); // 判断序列相等
var nums=Enumerable.Range(1, 10).OrderByRandom(); // 随机排序

// 多个集合取交集
var list=new List<List<MyClass>>(){
    new List<MyClass>(){
        new MyClass(){Name="aa",Age=11},
        new MyClass(){Name="bb",Age=12},
        new MyClass(){Name="cc",Age=13},
    },
    new List<MyClass>(){
        new MyClass(){Name="bb",Age=12},
        new MyClass(){Name="cc",Age=13},
        new MyClass(){Name="dd",Age=14},
    },
    new List<MyClass>(){
        new MyClass(){Name="cc",Age=13},
        new MyClass(){Name="dd",Age=14},
        new MyClass(){Name="ee",Age=15},
    },
};
var sect=list.IntersectAll(m=>m.Name); // new MyClass(){Name="cc",Age=13}

var list=new List<List<int>>(){
    new(){1,2,3},
    new(){2,3,4},
    new(){3,4,5}
};
var sect=list.IntersectAll();// [3]

// 集合元素改变其索引位置
list.ChangeIndex(item,3); // 将元素item的索引位置变为第3个
list.ChangeIndex(t=>t.Id=="123",2); // 将id为123的元素的索引位置变为第2个

var item=list.Percentile(50); // 取第50%分位数的元素

31.Mime类型

var mimeMapper = new MimeMapper();
var ext = mimeMapper.GetExtensionFromMime("image/jpeg"); // .jpg
var mime = mimeMapper.GetMimeFromExtension(".jpg"); // image/jpeg

ContentType常量库:
var type=ContentType.Exe; // application/octet-stream
var type=ContentType.Jpeg; // image/jpeg
var type=DefaultMimeItems.Items.FirstOrDefault(t=>t.Extension=="jpg"); // image/jpeg

32.日期时间扩展

double milliseconds = DateTime.Now.GetTotalMilliseconds();// 获取毫秒级时间戳
double microseconds = DateTime.Now.GetTotalMicroseconds();// 获取微秒级时间戳
double nanoseconds = DateTime.Now.GetTotalNanoseconds();// 获取纳秒级时间戳
double seconds = DateTime.Now.GetTotalSeconds();// 获取秒级时间戳
double minutes = DateTime.Now.GetTotalMinutes();// 获取分钟级时间戳

var indate=DateTime.Parse("2020-8-3").In(DateTime.Parse("2020-8-2"),DateTime.Parse("2020-8-4"));//true
DateTime time="2021-1-1 8:00:00".ToDateTime(); //字符串转DateTime

//时间段计算工具
var range = new DateTimeRange(DateTime.Parse("2020-8-3"), DateTime.Parse("2020-8-5"));
range.Union(DateTime.Parse("2020-8-4"), DateTime.Parse("2020-8-6")); //连接两个时间段,结果:2020-8-3~2020-8-6
range.In(DateTime.Parse("2020-8-3"), DateTime.Parse("2020-8-6"));//判断是否在某个时间段内,true
var (intersected,range2) = range.Intersect(DateTime.Parse("2020-8-4"), DateTime.Parse("2020-8-6"));//两个时间段是否相交,(true,2020-8-3~2020-8-4)
range.Contains(DateTime.Parse("2020-8-3"), DateTime.Parse("2020-8-4"));//判断是否包含某个时间段,true

range.GetUnionSet(List<DateTimeRange>); // 根据某个时间段查找在某批时间段中的最大并集
range.GetMaxTimePeriod(List<DateTimeRange>); // 获取一批时间段内存在相互重叠的最大时间段
...

33.流相关

stream.SaveAsMemoryStream(); // 任意流转换成内存流
stream.ToArray(); // 任意流转换成二进制数组
stream.ToArrayAsync(); // 任意流转换成二进制数组
stream.ShuffleCode(); // 流洗码,在流的末端随即增加几个空字节,重要数据请谨慎使用,可能造成流损坏

// 池化内存流,用法与MemorySteam保持一致
using var ms=PooledMemoryStream();

// 大型内存流,最大可支持1TB内存数据,推荐当数据流大于2GB时使用,用法与MemorySteam保持一致
using var ms=LargeMemoryStream();

//文件流快速复制
FileStream fs = new FileStream(@"D:\boot.vmdk", FileMode.OpenOrCreate, FileAccess.ReadWrite);
{
        //fs.CopyToFile(@"D:\1.bak");//同步复制大文件
        fs.CopyToFileAsync(@"D:\1.bak");//异步复制大文件
        string md5 = fs.GetFileMD5Async().Result;//异步获取文件的MD5
        string sha1 = fs.GetFileSha1();//异步获取文件的SHA1
}
memoryStream.SaveFile("filename"); // 将内存流转储成文件

34.类型操作/字符串

1.2345678901.Digits8(); // 将小数截断为8位
1.23.ConvertTo<int>(); // 小数转int
1.23.ConvertTo<T>(); // 小数转T基本类型
bool b=1.23.TryConvertTo<T>(out result); // 小数转T基本类型
var num=1.2345.ToDecimal(2); //转decimal并保留两位小数

1.23.ChangeTypeTo<T>(); //小数转T基本类型,ConvertTo和ChangeTypeTo的区别在于:ConvertTo只适用于基元类型的互转,ChangeTypeTo不仅适用于基元类型的互转还支持数组、字符串的转换(Parse),ConvertTo的性能更高

type.IsPrimitive(); // 判断类型是否是值类型
type.IsSimpleType(); // 判断类型是否是常见的简单类型,基元类型为 Boolean、 Byte、 SByte、 Int16、 UInt16、 Int32、 UInt32、 Int64、 UInt64、 IntPtr、 UIntPtr、 Char、 Double 、 Single、枚举、Nullable<T>。
type.IsSimpleArrayType(); // 判断类型是否是常见类型的 数组形式 类型
type.IsSimpleListType(); // 判断类型是否是常见类型的 泛型形式 类型

myClass.ToJsonString(); //序列化成json字符串

string s=null;
bool b=s.IsNullOrEmpty();//判断字符串是否为空
bool b=s.NotNullOrEmpty();//判断字符串不为空
string str=s.IfNullOrEmpty("aa");//如果为空则返回aa
string str=s.IfNullOrEmpty(()=>"aa");//如果为空则返回aa,延迟执行

bool contains=s.Contains(new[]{"aa","bb"});// 检测字符串中是否包含列表中的关键词(快速匹配)
bool contains=s.ContainsSafety(new[]{"aa","bb"});// 检测字符串中是否包含列表中的关键词(安全匹配),没有计时攻击风险
bool contains=s.EndsWith(new[]{"aa","bb"});// 检测字符串中是否以列表中的任意关键词结尾
bool contains=s.StartsWith(new[]{"aa","bb"});// 检测字符串中是否以列表中的任意关键词开头

string str=s.Take(10); // 取字符串前10个字符

bool emoji=s.MatchEmoji(); // 匹配字符串是否包含emoji

var width=str.StringWidth(14); // 计算字符串以14号字体大小的渲染宽度像素
var width=str.StringWidth("微软雅黑",14); // 计算字符串以14号字体大小的微软雅黑字体的渲染宽度像素
var width=str.CharacterCount(); // 获取字符串的字符数
var width=str.BytesCount(); // 获取字符串的字节数


var s = "🤔1🥳a👨‍👩‍👧‍👦啊";
Console.WriteLine("长度:" + s.Length); // 18
Console.WriteLine("宽度:" + s.StringWidth()); // 7
Console.WriteLine("字节数:" + s.BytesCount()); // 38
Console.WriteLine("字符数:" + s.CharacterCount()); // 6

var s="aa".ToSBC(); // 转换为全角
var s="aa".ToDBC(); // 转换为半角

35.INI配置文件操作

IniFile ini=new IniFile("X:\\filename.ini"); // 需要绝对路径,否则会写到C:\Windows目录下去
ini.SetValue(section,key,value); // 写值
var value=ini.GetValue(section,key); // 读值
var value=ini.GetValue<Enum>(section,key); // 读值并转换类型
var sections=ini.GetSections(); // 获取所有配置节
var section=ini.GetSection(section); // 获取配置节
var myclass=ini.GetSection<MyClass>(section); // 获取指定配置节并绑定到对象
ini.ClearAllSection(); // 清空所有配置节
ini.ClearSection(section); // 清空配置节
ini.Save(); // 保存ini文件
ini.Reload(); // 重新加载ini文件


class MyClass
{
    [IniProperty("str_value")] // 设置别名
    public string StringValue { get; set; }
}

36.雷达图计算引擎

应用场景:计算两个多边形的相似度,用户画像之类的

var points=RadarChartEngine.ComputeIntersection(chart1,chart2); //获取两个多边形的相交区域
points.ComputeArea(); //计算多边形面积

37.树形结构实现

基本接口类: ITreeChildren:带Children属性的接口
ITreeParent:带Parent属性的接口
ITree:继承ITreeParent和ITreeChildren,同时多了Name属性
ITreeEntity:继承ITreeChildren,同时多了Id和ParentId属性

相关扩展方法:

trees.Filter(func); // 从树形集合中过滤
trees.Flatten(); // 将数据平铺开
tree.AllChildren(); // 获取所有的子级
tree.AllParent(); // 获取所有的父级
tree.IsRoot(); // 是否是根节点
tree.IsLeaf(); // 是否是叶子节点
tree.Level(); // 所处深度/层级
tree.Path(); // 全路径

var tree=list.ToTree();//集合元素继承自ITreeEntity<T,TKey>或ITreeEntity<T>的集合转换成树形结构
var tree=list.ToTree(c => c.Id, c => c.Pid);//集合元素继承自ITreeParent<T>, ITreeChildren<T>的集合转换成树形结构
var tree=list.ToTreeGeneral(c => c.Id, c => c.Pid);//一般的集合转换成树形结构

Tree结构性能跑分

38.简单的Excel导出

需要额外依赖包:Masuit.Tools.Excel

var stream=list.Select(item=>new{
    姓名=item.Name,
    年龄=item.Age,
    item.Gender,
    Avatar=Image.FromStream(filestream) //图片列
}).ToDataTable().ToExcel("Sheet1"); //自定义列名导出
var stream=list.ToDataTable("Sheet1").ToExcel("文件密码");

一些约定规则:

  1. 图片列支持Stream、Bitmap、IEnumerable <Stream>、IEnumerable <Bitmap>、IDictionary<string,Stream>、IDictionary<string,MemoryStream>、IDictionary<string,Bitmap>类型;
  2. 其中,如果是IDictionary类型的图片列,字典的键为图片超链接的完整url;
  3. 默认字段名作为列名导出;
  4. 若list是一个具体的强类型,默认会先查找每个字段的Description标记,若有Description标记,则取Description标记作为列名显示
  5. ToExcel方法支持DataTable、List <DataTable>、Dictionary<string, DataTable>类型的直接调用

39.EFCore实用扩展

跟踪实体变更对比

获取指定实体的变更

var changes=dbContext.GetChanges<TEntity>();//获取变更字段信息
var added=dbContext.GetAdded<TEntity>();//获取添加的实体字段信息
var removed=dbContext.GetRemoved<TEntity>();//获取被移除的实体字段信息  
var allchanges=dbContext.GetAllChanges<TEntity>();//获取增删改的实体字段信息  

获取所有实体的变更

var changes=dbContext.GetChanges();//获取变更字段信息
var added=dbContext.GetAdded();//获取添加的实体字段信息
var removed=dbContext.GetRemoved();//获取被移除的实体字段信息  
var allchanges=dbContext.GetAllChanges();//获取增删改的实体字段信息  

对比信息包含属性信息、旧值、新值、实体信息、键信息、变更状态等

nolock查询

sqlserver:

上下文注入Interceptor即可在任何查询时使用nolock查询

services.AddDbContext<TContext>(opt => opt.UseSqlserver("ConnString", builder => builder.AddInterceptors(new QueryWithNoLockDbCommandInterceptor()));

通用数据库:

nolock本质是开启一个 读未提交级别的事务,此时的查询性能最好,但有可能会读取到脏数据。

dbcontext.NoLock(ctx=>ctx.Users...ToList()); // 开启一个nolock上下文,上下文范围内自动nolock

// 执行单个nolock查询
await dbcontext.Users.Where(x=>x.Name=="aaa").ToListWithNoLockAsync(); 
await dbcontext.Users.Where(x=>x.Name=="aaa").FirstOrDefaultWithNoLockAsync(); 
await dbcontext.Users.Where(x=>x.Name=="aaa").SingleOrDefaultWithNoLockAsync(); 
await dbcontext.Users.AnyWithNoLockAsync(x=>x.Name=="aaa"); 
await dbcontext.Users.AllWithNoLockAsync(x=>x.Name=="aaa"); 
await dbcontext.Users.CountWithNoLockAsync(x=>x.Name=="aaa"); 

// 如果在上下文开启了重试机制,执行单个nolock查询需要开启策略查询
services.AddDbContext<TContext>(opt => opt.UseNpgsql("ConnString", builder => builder.EnableRetryOnFailure(10));

// 执行策略
dbcontext.ExecutionStrategy(stg=>stg.NoLock(ctx=>ctx.Users...ToList()));
dbcontext.ExecutionStrategy(ctx=>ctx.Users.Where(x=>x.Name=="aaa").ToListWithNoLockAsync());

自动递归式Include扩展(通常用于树形表)

// 如果你现在正在写类似这样的代码:
dbcontext.Category.Include(c=>c.Children).ThenInclude(c=>c.Children).ThenInclude(c=>c.Children).ThenInclude(c=>c.Children);

// 那么可以改成这样:
dbcontext.Category.IncludeRecursive(4, c => c.Children); // 自动Include 4次

40.任何类型支持链式调用

a.Next(func1).Next(func2).Next(func3);
"123".Next(s=>s.ToInt32()).Next(x=>x*2).Next(x=>Math.Log(x));

41.Newtonsoft.Json和System.Text.Json的只允许字段(反)序列化行为的契约解释器

DeserializeOnlyContractResolver

该解释器针对类属性被DeserializeOnlyJsonPropertyAttribute/SerializeIgnoreAttribute标记的,在反序列化的时候生效,在序列化的时候忽略;被SerializeOnlyJsonPropertyAttribute/DeserializeIgnoreAttribute标记的,在序列化的时候生效,在反序列化的时候忽略

public class ClassDto
    {
        // 序列化时忽略这个属性/反序列化时加载这个属性
        [DeserializeOnlyJsonProperty]
        //[SerializeIgnore]
        public string MyProperty { get; set; }

        // 反序列化时忽略这个属性/序列化时加载这个属性
        [SerializeOnlyJsonProperty]
        //[DeserializeIgnore]
        public int Num { get; set; }
    }
  
    // Newtonsoft.Json
    JsonConvert.SerializeObject(new MyClass(),new JsonSerializerSettings()
    {
        ContractResolver = new DeserializeOnlyContractResolver() // 配置使用DeserializeOnlyContractResolver解释器
    });

    // System.Text.Json
    JsonSerializer.Serialize(object, new JsonSerializerOptions() { TypeInfoResolver = new SerializeIgnoreResolver() });

如果是WebAPI全局使用:

        //在Startup.ConfigureServices中
        services.AddMvc().AddNewtonsoftJson(options =>
             {
                 var resolver = new DeserializeOnlyContractResolver();
                 resolver.NamingStrategy = new CamelCaseNamingStrategy();
                 options.SerializerSettings.ContractResolver = resolver;
             });

FallbackJsonPropertyResolver

该解释器针对某个属性设置多个别名,反序列化时支持多个别名key进行绑定,弥补官方JsonProperty别名属性只能设置单一别名的不足

    public class ClassDto
    {
        [FallbackJsonProperty("MyProperty","a","b")]
        public string MyProperty { get; set; }

        public int Num { get; set; }
    }
  
    JsonConvert.SerializeObject(new MyClass(),new JsonSerializerSettings()
    {
        ContractResolver = new FallbackJsonPropertyResolver() // 配置使用FallbackJsonPropertyResolver解释器
    });

CompositeContractResolver

该解释器是DeserializeOnlyContractResolver和FallbackJsonPropertyResolver的融合版

42. ASP.NET Core Action同时支持queryString、表单和json请求类型的模型绑点器BodyOrDefaultModelBinder

用法: 引入包:Masuit.Tools.AspNetCore

PM> Install-Package Masuit.Tools.AspNetCore

Startup配置:

app.UseBodyOrDefaultModelBinder();

在action的参数模型前打上标记:[FromBodyOrDefault]即可,示例代码如下:

        [HttpGet("query"),HttpPost("query")]
        public IActionResult Query([FromBodyOrDefault]QueryModel query)
        {
            return Ok(...);
        }
  
        [HttpGet("query"),HttpPost("query")]
        public IActionResult Query([FromBodyOrDefault]int id,[FromBodyOrDefault]string name)
        {
            return Ok(...);
        }

43. 字符串SimHash相似度算法

var dis="12345678".HammingDistance("1234567");
var dis=new SimHash("12345678").HammingDistance(new SimHash("1234567"));

44. 真实文件类型探测/文本编码检测

var encoding=new FileInfo(filepath).GetEncoding(); // 获取文件编码(扩展调用)
var encoding=stream.GetEncoding(); // 获取流的编码(扩展调用)
var encoding=TextEncodingDetector.GetEncoding(filepath); // 获取文件编码(类调用)

// 多种方式,任君调用
var detector=new FileInfo(filepath).DetectFiletype(); // 扩展调用
//var detector=File.OpenRead(filepath).DetectFiletype(); // 流扩展调用
//var detector=FileSignatureDetector.DetectFiletype(filepath); // 类调用

detector.Precondition;//基础文件类型
detector.Extension;//真实扩展名
detector.MimeType;//MimeType
detector.FormatCategories;//格式类别

默认支持的文件类型

扩展名 说明
3GP 3GPP, 3GPP 2
7Z 7-Zip
APK ZIP based Android Package
AVI Audio-Video Interleave
SH Shell Script
BPLIST Binary Property List
BMP, DIB Bitmap
BZ2 Bunzip2 Compressed
CAB Microsoft Cabinet
CLASS Java Bytecode
CONFIG .NET Configuration File
CRT, CERT Certificate
CUR Cursor
DB Windows Thumbs.db Thumbnail Database
DDS DirectDraw Surface
DLL Windows Dynamic Linkage Library
DMG Apple Disk Mount Image
DMP Windows Memory Dump File
DOC Microsoft Office Word 97-2003 Document
DOCX Microsoft Office Word OpenXML Document
EPUB e-Pub Document
EXE Windows Executive
FLAC Loseless Audio
FLV Flash Video
GIF Graphics Interchage Format
GZ GZ Compressed
HDP HD Photo(JPEG XR) Image
HWP Legacy HWP, HWPML, CFBF HWP
ICO Icon
INI Initialization File
ISO ISO-9660 Disc Image
LNK Windows Shortcut Link
JP2 JPEG 2000 Image
JPG, JPEG Joint Photographic Experts Group Image
LZH LZH Compressed
M4A MP4 Container Contained Audio Only
M4V MP4 Container Contained Video
MID Midi Sound
MKA Matroska Container Contained Audio Only
MKV Matroska Container Contained Video
MOV QuickTime Movie Video
MP4 MP4 Container Contained Contents
MSI Microsoft Installer
OGG OGG Video or Audio
ODF OpenDocument Formula
ODG OpenDocument Graphics
ODP OpenDocument Presentation
ODS OpenDocument Spreadsheet
ODT OpenDocument Text
PAK PAK Archive or Quake Archive
PDB Microsoft Program Database
PDF Portable Document Format
PFX Microsoft Personal Information Exchange Certificate
PNG Portable Network Graphics Image
PPT Microsoft Office PowerPoint 97-2003 Document
PPTX Microsoft Office PowerPoint OpenXML Document
PPSX Microsoft Office PowerPoint OpenXML Document for Slideshow only
PSD Photoshop Document
RAR WinRAR Compressed
REG Windows Registry
RPM RedHat Package Manager Package
RTF Rich Text Format Document
SLN Microsoft Visual Studio Solution
SRT SubRip Subtitle
SWF Shockwave Flash
SQLITE, DB SQLite Database
TAR pre-ISO Type and UStar Type TAR Package
TIFF Tagged Image File Format Image
TXT Plain Text
WAV Wave Audio
WASM Binary WebAssembly
WEBM WebM Video
WEBP WebP Image
XAR XAR Package
XLS Microsoft Office Excel 97-2003 Document
XLSX Microsoft Office Excep OpenXML Document
XML Extensible Markup Language Document
Z Z Compressed
ZIP ZIP Package

45. 动态类型扩展

让动态类型支持属性访问器和索引器调用

        var obj = DynamicFactory.NewObject();
        obj.Name = "Masuit";
        obj.Age = 18;
        obj["Gender"]=""
        obj.MyClass = DynamicFactory.WithObject(new
        {
            X = 10,
            Y = 20,
            Z = new List<int> { 1, 2, 3, 4, 5 }
        });
        Assert.Equal(obj.Name, obj["Name"]);
        Assert.Equal(obj["Gender"], obj.Gender);
        Assert.Equal(obj["MyClass"]["X"], obj.MyClass.X);
        Assert.Equal(obj.MyClass.Z[2], obj["MyClass"]["Z"][2]);

普通类型转换成动态类型

        var obj = new
        {
            Name = "Masuit"
        }.ToDynamic();
        obj.Age = 18;
        obj.MyClass = new
        {
            X = 10,
            Y = 20,
            Z = new List<int> { 1, 2, 3, 4, 5 }
        }.ToDynamic();
        obj.Prop = "test";
        _ = obj - "Prop"; // 删除属性
        _ = obj + "Prop"; // 增加属性

        Assert.Equal(obj.Name, obj["Name"]);
        Assert.Equal(obj["MyClass"]["X"], obj.MyClass.X);

46. 反病毒(仅支持Windows)

// 要求系统WindowsDefender没有被停掉
var result = WindowsDefenderScanService.ScanFile(@"Y:\1.exe"); // 扫描文件
var result = WindowsDefenderScanService.ScanDirectory(@"Y:\"); // 扫描文件夹
var result = WindowsDefenderScanService.ScanStream(stream); // 扫描文件流

// 要求C:\Windows\System32\amsi.dll文件存在,可在WindowsDefender停止时工作
AmsiScanService.Scan(stream); // 扫描文件流
AmsiScanService.Scan(@"Y:\1.exe"); // 扫描文件
AmsiScanService.Scan(bytes); // 扫描二进制数组

47. 生成验证码

var code=ValidateCode.ValidateCode(6); // 生成6位长度的验证码
var stream=code.CreateValidateGraphic(); // 生成验证码图片流

48. DistributedCache扩展

var item=cache.Get<T>(key); // 获取值
var item=cache.GetOrAdd<T>(key,value); // 获取或添加值
var item=cache.GetOrAdd<T>(key,valueFactory); // 获取或添加值
cache.Set<T>(key,value); // 设置值
cache.AddOrUpdate<T>(key,value,valueFactory); // 添加或更新值

49. ViewData扩展

var item=ViewData.GetValue<T>(key);//获取对象
var item=ViewData.GetValueOrDefault<T>(key,defaultValue);//获取对象
var item=ViewData.GetValueOrDefault<T>(key,defaultValueFactory);//获取对象

50. 线程上下文存取临时值

CurrentContext<T>.SetData(value);//设置值
var item=CurrentContext<T>.GetData();//获取值

CurrentContext.SetData(value);//设置值
var item=CurrentContext.GetData<T>();//获取值

51. ASP.NET Core自动扫描注册服务

// 自动扫描注册服务
services.AutoRegisterServices();

// 需要自动注册的服务打上ServiceInject标记即可。
[ServiceInject(ServiceLifetime.Scoped)]
public class MyClass:MyInterface{...}

[ServiceInject(ServiceLifetime.Scoped)]
public class MyService{...}

52. 房贷试算模型

集成案例:https://masuit.org/tools/loan

支持多次提前还款和多次调整利率,同时支持提前还款时变更贷款方式和缩短年限,如有利率调整或提前还款计划,因银行计算受实时利率或提前还款违约金影响,本试算模型的计算结果和银行结果大约有1‰的误差,结果仅供参考,请以银行结果为准。

模拟案例: 贷款100万, 初始利率6.27%, 等额本息方式, 贷30年, 首次还款时间2021-2-1。

利率调整: 2022-1-1利率调整为5.92%,LPR调整 2023-1-1利率调整为5.85%,LPR调整 2023-9-25利率调整为4.3%,政策因素银行自动调整 2025-1-1利率调整为4.2%,LPR调整 2026-1-1利率调整为4.1%,LPR调整

提前还款计划: 2022-10-23提前还款10万,贷款方式不变, 2023-10-11提前还款10万并缩短年限(实际目前银行政策不允许), 2025-10-12提前还款10万并修改为等额本金方式, 2026-10-14提前还款10万并以等额本金方式+缩短年限(实际目前银行政策不允许)。

计算代码如下:

var (totalInterest, actualInterest, savedInterest, totalRepayment, actualPayment, paymentPlans) = new LoanModel(1000000, 0.0627m, 360, DateTime.Parse("2021-2-1"))
{
    RateAdjustments = new Dictionary<DateTime, decimal?>()
    {
        [DateTime.Parse("2022-1-1")] = 0.0592m, // 调整前月供6170.19,调整后月供5948.53
        [DateTime.Parse("2023-1-1")] = 0.058m, // 调整前月供5948.53,调整后月供5273.92
        [DateTime.Parse("2023-9-25")] = 0.043m, // 调整前月供5273.92,调整后月供4496.91,调整次月还款5118.55
        [DateTime.Parse("2025-1-1")] = 0.042m, // 调整前月供4496.91,调整后首月4457.15
        [DateTime.Parse("2026-1-1")] = 0.041m, // 调整前月供4762.47,调整后月供4702,调整次月还款8.87元(还款方式改为了等额本金)
    },

    Prepayments = new List<PrepaymentOption>()
    {
        new(DateTime.Parse("2022-10-23"), 100000m, false, LoanType.EquivalentInterest), // 提前还款前月供5948.53,提前还款后月供5339.85
        new(DateTime.Parse("2023-10-11"), 100000m, true, LoanType.EquivalentInterest), // 提前还款前月供5273.92,提前还款后月供4493.84,期数减少64期
        new(DateTime.Parse("2025-10-12"), 100000m, false, LoanType.EquivalentPrincipal), // 提前还款前月供4771.56,提前还款后月供首月4762.47,每月递减60.4元
        new(DateTime.Parse("2026-10-14"), 100000m, true, LoanType.EquivalentPrincipal), // 提前还款前月供4260.28,提前还款后月供首月4251.44,每月递减8.84元,期数减少38期
    }
}.Payment();

计算结果: 总利息totalInterest:1221266.8 实际支付利息actualInterest:403845.58 提前还款节省利息savedInterest:817421.22 总提前还款totalRepayment:400000.00 实际还款总额actualPayment:1403845.58 总还款期数paymentPlans:258期,List类型,每条记录可以展示当期的利率,利息,本金,剩余本金等信息

Asp.Net MVC和Asp.Net Core的支持断点续传和多线程下载的ResumeFileResult

在ASP.NET Core中通过MVC/WebAPI应用程序传输文件数据时使用断点续传以及多线程下载支持。

它提供了 ETag标头以及 Last-Modified标头。 它还支持以下前置条件标头:If-MatchIf-None-MatchIf-Modified-SinceIf-Unmodified-SinceIf-Range

支持 ASP.NET Core 2.0+

从.NET Core2.0开始,ASP.NET Core内部支持断点续传。 因此只是对FileResult做了一些扩展。 只留下了“Content-Disposition” Inline的一部分。 所有代码都依赖于基础.NET类。

如何使用

.NET Framework

在你的控制器中,你可以像在 FileResult一样的方式使用它。

using Masuit.Tools.Mvc;
using Masuit.Tools.Mvc.ResumeFileResult;
private readonly MimeMapper mimeMapper=new MimeMapper(); // 推荐使用依赖注入

public ActionResult ResumeFileResult()
{
    var path = Server.MapPath("~/Content/test.mp4");
    return new ResumeFileResult(path, mimeMapper.GetMimeFromPath(path), Request);
}

public ActionResult ResumeFile()
{
    return this.ResumeFile("~/Content/test.mp4", mimeMapper.GetMimeFromPath(path), "test.mp4");
}

public ActionResult ResumePhysicalFile()
{
    return this.ResumePhysicalFile(@"D:/test.mp4", mimeMapper.GetMimeFromPath(@"D:/test.mp4"), "test.mp4");
}

Asp.Net Core

要使用ResumeFileResults,必须在 Startup.csConfigureServices方法调用中配置服务:

using Masuit.Tools.AspNetCore.ResumeFileResults.Extensions;
public void ConfigureServices(IServiceCollection services)
{
    services.AddResumeFileResult();
}

然后在你的控制器中,你可以像在 FileResult一样的方式使用它。

点击查看代码
using Masuit.Tools.AspNetCore.ResumeFileResults.Extensions;
private const string EntityTag = "\"TestFile\"";

private readonly IHostingEnvironment _hostingEnvironment;

private readonly DateTimeOffset _lastModified = new DateTimeOffset(2016, 1, 1, 0, 0, 0, TimeSpan.Zero);

/// <summary>
/// 
/// </summary>
/// <param name="hostingEnvironment"></param>
public TestController(IHostingEnvironment hostingEnvironment)
{
    _hostingEnvironment = hostingEnvironment;
}

[HttpGet("content/{fileName}/{etag}")]
public IActionResult FileContent(bool fileName, bool etag)
{
    string webRoot = _hostingEnvironment.WebRootPath;
    var content = System.IO.File.ReadAllBytes(Path.Combine(webRoot, "TestFile.txt"));
    ResumeFileContentResult result = this.ResumeFile(content, "text/plain", fileName ? "TestFile.txt" : null, etag ? EntityTag : null);
    result.LastModified = _lastModified;
    return result;
}

[HttpGet("content/{fileName}")]
public IActionResult FileContent(bool fileName)
{
    string webRoot = _hostingEnvironment.WebRootPath;
    var content = System.IO.File.ReadAllBytes(Path.Combine(webRoot, "TestFile.txt"));
    var result = new ResumeFileContentResult(content, "text/plain")
    {
        FileInlineName = "TestFile.txt",
        LastModified = _lastModified
    };
    return result;
}

[HttpHead("file")]
public IActionResult FileHead()
{
    ResumeVirtualFileResult result = this.ResumeFile("TestFile.txt", "text/plain", "TestFile.txt", EntityTag);
    result.LastModified = _lastModified;
    return result;
}

[HttpPut("file")]
public IActionResult FilePut()
{
    ResumeVirtualFileResult result = this.ResumeFile("TestFile.txt", "text/plain", "TestFile.txt", EntityTag);
    result.LastModified = _lastModified;
    return result;
}

[HttpGet("stream/{fileName}/{etag}")]
public IActionResult FileStream(bool fileName, bool etag)
{
    string webRoot = _hostingEnvironment.WebRootPath;
    FileStream stream = System.IO.File.OpenRead(Path.Combine(webRoot, "TestFile.txt"));

    ResumeFileStreamResult result = this.ResumeFile(stream, "text/plain", fileName ? "TestFile.txt" : null, etag ? EntityTag : null);
    result.LastModified = _lastModified;
    return result;
}

[HttpGet("stream/{fileName}")]
public IActionResult FileStream(bool fileName)
{
    string webRoot = _hostingEnvironment.WebRootPath;
    FileStream stream = System.IO.File.OpenRead(Path.Combine(webRoot, "TestFile.txt"));

    var result = new ResumeFileStreamResult(stream, "text/plain")
    {
        FileInlineName = "TestFile.txt",
        LastModified = _lastModified
    };

    return result;
}

[HttpGet("physical/{fileName}/{etag}")]
public IActionResult PhysicalFile(bool fileName, bool etag)
{
    string webRoot = _hostingEnvironment.WebRootPath;

    ResumePhysicalFileResult result = this.ResumePhysicalFile(Path.Combine(webRoot, "TestFile.txt"), "text/plain", fileName ? "TestFile.txt" : null, etag ? EntityTag : null);
    result.LastModified = _lastModified;
    return result;
}

[HttpGet("physical/{fileName}")]
public IActionResult PhysicalFile(bool fileName)
{
    string webRoot = _hostingEnvironment.WebRootPath;

    var result = new ResumePhysicalFileResult(Path.Combine(webRoot, "TestFile.txt"), "text/plain")
    {
        FileInlineName = "TestFile.txt",
        LastModified = _lastModified
    };

    return result;
}

[HttpGet("virtual/{fileName}/{etag}")]
public IActionResult VirtualFile(bool fileName, bool etag)
{
    ResumeVirtualFileResult result = this.ResumeFile("TestFile.txt", "text/plain", fileName ? "TestFile.txt" : null, etag ? EntityTag : null);
    result.LastModified = _lastModified;
    return result;
}

以上示例将为您的数据提供“Content-Disposition:attachment”。 当没有提供fileName时,数据将作为“Content-Disposition:inline”提供。 另外,它可以提供 ETagLastModified标头。

[HttpGet("virtual/{fileName}")]
public IActionResult VirtualFile(bool fileName)
{
    var result = new ResumeVirtualFileResult("TestFile.txt", "text/plain")
    {
        FileInlineName = "TestFile.txt",
        LastModified = _lastModified
    };
    return result;
}

推荐项目

基于EntityFrameworkCore和Lucene.NET实现的全文检索搜索引擎:Masuit.LuceneEFCore.SearchEngine

开源博客系统:Masuit.MyBlogs

masuit.tools's People

Contributors

chenjingchun avatar dependabot[bot] avatar gaven-f avatar gtxck avatar hjkl950217 avatar jiffei avatar jtone123 avatar ldqk avatar murdering avatar qjesus avatar wujianfeng17 avatar yzpopulation avatar zhaoyis avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

masuit.tools's Issues

将nuget包拆分

请将现在的单个nuget包拆分成多个nuget包。
Masuit.EF;Masuit.File

MultiThreadDownloader 出现 System.IO.IOException 和 System.InvalidOperationException 异常

使用的版本:nuget 2.2.7.3
.net框架: .net framework 3.7

MultiThreadDownloader 这个类抛出异常

System.InvalidOperationException: 集合已修改;可能无法执行枚举操作。
   在 System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
   在 System.Collections.Generic.List`1.Enumerator.MoveNextRare()
   在 System.Linq.Enumerable.WhereSelectListIterator`2.MoveNext()
   在 System.Linq.Enumerable.Sum(IEnumerable`1 source)
   在 Masuit.Tools.Net.MultiThreadDownloader.get_TotalBytesReceived() 位置 D:\MyCode\Masuit.Tools\Masuit.Tools\Net\MultiThreadDownloader.cs:行号 64
   在 Masuit.Tools.Net.MultiThreadDownloader.UpdateProgress() 位置 D:\MyCode\Masuit.Tools\Masuit.Tools\Net\MultiThreadDownloader.cs:行号 198
   在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   在 System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   在 System.Threading.ThreadPoolWorkQueue.Dispatch()

出现这个异常后文件能正常下载完成,异常出现在文件合并的时候


System.IO.IOException
  HResult=0x80070020
  Message=文件“C:\Users\cnwan\AppData\Local\Temp\2d54b17c-61f6-45c2-9357-0c9100f26fa2”正由另一进程使用,因此该进程无法访问此文件。
  Source=mscorlib
  StackTrace:
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.IO.File.InternalDelete(String path, Boolean checkHost)
   at Masuit.Tools.Net.MultiThreadDownloader.MergeParts()
   at Masuit.Tools.Net.MultiThreadDownloader.temp_DownloadPartCompleted(Object sender, EventArgs e)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()

这个异常出现在下载过程之中,文件不能下载完成

Linux平台下RSA加密出现问题

Windows平台下没有问题
服务版本:ASP.Net Core2.1
Masuit.Tools版本

Masuit.Tools.NET 2.2.9.1

windows平台下正常
Linux环境下:
异常信息:
An unhandled exception has occurred while executing the request.
System.PlatformNotSupportedException: 'CspParameters' requires Windows Cryptographic API (CAPI), which is not available on this platform.
at System.Security.Cryptography.RSACryptoServiceProvider..ctor(CspParameters parameters)
at Masuit.Tools.Security.RsaPem.GetRSA()
at Masuit.Tools.Security.RSA..ctor(String key)

获取真实文件类型报错

bug描述
获取真实文件类型,报错:

Unable to load one or more of the requested types.
Could not load file or assembly 'System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. 系统找不到指定的文件。
Could not load file or assembly 'System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. 系统找不到指定的文件。
Could not load file or assembly 'System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. 系统找不到指定的文件。
Could not load file or assembly 'System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. 系统找不到指定的文件。

是哪个函数出现了bug

var filepath = @"D:\git\ConsoleApp1\ConsoleApp1\ConsoleApp1\file\f\新建 Microsoft Word 文档.docx";

// 报错
 var detector = new FileInfo(filepath).DetectFiletype();

异常堆栈信息

   在 Masuit.Tools.Files.FileDetector.FileSignatureDetector.DetectFiletype(FileInfo file)
   在 ConsoleApp1.Program.<Test>d__6.MoveNext() 在 D:\git\ConsoleApp1\ConsoleApp1\ConsoleApp1\Program.cs 中: 第 640 行
   在 ConsoleApp1.Program.<Main>d__5.MoveNext() 在 D:\git\ConsoleApp1\ConsoleApp1\ConsoleApp1\Program.cs 中: 第 630 行

函数入参

期望的结果或输出
获取真实文件类型

使用环境:

  • OS: Windows10 专业版 19043.1889
  • Version VS2022
  • .NET Runtime 6.0

您的一些其他的想法

请求添加新功能:希望可以添加自定义列头名称

目前列头好像不支持特殊字符,包括一些括号之类的,因为是select new 出来的,只能符合c#变量命名规范,希望添加自定义列头。有个不成熟的想法,就是传参的时候再传个dictionary,里面的key作为select出来的变量,value对应实际要显示的列头名称。

[BUG]SevenZipCompressor解压7z压缩包报异常

bug描述
SevenZipCompressor解压7z压缩包报异常,压缩包是通过7z官方安装的图形化工具压缩的

image

是哪个函数出现了bug
SevenZipCompressor.Decompress

异常堆栈信息
image

函数入参
SevenZipCompressor.Decompress(@"E:\Download\11.7z", @"E:\Download");

期望的结果或输出
正常解压和压缩7z压缩包

使用环境:

  • OS: [WIN10]
  • Version [2.4.5.5]
  • .NET Runtime [4.8]

您的一些其他的想法

配置项可以添加更多默认行为

比如AES或者MD5的默认密钥可以在appsetting中设置之类,MD5默认密钥好像是个静态属性,但是AES的默认密钥好像是readyonly的,如果无法设置,不得每次都带上密钥么,或者在加密之上在封装一个带密钥的加解密方法?

希望可以升级到最新版本

执行 Install-Package Masuit.Tools.Core
返回 ` Install-Package : NU1605: 检测到包降级: Microsoft.Extensions.DependencyInjection 从 3.1.2 降级到 3.1.0。直接从项目引用包以选择不同版本。
Coldairarrow.Api -> Masuit.Tools.Core 2.2.8.4 -> Microsoft.EntityFrameworkCore 3.1.2 -> Microsoft.Extensions.DependencyInjection (>= 3.1.2)
Coldairarrow.Api -> Microsoft.Extensions.DependencyInjection (>= 3.1.0)
所在位置 行:1 字符: 1

  • Install-Package Masuit.Tools.Core
  •   + CategoryInfo          : NotSpecified: (:) [Install-Package], Exception
      + FullyQualifiedErrorId : NuGetCmdletUnhandledException,NuGet.PackageManagement.PowerShellCmdlets.InstallPackageCommand
    
    

Install-Package : NU1605: 检测到包降级: Microsoft.Extensions.DependencyInjection 从 3.1.2 降级到 3.1.0。直接从项目引用包以选择不同版本。
Coldairarrow.Api -> Masuit.Tools.Core 2.2.8.4 -> Microsoft.Extensions.Http 3.1.2 -> Microsoft.Extensions.Logging 3.1.2 -> Microsoft.Extensions.DependencyInjection (>= 3.1.2)
Coldairarrow.Api -> Microsoft.Extensions.DependencyInjection (>= 3.1.0)
所在位置 行:1 字符: 1

  • Install-Package Masuit.Tools.Core
  •   + CategoryInfo          : NotSpecified: (:) [Install-Package], Exception
      + FullyQualifiedErrorId : NuGetCmdletUnhandledException,NuGet.PackageManagement.PowerShellCmdlets.InstallPackageCommand
    
    

Install-Package : 程序包还原失败。正在回滚“05.Coldairarrow.Api”的程序包更改。
所在位置 行:1 字符: 1

  • Install-Package Masuit.Tools.Core
  •   + CategoryInfo          : NotSpecified: (:) [Install-Package], Exception
      + FullyQualifiedErrorId : NuGetCmdletUnhandledException,NuGet.PackageManagement.PowerShellCmdlets.InstallPackageCommand
    

`

系统环境 netcore 3.1.1

[BUG]下载超过8G的文件没有反应

温馨提示:提交后请保证回复及时,若长时间未回复的issue,将在超过30天以后作关闭处理!

bug描述
下载超过8G的文件没有反应,未找到bug原因
使用环境:

  • OS: [e.g. iOS]
  • Version [e.g. 22]
  • .NET Runtime [e.g. 6.0]

您的一些其他的想法

获取用户本机IP地址的方法

Masuit.Tools/Masuit.Tools.Core/Win32/Windows.cs
正常的一台机器可能会有多个IP地址,应该返回 string[] 类型吧?
以下写法是否更合适?

public static string[] GetLocalUsedIP()
{

		try
		{
			var ipList = new List<string>();
			var ipAddress = System.Net.Dns.GetHostAddresses(System.Net.Dns.GetHostName());
			foreach(var ip in ipAddress)
			{
				if(ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
				{
					ipList.Add(ip.ToString());
				}
			}
			return ipList.ToArray();
		}
		catch
		{
			return null;
		}
	}

.net stardand2.0支持

没什么必要直接上到.net stardand 2.1吧?2.1直接就把.net framework抛弃掉了啊。
既然.net framework版本的已经到了4.6.1,为什么不直接做成.net stardand 2.0支持呢?这样更方便一点把?

能否支持List分割扩展

能否支持IEnumerable分割扩展?
1.如有个List 有 200000 元素 要平均分成 5个list
2.如有个List 有 200000 元素 分成每个List 1000个(主要用于数据库录入数据时,一次太多影响拼接性能,需要分割)

建议代码如下,可能抄写的不是很好,也是百度的
1.
splitlist(this list src,int nsize){
for (int i=0;i<src.count;i+=nsize){
yield return src.getrange(i,math.min(nsize,src.count-i));
}
}
2.
chucklist (this list src,int nsize){
int count=src.count/nsize;
if (src.count % nsize !=0){
count++;
}
for (int i=0;i<nsize;i++){
if (i==nsize-1){
yield return src.getrange(icount,src.count-icount);
}else{
yield return src.getrange(i*count,count);
}
}
}

调整结构

  1. 抽取公共代码,抽取成一个“Abstractions”
  2. 配置github action

Cannot create Archives of type: SevenZip

.Net Core 3.1项目
操作步骤
1、Startup中ConfigureServices添加服务
//注入7z和断点续传 services.AddSevenZipCompressor().AddResumeFileResult();
2、构造函数注入
private readonly ISevenZipCompressor _sevenZipCompressor; public ScRecordsScreeningRepository(IWebHostEnvironment env, ISevenZipCompressor sevenZipCompressor) : base(dbFactory.Db(Constants.SystemDbKey), user) { _env = env; _sevenZipCompressor = sevenZipCompressor; }
3、使用7z
MemoryStream ms = _sevenZipCompressor.ZipStream(new List<string>() { @"D:\1.txt", "http://ww3.sinaimg.cn/large/87c01ec7gy1fsq6rywto2j20je0d3td0.jpg", });
结果:在使用时报'System.NotSupportedException:“Cannot create Archives of type: SevenZip”'

StrictNext不安全

函数原型

  /// <summary>
  /// 生成真正的随机数
  /// </summary>
  /// <param name="r"></param>
  /// <param name="seed"></param>
  /// <returns></returns>
  public static int StrictNext(this Random r, int seed = int.MaxValue)
  {
      return new Random((int)Stopwatch.GetTimestamp()).Next(seed);
  }

错误原因

Random((int)Stopwatch.GetTimestamp()).Next(seed)依赖于生成随机数的时间,也就是(int)Stopwatch.GetTimestamp(),如果我知道你生成的时间,那么我就可以猜测你产生的随机数。

因此这个方法是不安全的,不应该用于加密密钥和保护敏感信息的盐。

如果,您希望产生一个不可预测的安全随机数,应该使用RNGCryptoServiceProvider。system.Security.Cryptography.RNGCryptoServiceProvider的类,它采用系统当前的硬件信息、进程信息、线程信息、系统启动时间和当前精确时间作为填充因子,通过更好的算法生成高质量的随机数

https://docs.microsoft.com/zh-cn/dotnet/api/system.security.cryptography.rngcryptoserviceprovider?view=net-5.0#code-try-3

//The following sample uses the Cryptography class to simulate the roll of a dice.

using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;

class RNGCSP
{
    private static RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider();
    // Main method.
    public static void Main()
    {
        const int totalRolls = 25000;
        int[] results = new int[6];

        // Roll the dice 25000 times and display
        // the results to the console.
        for (int x = 0; x < totalRolls; x++)
        {
            byte roll = RollDice((byte)results.Length);
            results[roll - 1]++;
        }
        for (int i = 0; i < results.Length; ++i)
        {
            Console.WriteLine("{0}: {1} ({2:p1})", i + 1, results[i], (double)results[i] / (double)totalRolls);
        }
        rngCsp.Dispose();
    }

    // This method simulates a roll of the dice. The input parameter is the
    // number of sides of the dice.

    public static byte RollDice(byte numberSides)
    {
        if (numberSides <= 0)
            throw new ArgumentOutOfRangeException("numberSides");

        // Create a byte array to hold the random value.
        byte[] randomNumber = new byte[1];
        do
        {
            // Fill the array with a random value.
            rngCsp.GetBytes(randomNumber);
        }
        while (!IsFairRoll(randomNumber[0], numberSides));
        // Return the random number mod the number
        // of sides.  The possible values are zero-
        // based, so we add one.
        return (byte)((randomNumber[0] % numberSides) + 1);
    }

    private static bool IsFairRoll(byte roll, byte numSides)
    {
        // There are MaxValue / numSides full sets of numbers that can come up
        // in a single byte.  For instance, if we have a 6 sided die, there are
        // 42 full sets of 1-6 that come up.  The 43rd set is incomplete.
        int fullSetsOfValues = Byte.MaxValue / numSides;

        // If the roll is within this range of fair values, then we let it continue.
        // In the 6 sided die case, a roll between 0 and 251 is allowed.  (We use
        // < rather than <= since the = portion allows through an extra 0 value).
        // 252 through 255 would provide an extra 0, 1, 2, 3 so they are not fair
        // to use.
        return roll < numSides * fullSetsOfValues;
    }
}

另外,AES和其他加密算法的key尽量使用byte[] 有助于提高安全性

支持netstandard 2.0

能否去除System.Drawing引用及相关图片处理库,然后支持netstandard2.0?

请求添加新功能:DateTime to string

温馨提示:提交后请保证回复及时,若长时间未回复的issue,将在超过30天以后作关闭处理!

请详细描述一下你想要的功能,实现什么效果?

导出Excel,DateTime类型是否能直接转成时间字符串

image

[建议]移除方法的建议

为了保证兼容性,建议:
如果你打算移除一个方法,建议标记这个方法为废弃,而不删除。
同时,在代码里或更新记录里描述替代方法或描述为什么
然后在以后几个的版本中再完全移除。

MultiThreadDownloader类抛出System.IO.IOException

在此前的issue中已有相同问题,但我发现改问题仍未得到妥善的解决。

我不确定是否是我的代码问题引起异常,因此我创建了一个测试工程以复现问题
环境:.Net Framework 4.6.1
Nuget版本:Masuit.Tools.Net 2.2.8.9
代码如下:

static void Main(string[] args)
        {
            var mtd1 = new MultiThreadDownloader("url",  Environment.CurrentDirectory+"\\test1.png", 1);
            mtd1.Start();
            Console.ReadLine();

            var mtd2 = new MultiThreadDownloader("url", Environment.CurrentDirectory + "\\test2.png", 1);
            mtd2.Start();
            Console.ReadLine();
        }

以下是异常信息:
System.IO.IOException
HResult=0x80070020
Message=文件“C:\Users\23877\AppData\Local\Temp\c5ef965e-b056-4a54-9090-6af3b596eefc”正由另一进程使用,因此该进程无法访问此文件。
Source=mscorlib
StackTrace:
在 System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
在 System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
在 System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access)
在 Masuit.Tools.Net.MultiThreadDownloader.MergeParts()
在 Masuit.Tools.Net.MultiThreadDownloader.temp_DownloadPartCompleted(Object sender, EventArgs e)
在 Masuit.Tools.Net.PartialDownloader.b__60_0(Object state)
在 System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)
在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
在 System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
在 System.Threading.ThreadPoolWorkQueue.Dispatch()
在 System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

在Blog.core框架中通过nuget安装Masuit.Tool后,启动项目在UseEndpoint时候出现以下错误

DependencyResolutionException: An exception was thrown while invoking the constructor 'Void .ctor(Microsoft.AspNetCore.Mvc.Infrastructure.IActionDescriptorCollectionProvider, Microsoft.AspNetCore.Mvc.Routing.ActionEndpointFactory)' on type 'ControllerActionEndpointDataSource'.

ReflectionTypeLoadException: Unable to load one or more of the requested types.
Could not load file or assembly 'Masuit.Tools.Abstractions, Version=2.3.1.5, Culture=neutral, PublicKeyToken=null'. 系统找不到指定的文件。
Could not load file or assembly 'Masuit.Tools.Abstractions, Version=2.3.1.5, Culture=neutral, PublicKeyToken=null'. 系统找不到指定的文件。
Could not load file or assembly 'Masuit.Tools.Abstractions, Version=2.3.1.5, Culture=neutral, PublicKeyToken=null'. 系统找不到指定的文件。
Could not load file or assembly 'Masuit.Tools.Abstractions, Version=2.3.1.5, Culture=neutral, PublicKeyToken=null'. 系统找不到指定的文件。

请问这种应该如何定位 谢谢

图片类会自动释放

使用类似bitmap.ResizeImage的方法时,获取到的bitmap因为在方法内使用了using,会得到一个不正确的bitmap。
无法进行后续处理

一些建议

多线程下载建议添加可配置参数
如 修改UA,添加Cookie,添加Headers
添加CPU序列号,硬盘序列号

[BUG]文件下载功能,下载超过2g文件报错

温馨提示:提交后请保证回复及时,若长时间未回复的issue,将在超过30天以后作关闭处理!

bug描述
[BUG]文件下载功能,下载超过2g文件报错

解决方法
Masuit.Tools.Abstractions\Net\PartialDownloader.cs
给From 和 TO 的 Int 改成 Long

7z解压不了

SevenZipCompressor.Decompress(@"D:\Download\test.7z", @"D:\Download\");
Unhandled exception. System.InvalidOperationException: Cannot determine compressed stream type.  Supported Reader Formats: Zip, GZip, BZip2, Tar, Rar, LZip, XZ
   at SharpCompress.Readers.ReaderFactory.Open(Stream stream, ReaderOptions options)
   at Masuit.Tools.Files.SevenZipCompressor.Decompress(String compressedFile, String dir, Boolean ignoreEmptyDir) in \ldqk\Masuit.Tools\Masuit.Tools.Abstractions\Files\SevenZipCompressor.cs:line 122

依赖ASP.NET Core Mvc而无法被Blazor直接或间接引用

当Blazor项目安装包Masuit.Tools或其引用的项目(比如某些共享的模型)安装了Masuit.Tools时,编译器会报错:

严重性	代码	说明	项目	文件	行	禁止显示状态
错误	CS0234	命名空间“Microsoft.AspNetCore.Mvc.ApplicationParts”中不存在类型或命名空间名“ApplicationPartAttribute”(是否缺少程序集引用?)	DotBlog.Blazor	xxxx\obj\Debug\net5.0\xxxx.MvcApplicationPartsAssemblyInfo.cs	14	活动

(这个报错挺迷惑的,因为它报错的文件是MSbuild自动生成的)
原因是Blazor不支持使用ASP.NET Core Mvc的二进制库相关issue链接,而Masuit.Tools依赖中有ASP.NET Core Mvc。
我不知道能否不依赖于ASP.NET Core Mvc。为了防止后来者踩坑就发个issue
(说实话Blazor不能用Masuit.Tools挺难受的,毕竟很难找到和这个媲美的工具库)

socket相关的代码要引用哪个命名空间?

var tcpClient = new TcpClient(AddressFamily.InterNetwork);
Socket socket = tcpClient.ConnectSocket(IPAddress.Any,5000);
socket.SendFile("D:\test\1.txt",false,i =>
{
Console.WriteLine("已发送"+i+"%");
});

[BUG] DistinctBy去重存在问题

IEnumerable<int> arr = new List<int>() { 1, 2, 3 };

arr=arr.DistinctByEx(it => it);
arr.Count();
// 迭代无法获取到值
foreach (var i in arr)
{
    Console.Out.WriteLine("i={0}", i);
}
public static class Test
{
    public static IEnumerable<TSource> DistinctByEx<TSource, TKey>(
        this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector)
    {
        HashSet<TKey> hash = new HashSet<TKey>();
        return source.Where<TSource>((Func<TSource, bool>) (p => hash.Add(keySelector(p))));
    }

}

使用了Count之后无法遍历集合,调试发现在Count内部调用了where的条件判断:

image

导致foreach遍历时迭代器再次判断where条件时HashSet内部已经有值

image

建议替换成System.Linq内的官方写法,实测没有问题

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer)
{
    if (source is null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
    }
    if (keySelector is null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keySelector);
    }

    return DistinctByIterator(source, keySelector, comparer);
}

private static IEnumerable<TSource> DistinctByIterator<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer)
{
    using IEnumerator<TSource> enumerator = source.GetEnumerator();

    if (enumerator.MoveNext())
    {
        var set = new HashSet<TKey>(DefaultInternalSetCapacity, comparer);
        do
        {
            TSource element = enumerator.Current;
            if (set.Add(keySelector(element)))
            {
                yield return element;
            }
        }
        while (enumerator.MoveNext());
    }
}

请问

请问一下师傅,我想个人做个开源项目可以使用吗。。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.