|
| 1 | +# .NET 每周分享第 61 期 |
| 2 | + |
| 3 | +## 卷首语 |
| 4 | + |
| 5 | + |
| 6 | + |
| 7 | +11 月 12 到 14 号是一年一度的 `.NET` 开发者大会,届时 `.NET 9` 将会正式发布。到时候会有一系列的现场直播的环节,而且现在向所有开发者征求演讲。 |
| 8 | + |
| 9 | +## 行业资讯 |
| 10 | + |
| 11 | +1、[Mono 装交给 Wine 项目](https://www.omgubuntu.co.uk/2024/08/microsoft-mono-project-to-wine) |
| 12 | + |
| 13 | + |
| 14 | + |
| 15 | +最近微软将 [mono](https://github.com/mono/mono) 项目交给了 `WineHQ` 组织,也就是在 `Linux` 中著名的 `wine` 项目的组织。注意这个是支持 `.NET Framework` 中的 mono 项目,而不是 [dotnet/runtime](https://github.com/dotnet/runtime/tree/main/src/mono) 中的 mono。 |
| 16 | + |
| 17 | +2、[JetBrain .NET Day](https://blog.jetbrains.com/dotnet/2024/09/02/dotnet-days-online-2024/) |
| 18 | + |
| 19 | + |
| 20 | + |
| 21 | +2024 年 Jetbrains `.NET Day` 会在 9 月 25, 26 号举办,目前的演讲主题已经确定: |
| 22 | + |
| 23 | +25 号 |
| 24 | + |
| 25 | +– Not Your Father’s ReSharper by Andrew Karpov |
| 26 | + |
| 27 | +– Crafting Blazor Components With Precision and Assurance by Mariekie Coetzee |
| 28 | + |
| 29 | +– A Homage to the Good Old MVC by Alexander Zeitler |
| 30 | + |
| 31 | +– Overcoming Broken Window Syndrome: Code Verification Techniques for .NET Developers by Gael Fraiteur |
| 32 | + |
| 33 | +– Enhancing ASP.NET Core Razor Pages With HTMX: A Simplicity-First Approach by Chris Woodruff |
| 34 | + |
| 35 | +– No More SQLite – How to Write Tests With EF Core Using Testcontainers by Daniel Ward |
| 36 | + |
| 37 | +26 号 |
| 38 | + |
| 39 | +– Building Functional DSLs for Life-Saving Applications by Roman Provazník |
| 40 | + |
| 41 | +– Pushing ‘await’ to the Limits by Konstantin Saltuk |
| 42 | + |
| 43 | +– Functional Programming Made Easy in C# With Language Extensions by Stefano Tempesta |
| 44 | + |
| 45 | +– Into the Rabbit Hole of Blazor Wasm Hot Reload by Andrii Rublov |
| 46 | + |
| 47 | +– Orchestration vs. Choreography: The Good, the Bad, and the Trade-Offs by Laila Bougria |
| 48 | + |
| 49 | +– Contract Testing Made Easy: Mastering Pact for Microservices in C# by Irina Scurtu |
| 50 | + |
| 51 | +– Composing Distributed Applications With .NET Aspire by Cecil Phillip |
| 52 | + |
| 53 | +## 文章推荐 |
| 54 | + |
| 55 | +1、[.NET 9 中移除 BinaryFormatter](https://devblogs.microsoft.com/dotnet/binaryformatter-removed-from-dotnet-9/) |
| 56 | + |
| 57 | + |
| 58 | + |
| 59 | +由于安全方面的要求,从一个二进制文件反序列化成个对象是危险的,因为攻击者可以替换文件的内容,从而在反序列化的时候,注入攻击代码。所以在 `.NET 9` 中移除了 `BinaryFormatter` 的实现,而 `.NET Framework` 不受影响。 |
| 60 | +解决办法有两个 |
| 61 | + |
| 62 | +1. 使用 [System.Runtime.Serialization.Formatters](https://learn.microsoft.com/dotnet/standard/serialization/binaryformatter-migration-guide/compatibility-package) 包,其包含了原本的实现 |
| 63 | +2. 使用其他反序列化工具 |
| 64 | + |
| 65 | +2、[Visual Studio 中的新特性](https://devblogs.microsoft.com/visualstudio/new-ide-features-in-visual-studio-v17-11/) |
| 66 | + |
| 67 | + |
| 68 | + |
| 69 | +在 `Visual Studio 2022 v17.11` 增加了如下的特性 |
| 70 | + |
| 71 | +1. 在 `Search` 功能可以限制查询的范围 |
| 72 | +2. 和 `VS Code` 同意快捷键: `Ctrl+/` 注释,`Ctrl+Shift+P` 打开控制面板 |
| 73 | +3. `*.vsconfig` 文件支持 |
| 74 | +4. Authentication 功能加强 |
| 75 | +5. Teams 开发套件模板 |
| 76 | + |
| 77 | +3、[Aspire 介绍](https://www.youtube.com/watch?v=-jrUiq21gS4) |
| 78 | + |
| 79 | +`Scott Hunter` 在 NDC 会议长介绍了 `Aspire` 。主要包含以下几点 |
| 80 | + |
| 81 | +- 弹性: 最早在 ASP.NET Core 3.0 中引入,Aspire 增强了应用程序的弹性,使其能够优雅地处理云相关的故障(如间歇性失败)。 |
| 82 | +- 可观测性: Aspire 内置对 OpenTelemetry 的支持,提供了应用性能和错误的全面洞察。 |
| 83 | +- 可扩展性: Aspire 轻松管理本地和云端的资源(如容器),简化了部署过程。 |
| 84 | + |
| 85 | +在演示与实际应用, Scott 展示了一个中间件,它随机导致应用程序失败,以展示 Aspire 的弹性功能如何确保应用程序继续运行。 |
| 86 | +Aspire 引入了服务发现,自动管理云部署中的 IP 地址和端口。Aspire 提供了一个仪表盘,用于本地和云端监控应用健康状况,并跟踪错误和性能指标。Aspire 支持部署到多个云平台,包括 Azure 和 AWS。它抽象了跨云平台管理基础设施的复杂性。 |
| 87 | + |
| 88 | +4、[使用 CollectionsMarshal 访问 Dictionary](https://blog.ndepend.com/faster-dictionary-in-c/) |
| 89 | + |
| 90 | +使用 `CollectionMarshal` 可以提高访问集合类型的性能,比如以 `Dictionary` 为例 |
| 91 | + |
| 92 | +- GetValueRefOrAddDefault |
| 93 | + |
| 94 | +如果字典至类型为 `Struct` 类型, 那么可以通过这个方法判断是否存在,如果不存在,可以创建一个这个 key 指向的值的引用 |
| 95 | + |
| 96 | +```csharp |
| 97 | +static readonly Dictionary<Guid, string> s_Dico1 = new(); |
| 98 | +[Benchmark] |
| 99 | +public void CreateValIfKeyNotInDico() { |
| 100 | + var key = Guid.NewGuid(); |
| 101 | + if (!s_Dico1.ContainsKey(key)) { |
| 102 | + // Create the object to be used as key's value |
| 103 | + // because the key is not present in the dictionary |
| 104 | + string val = key.ToString(); |
| 105 | + s_Dico1.Add(key, val); |
| 106 | + } |
| 107 | +} |
| 108 | + |
| 109 | +static readonly Dictionary<Guid, string> s_Dico2 = new(); |
| 110 | +[Benchmark] |
| 111 | +public void CreateValIfKeyNotInDico_Faster() { |
| 112 | + var key = Guid.NewGuid(); |
| 113 | + ref string pointerToValLocation = |
| 114 | + ref CollectionsMarshal.GetValueRefOrAddDefault(s_Dico2, key, |
| 115 | + out bool exists); |
| 116 | + if (!exists) { |
| 117 | + // Create the object to be used as key's value |
| 118 | + // because the key is not present in the dictionary |
| 119 | + var val = key.ToString(); |
| 120 | + pointerToValLocation = val; |
| 121 | + } |
| 122 | +} |
| 123 | +``` |
| 124 | + |
| 125 | +| Method | Mean | Error | StdDev | Gen0 | Gen1 | Allocated | |
| 126 | +|------------------------------- |---------:|-----------:|---------:|-------:|-------:|----------:| |
| 127 | +| CreateValIfKeyNotInDico | 527.0 ns | 2,118.9 ns | 116.1 ns | 0.0095 | 0.0086 | 96 B | |
| 128 | +| CreateValIfKeyNotInDico_Faster | 452.9 ns | 1,859.4 ns | 101.9 ns | 0.0100 | 0.0095 | 96 B | |
| 129 | + |
| 130 | +- GetValueRefOrNullRef |
| 131 | + |
| 132 | +第二种方式是获取值的一个引用,方便修改和读取 |
| 133 | + |
| 134 | +```csharp |
| 135 | +static readonly Guid s_Guid = Guid.NewGuid(); |
| 136 | + |
| 137 | +static readonly Dictionary<Guid, int> s_Dico1 = new() { { s_Guid, 0 } }; |
| 138 | + |
| 139 | +[Benchmark] |
| 140 | +public void StructValueInDico() { |
| 141 | + for (int i = 0; i < 1000; i++) { |
| 142 | + s_Dico1[s_Guid] += 1; |
| 143 | + } |
| 144 | +} |
| 145 | + |
| 146 | +static readonly Dictionary<Guid, int> s_Dico2 = new() { { s_Guid, 0 } }; |
| 147 | +[Benchmark] |
| 148 | +public void StructValueInDico_Faster() { |
| 149 | + for (int i = 0; i < 1000; i++) { |
| 150 | + ref int pointerToValLocation = |
| 151 | + ref CollectionsMarshal.GetValueRefOrNullRef(s_Dico2, s_Guid); |
| 152 | + // if(!Unsafe.IsNullRef(pointerToValLocation)) |
| 153 | + pointerToValLocation++; |
| 154 | + } |
| 155 | +} |
| 156 | +``` |
| 157 | + |
| 158 | +| Method | Mean | Error | StdDev | Allocated | |
| 159 | +|------------------------- |---------:|---------:|----------:|----------:| |
| 160 | +| StructValueInDico | 7.463 us | 2.470 us | 0.1354 us | - | |
| 161 | +| StructValueInDico_Faster | 3.069 us | 1.018 us | 0.0558 us | - | |
| 162 | + |
| 163 | +5、[.NET 开源贡献者名单](https://dotnet.microsoft.com/en-us/thanks) |
| 164 | + |
| 165 | + |
| 166 | + |
| 167 | +随着 `.NET` 开源,越来越多的开发者参与进来,并且做出贡献,该网页可以查看每个 `.NET` 开源依赖所有的开发者。 |
| 168 | + |
| 169 | +## 开源项目 |
| 170 | + |
| 171 | +1、[Grok.net](https://github.com/Marusyk/grok.net) |
| 172 | + |
| 173 | + |
| 174 | + |
| 175 | +`Gork.net` 借助正则表达式,可以帮助我们很方便的在一些非结构化数据中找到结构化数据。 |
| 176 | + |
| 177 | +```csharp |
| 178 | +Grok grok = new Grok("%{MONTHDAY:month}-%{MONTHDAY:day}-%{MONTHDAY:year} %{TIME:timestamp};%{WORD:id};%{LOGLEVEL:loglevel};%{WORD:func};%{GREEDYDATA:msg}"); |
| 179 | + |
| 180 | +string logs = @"06-21-19 21:00:13:589241;15;INFO;main;DECODED: 775233900043 DECODED BY: 18500738 DISTANCE: 1.5165 |
| 181 | + 06-22-19 22:00:13:589265;156;WARN;main;DECODED: 775233900043 EMPTY DISTANCE: --------"; |
| 182 | + |
| 183 | +var grokResult = grok.Parse(logs); |
| 184 | +foreach (var item in grokResult) |
| 185 | +{ |
| 186 | + Console.WriteLine($"{item.Key} : {item.Value}"); |
| 187 | +} |
| 188 | +``` |
| 189 | + |
| 190 | + |
| 191 | + |
| 192 | +2、[Sisk](https://github.com/sisk-http/core) |
| 193 | + |
| 194 | + |
| 195 | + |
| 196 | +`Sisk` 是除了 `ASP.NET Core` 之外,又一个网络框架。和 `ASP.NET Core` 相比,它更加简单,轻便。不到 10 行代码就可以启动一个 Http 服务 |
| 197 | + |
| 198 | +```csharp |
| 199 | +using Sisk.Core.Http; |
| 200 | + |
| 201 | +using var app = HttpServer.CreateBuilder(5555).Build(); |
| 202 | +app.Router.MapGet("/", request => { |
| 203 | + return new HttpResponse("Hello World!"); |
| 204 | +}); |
| 205 | + |
| 206 | +await app.StartAsync(); |
| 207 | +``` |
| 208 | + |
| 209 | +3、[EntityFramework.Exceptions](https://github.com/jdevillard/JmesPath.Net) |
| 210 | + |
| 211 | + |
| 212 | + |
| 213 | +在 `Entity Framework Core` 中,在插入数据的时候如果违反了数据库的条件,就会抛出 `DbUpdateException` 异常,但是这个有一个问题是,需要从详细的异常信息中找到错误的原因。而且如果换了不同的数据库,有需要重新提取错误信息。`EntityFramework.Exceptions` 库可以帮助我们统一数据库异常信息。 |
| 214 | + |
| 215 | +```csharp |
| 216 | +public class EFCoreContext : DbContext |
| 217 | +{ |
| 218 | + public EFCoreContext(DbContextOptions<EFCoreContext> options) : base(options) |
| 219 | + { |
| 220 | + } |
| 221 | + |
| 222 | + public DbSet<Product> Products { get; set; } |
| 223 | + |
| 224 | + protected override void OnModelCreating(ModelBuilder modelBuilder) |
| 225 | + { |
| 226 | + modelBuilder.Entity<Product>().HasKey(p => p.Id); |
| 227 | + modelBuilder.Entity<Product>().HasIndex(p => p.Name).IsUnique(); |
| 228 | + } |
| 229 | + |
| 230 | + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) |
| 231 | + { |
| 232 | + optionsBuilder.UseExceptionProcessor(); |
| 233 | + } |
| 234 | +} |
| 235 | + |
| 236 | +using var context = new EFCoreContext(options); |
| 237 | +context.Products.Add(new Product { Name = "Product 1" , Price = 10}); |
| 238 | +context.Products.Add(new Product { Name = "Product 1" , Price = 20}); |
| 239 | + |
| 240 | +try |
| 241 | +{ |
| 242 | + context.SaveChanges(); |
| 243 | +} |
| 244 | +catch (UniqueConstraintException e) |
| 245 | +{ |
| 246 | + Console.WriteLine($"Unique constraint {e.ConstraintName} violated. Duplicate value for {e.ConstraintProperties[0]}"); |
| 247 | +} |
| 248 | +``` |
| 249 | + |
| 250 | +这里的 `UniqueConstraintException` 包含了异常的详细信息。除此之外,还支持如下的异常 |
| 251 | + |
| 252 | +- CannotInsertNullException |
| 253 | +- MaxLengthExceededException |
| 254 | +- NumericOverflowException |
| 255 | +- ReferenceConstraintException |
0 commit comments