|
| 1 | +# .NET 每周分享第 38 期 |
| 2 | + |
| 3 | +## 卷首语 |
| 4 | + |
| 5 | + |
| 6 | + |
| 7 | +我们的大部分开发工作都是在 `Visual Studio` 中完成,当我们在自己的分支上完成工作,就需要登录到 `GitHUB` 或者 `Azure DevOps` 上创建 PR。在新的 `Visual Studio` 中,我们就可直接在 `Visual Studio` 中直接创建 PR。 基本流程是这样的 |
| 8 | +1. 创建新的分支 |
| 9 | +2. 提交和推送分支 |
| 10 | +3. 创建 PR |
| 11 | +4. 修改并更新 PR |
| 12 | + |
| 13 | +## 行业资讯 |
| 14 | + |
| 15 | +1、[qodana](https://blog.jetbrains.com/dotnet/2023/06/29/elevating-csharp-code-quality-with-qodana-a-journey-towards-perfection/) |
| 16 | + |
| 17 | + |
| 18 | + |
| 19 | +JetBrains 为 `.NET` 应用程序的代码质量推出了一个工具叫做 `qodana for .NET`,它既可以集成到 `CI` 工具中,也可以在本地执行,并且生成网页版本的质量报告。 |
| 20 | + |
| 21 | +## 文章推荐 |
| 22 | + |
| 23 | +1、[C# 中的 GUID](https://code-maze.com/csharp-guid-class/) |
| 24 | + |
| 25 | + |
| 26 | + |
| 27 | +`GUID` 是一个 128 bit 组成的值,用来减少冲突。在 `C#` 中 GUID 有以下几个特征 |
| 28 | + |
| 29 | +1. 它是一个值类型,所以默认值为 `00000000-0000-0000-0000-000000000000`, 也等于 `Guid.Empty` |
| 30 | +2. 随机创建 GUID 的方式是 `GUID.Empty` |
| 31 | +3. 它的 `ToString` 方式有多种: |
| 32 | + |
| 33 | +```C# |
| 34 | +var inputGuid = Guid.NewGuid(); |
| 35 | +Console.WriteLine(inputGuid.ToString()); // 081bdc64-b126-4ac4-a1a5-96d41c8622fc |
| 36 | +Console.WriteLine(inputGuid.ToString("D")); //081bdc64-b126-4ac4-a1a5-96d41c8622fc |
| 37 | +Console.WriteLine(inputGuid.ToString("N")); // 081bdc64b1264ac4a1a596d41c8622fc |
| 38 | +Console.WriteLine(inputGuid.ToString("B")); // {081bdc64-b126-4ac4-a1a5-96d41c8622fc} |
| 39 | +Console.WriteLine(inputGuid.ToString("P")); //(081bdc64-b126-4ac4-a1a5-96d41c8622fc) |
| 40 | +``` |
| 41 | + |
| 42 | +2、[C# 编码过程中的建议](https://scottsauber.com/2023/06/16/iowa-code-camp-2023-10-things-i-do-on-every-net-app/) |
| 43 | + |
| 44 | +Scott Sauber 是 `Microsoft MVP`, 他分享了他关于 `.NET` 应用程序的原则 |
| 45 | + |
| 46 | +1. 在 `ASP.NET Core` 中使用功能的方式组织文件夹 |
| 47 | +2. 将 `Warning` 当作 `Error`, 在 `csproj` 文件中这么配置 |
| 48 | + |
| 49 | +```xml |
| 50 | +<propertyGroup> |
| 51 | + <TreatWarningsAsErrors>true</TreatWarningsAsErrors> |
| 52 | +</propertyGroup> |
| 53 | +``` |
| 54 | +3. 正确使用日志 |
| 55 | +4. 使用`FallbackPolicy` 全局使用 `Authorize` 标注 |
| 56 | +5. 删除 `Asp.NET Core` 的 Server Header |
| 57 | +6. 不要使用 `IOption` 作为依赖注入的选项,使用具体的值 |
| 58 | +7. `Endpoint` 要版本化 |
| 59 | +8. 结构化方法,比如正确的结果在方法的最后,使用 `return` 而不是 `if-else` |
| 60 | +9. 注意 `HTTP` 安全方面的控制 |
| 61 | +10. 构建一次到处部署 |
| 62 | + |
| 63 | +3、[Visaul Studio NewFile](https://www.youtube.com/watch?v=3dpFeNV1smU&ab_channel=IAmTimCorey) |
| 64 | + |
| 65 | + |
| 66 | + |
| 67 | +在过去的 `Visual Studio` 中,在添加新的文件的时候,通常会弹出上面的窗口,它会展示所有的已经安装的模板。在新版的 `Visual Studio` 中,我们可以使用更加简单的对话框,在这里就直接直接输入文件的名字,Visual Studio 会根据模板创建相应的文件,而且也支持多个文件,文件夹的创建。 |
| 68 | + |
| 69 | +4、[ASP.NET Core WebSocket 客户端服务端通信](https://medium.com/bina-nusantara-it-division/implementing-websocket-client-and-server-on-asp-net-core-6-0-c-4fbda11dbceb) |
| 70 | + |
| 71 | + |
| 72 | + |
| 73 | +`ASP.NET Core` 支持 `WebSocket` 开发,这篇文章展示了如果使用 `WebSocket` 实现一个简单的聊天室功能。 |
| 74 | + |
| 75 | +5、[StringBuilder 中 Replace 功能](https://khalidabuhakmeh.com/using-stringbuilder-to-replace-values) |
| 76 | + |
| 77 | + |
| 78 | + |
| 79 | +我们都知道在 `C#` 中, `StringBuilder` 在字符串处理方面比 `string` 操作更加高效,通常是发生在字符串拼接的使用场景中。但是在 `Replace` 的操作中,`StringBuilder` 的效率也是比 `String` 效率高。 |
| 80 | + |
| 81 | +```csharp |
| 82 | +Dictionary<string, string> _opMapper = new() |
| 83 | +{ |
| 84 | + {"×", "*"}, |
| 85 | + {"÷", "/"}, |
| 86 | + {"SIN", "Sin"}, |
| 87 | + {"COS", "Cos"}, |
| 88 | +}; |
| 89 | + |
| 90 | +var retString = new StringBuilder(inputString); |
| 91 | +foreach (var key in _opMapper.Keys) |
| 92 | +{ |
| 93 | + retString.Replace(key, _opMapper[key]); |
| 94 | +} |
| 95 | +return retString.ToString(); |
| 96 | +``` |
| 97 | + |
| 98 | +6、[Nuget 包中添加 ReadME](https://khalidabuhakmeh.com/adding-a-readme-to-nuget-package-landing-pages) |
| 99 | + |
| 100 | + |
| 101 | + |
| 102 | +当我们在浏览 `Nuget` 包的时候,通常希望有一个精美的 `README` 文件来描述这个包,并且包含丰富的文档链接。那么在 `csproj` 文件中包含整个就可以了。 |
| 103 | + |
| 104 | +```xml |
| 105 | +<PropertyGroup> |
| 106 | + <PackageIcon>icon.png</PackageIcon> |
| 107 | + <RepositoryUrl>https://github.com/khalidabuhakmeh/Htmx.Net</RepositoryUrl> |
| 108 | + <PackageLicenseExpression>MIT</PackageLicenseExpression> |
| 109 | + <!-- IMPORTANT Do not forget this --> |
| 110 | + <PackageReadmeFile>README.md</PackageReadmeFile> |
| 111 | + <Description> |
| 112 | + Adds ASP.NET Core tag helpers to make generating urls for Htmx (https://htmx.org) easier. Mimics the ASP.NET Core url tag helpers. |
| 113 | + </Description> |
| 114 | +</PropertyGroup> |
| 115 | +``` |
| 116 | + |
| 117 | +7、[关于 Logging 的八个原则](https://www.youtube.com/watch?v=NlBjVJPkT6M&t=755s&ab_channel=NDCConferences) |
| 118 | + |
| 119 | +关于 `Logging`, `Nick` 在 NDC 大会上分享他的原则: |
| 120 | + |
| 121 | +1. 日志中的 `message` 参数其实是模板 |
| 122 | +2. 使用结构化日志并且给参数合适的名字 |
| 123 | +3. 不在模板中使用字符串拼接的方式 |
| 124 | +4. 对于 `inactive` 的日志级别,避免装箱操作 |
| 125 | +5. 考虑使用 `Source Generated` 的日志 |
| 126 | +6. 不要尝试比工具更加聪明 |
| 127 | +7. 考虑使用 `Warning` 作为默认级别 |
| 128 | +8. 日志只记录讲好一个故事的必要信息 |
| 129 | + |
| 130 | +## 开源项目 |
| 131 | + |
| 132 | +1、[html-agility-pack](https://github.com/zzzprojects/html-agility-pack/) |
| 133 | + |
| 134 | + |
| 135 | + |
| 136 | +当我们在使用 `C#` 对网页 `HTML` 元素进行解析,通常我们会采取类型字符串正则匹配的方式,这样做太繁琐了。`HTMLAgilityPack` 包可以然我们类似 `HTML` 元素选择的方式获取想要的结果。 |
| 137 | + |
| 138 | +```csharp |
| 139 | +var html = @"http://html-agility-pack.net/"; |
| 140 | +HtmlWeb web = new HtmlWeb(); |
| 141 | +var htmlDoc = web.Load(html); |
| 142 | +var node = htmlDoc.DocumentNode.SelectSingleNode("//head/title"); |
| 143 | +Console.WriteLine("Node Name: " + node.Name + "\n" + node.OuterHtml); |
| 144 | +``` |
| 145 | + |
| 146 | +2、[AutoFixture](https://github.com/AutoFixture/AutoFixture) |
| 147 | + |
| 148 | + |
| 149 | + |
| 150 | +AutoFixture 是一个单元测试辅助开发库,用来帮助我们开发的单元测试库只关心我们需要测试的内容,而不需要被其他的修改而影响。 |
| 151 | + |
| 152 | +假设我们现在有一个 `Employee` 的类 |
| 153 | + |
| 154 | +```csharp |
| 155 | +public class Employee |
| 156 | +{ |
| 157 | + public Employee(string firstName, string lastName) |
| 158 | + { |
| 159 | + FirstName = firstName; |
| 160 | + LastName = lastName; |
| 161 | + } |
| 162 | + public string FirstName { get; set; } |
| 163 | + public string LastName {get; set;} |
| 164 | + public string FullName => $"{FirstName} {LastName}"; |
| 165 | +} |
| 166 | +``` |
| 167 | + |
| 168 | +我们的单元测试是这样的 |
| 169 | + |
| 170 | +```csharp |
| 171 | +public void FullName_Return_Expected() |
| 172 | +{ |
| 173 | + var sut = new Employee("feng", "gao"); |
| 174 | + var actual = sut.FullName; |
| 175 | + Assert.Equal("feng gao", actual); |
| 176 | +} |
| 177 | +``` |
| 178 | + |
| 179 | +这个单元测试没有问题,如果我们的 `Employee` 的构造函数被修改成这样 |
| 180 | + |
| 181 | +```diff |
| 182 | +- public Employee(string firstName, string lastName) |
| 183 | ++ public Employee(string firstName, string lastName, int employeeNumber) |
| 184 | +``` |
| 185 | + |
| 186 | +毫无疑问,我们之前的单元测试肯定失败,甚至无法编译。这时候我们的 `AutoFixture` 起作用的的 |
| 187 | + |
| 188 | +```csharp |
| 189 | +public void FullName_Return_Expected() |
| 190 | +{ |
| 191 | + var fixture = new Fixture(); |
| 192 | + var sut = fixture.Build<Employee>().With(a => a.FirstName, "feng") |
| 193 | + .With(a => a.LastName, "gao").Create(); |
| 194 | + var actual = sut.FullName; |
| 195 | + Assert.Equal("feng gao", actual); |
| 196 | +} |
| 197 | +``` |
0 commit comments