Microsoft Build 大会 .NET 内容汇总。
之前我们分享过 .NET
官方文档中现在提供了 GitHub
源码查看链接,这样大大方便了我们了解更多实现的细节,这篇文章介绍了 .NET
组是如何开发这个功能的。
功能 | .NET Aspire | Docker |
---|---|---|
编排 | 提供用于管理应用程序组成、服务发现和本地开发环境连接字符串的抽象。这简化了复杂分布式应用程序的设置。 | 主要专注于容器运行时和管理。复杂应用程序的编排通常涉及额外的工具,如Docker Compose或Kubernetes。 |
云原生组件 | 提供标准化与流行云服务(Redis、PostgreSQL、Azure Service Bus等)集成的NuGet包(组件)。这简化了设置和配置过程,包括健康检查、遥测和服务发现。 | 虽然Docker可以运行任何容器化服务,但与特定云服务的集成通常通过应用程序代码中的配置或通过单独的工具来处理。 |
工具和模板 | 提供适用于Visual Studio和dotnet CLI的项目模板和工具,专为云原生应用程序开发而设计。这包括默认配置和服务的样板代码,如健康检查、遥测和服务发现,加速开发。 | 主要是命令行工具(docker)和相关工具(Docker Compose等)。与IDE的集成可能需要额外的扩展或插件。 |
目标用户 | 主要是构建云原生应用程序的.NET开发人员。它假设了解.NET概念,并提供更高层次的抽象,以简化云原生开发体验。 | 从事容器化应用程序的开发人员、DevOps工程师和系统管理员。Docker需要对容器及其管理有更深入的了解。 |
生态系统 | 与.NET生态系统和Microsoft Azure云服务紧密集成。 | 一个被广泛采用的行业标准,拥有支持容器化的庞大工具、平台和服务生态系统。 |
在 ASP.NET Core
中每个请求的处理流程如下:
- 找到相应的
Route
定义 - 链式处理每个中间件
- 找到对应的
Endpoint
并且处理
在 ASP.NET Core 8
中引入的 Short Circuit
。顾名思义,我们定义好一些请求可以不用经过各种 Middleware
而直接处理并且返回,比如像一些静态文件请求,我们可以直接返回结果,通常是为了下述考虑:
- 性能优化
- 资源有效性
- 简化路由
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseMiddleware<CustomerMiddleware>();
app.MapGet("/shortcircuit", () => "Hello From Short Circuit").ShortCircuit();
app.Run();
class CustomerMiddleware
{
private readonly RequestDelegate _next;
public CustomerMiddleware(RequestDelegate next)
{
_next = next;
}
public Task Invoke(HttpContext context)
{
System.Console.WriteLine("Customer Middleware");
return _next(context);
}
}
当我们访问 shortcircuit
路径的时候,CustomerMiddleware
并不会执行。除此之外,还有 MapShortCircuit
拓展方法
app.MapShortCircuit(500, "robots.txt", "favicon.ico");
这里定义了所有访问 robots.txt
和 favicon.ico
路径的请求都会返回 500.
- StringBuilder Vs String concatenation
- 如果只有几个字符串,使用字符串拼接
- 如果有很多字符串,使用
StringBuilder
- EndsWith(string) Vs EndsWith(char)
EndsWith(char)
性能比较好
- IsNullOrEmpty Vs IsNullOrWHitespace Vs IsNullOrEmpty + Trim
StringIsNullOrWhitespace
性能比StringIsNullOrEmpty
慢- 如果数据是来在外部,使用
StringIsNullOrWhitespace
- 如果认为
\n \n\t
是合法字符,使用StringIsNullOrEmpty
- ToUpper Vs ToUpperInvariant 和 ToLower Vs ToLowerInvariant
ToUpper
方法通常比ToLower
慢Invariant
方法通常比non-invariant
慢
- OrdinalIgnoreCase Vs InvariantCultureIgnoreCase
InvariantCultureIgnoreCase
比OrdinalIgnoreCase
慢, 因为InvariantCultureIgnoreCase
检查字符串的意义
var s1 = = "Straße"; // German for "street"
var s2 ="STRASSE";
string.Equals(s1, s2, StringComparison.InvariantCultureIgnoreCase); //True
string.Equals(s1, s2, StringComparison.OrdinalIgnoreCase); //False
- Newtonsoft Vs System.Text.Json
System.Text.Json
在时间和内存消耗上比 Newtonsoft
有更好的表现
对于单元测试,通常的做法是将一些外部依赖抽象成接口,这样可以通过接口不同的实现来进行单元测试。但是 C#
除了接口,还可以通过其他方式完成单元测试:
- Virtual
C# 中 Virtual
关键字用来表示该属性或者方法可以在子类中进行重载,所以在单元测试中可以构造新的子类来重写这个方法。
public class NumbersRepository
{
private readonly int[] _allNumbers;
public NumbersRepository(){
_allNumbers = Enumerable.Range(0, 100).ToArray();
}
public virtual IEnumerable<int> GetNumbers() => Random.Shared.GetItems(_allNumbers, 50);
}
public class NumbersSearchService
{
private readonly NumbersRepository _repository;
public NumbersSearchService(NumbersRepository repository) {
_repository = repository;
}
public bool Contains(int number){
var numbers = GetNumbers();
return numbers.Contains(number);
}
public IEnumerable<int> GetNumbers() => _repository.GetNumbers();
}
// 单元测试
internal class StubNumberRepo : NumbersRepository
{
private IEnumerable<int> _numbers;
public void SetNumbers(params int[] numbers) => _numbers = numbers;
public override IEnumerable<int> GetNumbers() => _numbers;
}
[TestMethod]
public void Should_WorkWithStubRepo() {
// Arrange
var repository = new StubNumberRepo();
repository.SetNumbers(1, 2, 3);
var service = new NumbersSearchService(repository);
// Act
var result = service.Contains(3);
// Assert
Assert.AreEqual(result, true);
}
- New
C# 中 new
关键字也能隐藏父类的方法和属性,这样我们只需要测试其他内容就可以完成单元测试
public class NumbersSearchService {
private readonly NumbersRepository _repository;
public NumbersSearchService(NumbersRepository repository) {
_repository = repository;
}
public bool Contains(int number) {
var numbers = GetNumbers();
return numbers.Contains(number);
}
public IEnumerable<int> GetNumbers() => _repository.GetNumbers();
}
internal class StubNumberSearch : NumbersSearchService {
private IEnumerable<int> _numbers;
private bool _useStubNumbers;
public void SetNumbers(params int[] numbers) {
_numbers = numbers.ToArray();
_useStubNumbers = true;
}
public new IEnumerable<int> GetNumbers() => _useStubNumbers ?
_numbers:base.GetNumbers();
}
在单元你测试中只需要测试 StubNumberSearch
即可。
- Moq
Moq
是 .NET
社区广泛使用的单元测试框架,使用 Moq
可以很方便地构造测试子类
public class NumbersRepository {
private readonly int[] _allNumbers;
public NumbersRepository() {
_allNumbers = Enumerable.Range(0, 100).ToArray();
}
public virtual IEnumerable<int> GetNumbers() => Random.Shared.GetItems(
_allNumbers, 50);
}
[TestMethod]
public void Should_WorkWithMockRepo() {
// Arrange
var repository = new Moq.Mock<NumbersRepository>();
repository.Setup(_ => _.GetNumbers()).Returns(new int[]{1, 2, 3});
var service = new NumbersSearchService(repository.Object);
// Act
var result = service.Contains(3);
// Assert
Assert.AreEqual(result, true);
}
今年 Build
大会,Scott Hanselman
和 Stephen Toub
这对老搭档又来整活,他们通过对 Humanizer 这个库的优化,来提升代码性能。
- Span
- value.Substring(0, length - truncationString.Length) + truncationString
+ String.Concat(value.AspSpan(0, length - truncationString.Length), truncationString.AsSpan())
- ReadOnlySpan Parameter
- public static string Transform(this string input, params IStringTransformer[] transformers)
+ public static string Transform(this string input params ReadonlySpan<IStringTransformer> transformers)
- Regex
- private static readonly Regex ValidRomanNumeral = new Regex("^(?i:(?=[MDCLXVI])((M{0,3})((C[DM])|(D?C{0,3}))?((X[LC])|(L?XX{0,2})|L)?((I[VX])|(V?(II{0,2}))|V)?))$", RegexOptionsUtil.Compiled);
+ [GeneratedRegex("^(?i:(?=[MDCLXVI])((M{0,3})((C[DM])|(D?C{0,3}))?((X[LC])|(L?XX{0,2})|L)?((I[VX])|(V?(II{0,2}))|V)?))$", RegexOptions.IgnroeCase)]
+ private static partial Regex ValidRomanNumeral();
- Switch Pattern
- private static readonly IDictionary<string, int> RomanNumerals =
- new Dictionary<string, int>(NumberOfRomanNumeralMaps){
- {"M", 1000}, {"D", 500}, {"C", 100}, {"L", 50}, {"X", 10}, , {"V", 5}, {"I", 1}};
+ private static int GetValue(char c) =>(c & 0x20) switch {
+ 'M' => 1000, 'D' => 500, 'C' => 100, 'L' => 50,
+ 'X' => 10, 'V' => 5, 'I' => 1 };
如果说你的应用程序是 .NET Framework
+ SQL Server
的架构,并且部署在 On premise
机器上,那么你的应用程序就像囚犯一样:
- 受限于
on-premise
无法动态扩展 - 被 Windows 和
SQL Server
授权费用牵制 - 受困于服务器管理
- 受整体架构的限制
使用 AWS
和相关工具可以解决上述问题
阶段一
- SQL Server 迁移到 SQL Server on EC2
- 应用程序迁移到 Virtaul Machine (Amazon EC2) 或者 Windows Container (Amazon EKS)
阶段二
- 迁移
.NET Framework
到.NET Core
- 迁移
SQL Server
到开源数据库,比如Amazon Aurora
,PostgreSQL
或者MySQL
阶段三
- 将服务进行微服务化,使用
Linux Container
- 将数据库中特定服务中数据拆分,比如
Amazon DynamoDB
阶段四
- 云原生重构,比如
Serverless
,Event-driven
这是一篇令人难过的文章,作者是一名 .NET
开源库的维护者。但是最近他被诊断为一种叫做 失智
的疾病,该疾病导致作者无法再及时更新这些开源库。
微软每年都会发布新的 .NET
版本,每个版本都包含新的功能,或者性能的提升,亦或者包含一些 breaking change。如果这个开源库只依赖 .NET Standard
, 新发布的 .NET
并不会影响他们,以为只依赖 BCL
。如果依赖特定的 .NET
,但是使用者想再新的 .NET
版本中使用这个库,但是作者并没有及时更新这个库,那该怎么办呢?
- 首先下载这个库的源码到本地,通常
NuGet
页面会有指向源码的链接 - 修改项目的
TargetFramework
属性到最新的版本 - 更新项目的依赖到最新版本
- 编译代码
- 运行单元测试
- 更新
NuGet
文件信息 - 创建本地
.nupkg
文件 - 将其加入到
NuGet
源
最近在 Build
大会中,Aspire
这个项目已经处于 GA
状态,也就是可以可以尝试使用。
ExcelMapper
库可以帮助我们非常方便的从 Excel 中读取数据,并且转换成相应的 POCO 对象集合。比如 Excel 中的数据如下
首先定义实体
class Product
{
public string Name { get; set; }
[Column("Number")]
public int NumberInStock { get; set;}
public decimal Price { get; set; }
}
使用 Fetch
方法获取对象集合
using Ganss.Excel;
var products = new ExcelMapper("Product.xlsx").Fetch<Product>();
System.Console.WriteLine(products.Count());
JMESPath 是一种针对 JSON 的查询语言。您可以从 JSON 文档中提取和转换元素。JMESPath.Net
是一个 C#
实现的库,可以帮助我们在 C#
中使用 JMESPath
查询语言。
4、OFGB
Window 11 在最新的一次更新中,包含了大量的广告,比如文件管理系统,开始菜单等等。这个开源工具可以一键关闭这些功能。