|
| 1 | +# .NET 每周分享第 50 期 |
| 2 | + |
| 3 | +## 卷首语 |
| 4 | + |
| 5 | + |
| 6 | + |
| 7 | +最近微软在自己的招聘网站上发布了职位,要求熟练使用 `C#`,而且工作内容是负责 `Microsoft 365` 产品。那么是否意味着微软要摒弃 `C#` 拥抱 `Rust` 呢? |
| 8 | +这个话题在 [Reddt](https://www.reddit.com/r/dotnet/comments/1aezqmg/came_across_a_job_posting_on_microsoft_career/) 上也又相关的讨论,我的看法是,没有语言都有自己擅长的使用场景,这个招聘信息并没有太多的值得注意的地方。 |
| 9 | + |
| 10 | +## 行业资讯 |
| 11 | + |
| 12 | +1、[Aspire 项目会死吗](https://www.youtube.com/watch?v=2L68EldtKFo&ab_channel=NickChapsas) |
| 13 | + |
| 14 | +Reddit 上有一个用户说 `Aspire` 项目会在 5 年内死亡,视频作者对这个看法发表自己的观点: |
| 15 | + |
| 16 | +1. 每个大的公司都会停止开发一些产品和项目。 |
| 17 | +2. Aspire 仍然处于 Preview 状态,但是不停的提供新的功能 |
| 18 | + |
| 19 | +但是令人担心的是 `Aspire` 很多包都是微软维护的,这个并不能保证随时能得到更新。 |
| 20 | + |
| 21 | +2、[Dev Tunnel](https://devblogs.microsoft.com/dotnet/dev-tunnels-a-game-changer-for-mobile-developers/) |
| 22 | + |
| 23 | + |
| 24 | + |
| 25 | +Dev Tunnel 是 Visual Studio 推出的实验性功能,它主要解决的是在移动开发领域,应用程序在和后端服务通信时候调试问题,比如本地开发的时候只能通过 `localhost` 访问,而移动应用模拟器并不能直接访问开发机器。这给调试工作增加了困难。而 Dev Tunnel 可以提供一个域名可以直接访问,而且调式仍然发生在本地。 |
| 26 | + |
| 27 | +## 文章推荐 |
| 28 | + |
| 29 | +1、[HTTP Redirect 特殊处理 Authentication Header](https://ardalis.com/http-file-not-sending-auth-header/) |
| 30 | + |
| 31 | +在 `ASP.NET Core` 中,如果返回 `301 redirect` 这样的请求,那么后续的请求会将 `Authentication` 头部信息丢失,这是因为 `HTTP` 协议的规范。 |
| 32 | + |
| 33 | +2、[Polly 库中 Chaos Engineering](https://devblogs.microsoft.com/dotnet/resilience-and-chaos-engineering/) |
| 34 | + |
| 35 | + |
| 36 | + |
| 37 | +`Polly` 8.0 版本引入了 `Chaos Engineering` 功能,它能够主动引入 Fault 的机制,这样要求我们的应用程序能够主动进行 `Resilience` 相关的操作。它主要包含四种类型的策略 |
| 38 | + |
| 39 | +- Fault: 引入异常 |
| 40 | +- Outcome: 插入假的输出 |
| 41 | +- Latency: 增加延迟 |
| 42 | +- Behavior: 开启额外行为 |
| 43 | + |
| 44 | +在 `HttpClient` 中示例 |
| 45 | + |
| 46 | +1. 创建 `HttpClient` |
| 47 | + |
| 48 | +```csharp |
| 49 | +var httpClientBuilder = builder.Services.AddHttpClient<TodosClient> |
| 50 | +(client => client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com")); |
| 51 | +``` |
| 52 | + |
| 53 | +2. 引入 `ResilienceHandler` |
| 54 | + |
| 55 | +```csharp |
| 56 | +httpClientBuilder.AddResilienceHandler("chaos", (builder, context) => |
| 57 | +{ |
| 58 | + var chaosManager = context.ServiceProvider.GetRequiredService<IChaosManager>(); |
| 59 | + builder |
| 60 | + .AddChaosLatency(new ChaosLatencyStrategyOptions |
| 61 | + { |
| 62 | + EnabledGenerator = args => chaosManager.IsChaosEnabledAsync(args.Context), |
| 63 | + InjectionRateGenerator = args => chaosManager.GetInjectionRateAsync(args.Context), |
| 64 | + Latency = TimeSpan.FromSeconds(5), |
| 65 | + }) |
| 66 | + .AddChaosFault(new ChaosFaultStrategyOptions |
| 67 | + { |
| 68 | + EnabledGenerator = args => chaosManager.IsChaosEnabledAsync(args.Context), |
| 69 | + InjectionRateGenerator = args => chaosManager.GetInjectionRateAsync(args.Context), |
| 70 | + FaultGenerator = new FaultGenerator().AddException(() => new InvalidOperationException("Chaos strategy injection!")) |
| 71 | + }) |
| 72 | + .AddChaosOutcome(new ChaosOutcomeStrategyOptions<HttpResponseMessage> |
| 73 | + { |
| 74 | + EnabledGenerator = args => chaosManager.IsChaosEnabledAsync(args.Context), |
| 75 | + InjectionRateGenerator = args => chaosManager.GetInjectionRateAsync(args.Context), |
| 76 | + OutcomeGenerator = new OutcomeGenerator<HttpResponseMessage>().AddResult(() => new HttpResponseMessage(System.Net.HttpStatusCode.InternalServerError)) |
| 77 | + }); |
| 78 | +}); |
| 79 | +``` |
| 80 | + |
| 81 | +这样,当我们使用 `TodosClient` 的时候,在发起 `HTTP` 请求的时候就会有几率触发相应的 `Fault` 行为。 |
| 82 | + |
| 83 | +3、[ASP.NET Core Functional Test](https://www.youtube.com/watch?v=ASa8wXMXwrQ&ab_channel=MilanJovanovi%C4%87) |
| 84 | + |
| 85 | +不同于单元测试,功能测试(或者集成测试)是一种端到端的测试,它会尽可能的测试软件或者系统对外界暴露的接口测试,而且外部依赖保持一致。在 `ASP.NET Core` 中,我们对外暴露的是 Web API 请求,依赖可能是数据库,文件系统访问等等。我们可以借助两个库完成上述的的要求 |
| 86 | + |
| 87 | +1. Microsoft.AspNetCore.Mvc.Testing |
| 88 | + |
| 89 | +该库的作用是拷贝应用程序的依赖至测试功能,并且内容根目录内容至测试工程下,并且提供了 `WebApplicationFactory` 来启动测试服务。 |
| 90 | + |
| 91 | +```csharp |
| 92 | +public class IntegrationTestWebApplicationFactory : WebApplicationFactory<Program> |
| 93 | +{ |
| 94 | + protected override void ConfigureWebHost(IWebHostBuilder builder) |
| 95 | + { |
| 96 | + builder.ConfigureTestServices(services => |
| 97 | + { |
| 98 | + services.RemoveAll(typeof(DbContextOptions<ApplicationDbContext>)); |
| 99 | + // Add Integration test Dbcontext |
| 100 | + }); |
| 101 | + |
| 102 | + builder.UseEnvironment("Development"); |
| 103 | + } |
| 104 | +} |
| 105 | +``` |
| 106 | + |
| 107 | +2. TestContainer |
| 108 | + |
| 109 | +这个库帮助我们运行一个 Container,然后将我们的外部依赖,比如数据库给运行起来,这样就能在测试的时候直接连接该数据库。 |
| 110 | + |
| 111 | +```csharp |
| 112 | +PostgreSqlContainer _dbContainer = new PostgreSqlBuilder() |
| 113 | +.WithImage("Postgres:latest") |
| 114 | +.WithDatatabase("runtrack") |
| 115 | +.WithUserName("User") |
| 116 | +.WithPassword("password") |
| 117 | +.Build() |
| 118 | +``` |
| 119 | + |
| 120 | +这样就能获得一个 `Postgre` 数据库,在我们 `WebApplicationFactory` 构造 `WebHost` 的时候,将它作为数据库的来源。 |
| 121 | + |
| 122 | + |
| 123 | +4、[TerminalLogger](https://www.meziantou.net/enable-the-new-terminallogger-in-dotnet-8-sdk-automatically.htm) |
| 124 | +
|
| 125 | +.NET 8 Build 引入了新的日志输出框架: `TerminalLogger`, 相对于其他默认的日志输出有如下的优势 |
| 126 | + |
| 127 | +1. Warning 和 Error 安装目前框架分组 |
| 128 | +2. 通过颜色让输出更加容易阅读 |
| 129 | +3. 超链接到build 主要输出 |
| 130 | +4. 展示每个 build target 耗时 |
| 131 | +5. 编译最终结果更加清晰 |
| 132 | + |
| 133 | +- 默认日志输出 |
| 134 | + |
| 135 | + |
| 136 | +
|
| 137 | +- TerminalLogger 输出 |
| 138 | + |
| 139 | + |
| 140 | +
|
| 141 | +5、[C# 11 中检查集合空](https://www.meziantou.net/checking-if-a-collection-is-empty-in-csharp.htm) |
| 142 | +
|
| 143 | + |
| 144 | +
|
| 145 | +在 `C# 11` 中包含了模式匹配功能可以帮助写出更加优雅的代码,尤其是集合的 `Null` 或者空判断。 |
| 146 | + |
| 147 | +```csharp |
| 148 | +var collection = new Collection<object>(); |
| 149 | + |
| 150 | +if (collection is null or []) |
| 151 | +{ |
| 152 | + Console.WriteLine("collection is not null or empty"); |
| 153 | +} |
| 154 | + |
| 155 | +var array = (string[])null; |
| 156 | + |
| 157 | +if (array is null or []) |
| 158 | +{ |
| 159 | + Console.WriteLine("array is null or empty"); |
| 160 | +} |
| 161 | + |
| 162 | +array = Array.Empty<string>(); |
| 163 | + |
| 164 | +if (array is null or []) |
| 165 | +{ |
| 166 | + Console.WriteLine("array is null or empty"); |
| 167 | +} |
| 168 | +``` |
| 169 | + |
| 170 | +## 开源项目 |
| 171 | + |
| 172 | +1、[coravel](https://github.com/jamesmh/coravel) |
| 173 | +
|
| 174 | + |
| 175 | +
|
| 176 | +`Coravel` 是 `.NET` 生态中用来实现定时任务的库,不同 `Hangfire` 或者 `Quartz`,它支持异步和依赖注入编程方式,而且支持 `Fluent` 编程方式,在 ASP.NET Core 中的实例 |
| 177 | + |
| 178 | +```csharp |
| 179 | +builder.Services.AddScheduler(); |
| 180 | +app.Services.UseScheduler(scheduler => |
| 181 | +{ |
| 182 | + scheduler.Schedule(() => |
| 183 | + { |
| 184 | + Console.WriteLine("Every second."); |
| 185 | + }) |
| 186 | + .EverySecond(); |
| 187 | +}); |
| 188 | +``` |
| 189 | + |
| 190 | +2、[EF Core SQL debug 插件](https://marketplace.visualstudio.com/items?itemName=GiorgiDalakishvili.EFCoreVisualizer) |
| 191 | +
|
| 192 | + |
| 193 | +
|
| 194 | +Visual Studio 一款插件,可以展示 `EF Core` 查询语句的执行计划,这个对查询优化工作非常有帮助。 |
0 commit comments