## .NET Extension : .NET Upgrade Assistant
- Automatically upgrade to target framework
[Reference](https://medium.com/@hany.habib1988/my-journey-to-migrate-from-net-core-6-to-net-core-8-c5e9a164c695)
## SqlServer => (Default) Encrypt = true
>Q: 新版 Microsoft.EntityFrameworkCore.SqlServer version > 7後,將Encrypt加密預設為true;
>A: 開發時,將連線字串加上 ";TrustServerCertificate=true",使憑證通過。(not for production cuz it will bypassed identity certificate)
[Microsoft.Data.SqlClient breaking-changes](https://learn.microsoft.com/zh-tw/ef/core/what-is-new/ef-core-7.0/breaking-changes?tabs=v7#encrypt-true)
>A: In production mode, you should configured certificated Authority to the sqlServer.
>using Active Directory produce CA, authorized in client side. https://blog.darkthread.net/blog/setup-ad-n-ca/
---
## System.Text.Json.JsonSerializer => .Net 8 序列過程 將「延遲加載」(lazy loading) 改成 「提前解析」(eager loading)
>Q: 嘗試Deserialize時,會遇到以下錯誤,在反序列化過程中,Encoding 的屬性可能使用 ReadOnlySpan<byte> 或其他類似的字節流來進行字符解碼。 (因為ReadOnlySpan<byte> 是pointer type or ref struct)Unsuported,.Net 7 延遲加載可能會忽略某些屬性,.Net 8提前解析是針對每一個屬性,就不會有不支援的屬性被忽略的問題。
反序列化過程與 ref struct 類型的衝突:因為 ref struct 類型(如 ReadOnlySpan<byte>)不能直接存儲在Heap(堆)上或直接序列化,因此可能會在反序列化過程中觸發錯誤。
```CSharp
({"The type 'System.ReadOnlySpan`1[System.Byte]' of property 'Preamble' on type 'System.Text.Encoding' is invalid for serialization or deserialization because it is a pointer type, is a ref struct, or contains generic parameters that have not been replaced by specific types."})
```
>A: 使用自定義的JsonEncodingConverter,讓Encoding屬性可以被正常解析
```csharp
internal class JsonEncodingConverter : JsonConverter<Encoding>
{
public override Encoding Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> Encoding.GetEncoding(reader.GetString()); // 直接返回Encoding
public override void Write(Utf8JsonWriter writer, Encoding value, JsonSerializerOptions options)
=> writer.WriteStringValue(value.EncodingName); // 只寫入Encoding的名稱
}
var options = new JsonSerializerOptions();
options.Converters.Add(new JsonEncodingConverter());
var file = System.IO.File.ReadAllText(path);
var model = JsonSerializer.Deserialize<VM_Mail>(file, options);
```
```csharp
/// <summary> email附件 </summary>
[JsonIgnore]
public List<Attachment> Attachments { get; set; }
public class Attachment : AttachmentBase
{
private string? _name;
private Encoding? _nameEncoding;
//因為這個Attachment型別裡面有包到Encoding,只要Ignore就好
//System.Text.Json 預設不支援轉換 Encoding
```

```
JsonConverter<T>是抽象泛型類別,覆寫他裡面Read(反序列化)及Write(序列化)的方法
Encoding.GetEncoding(reader.GetString()),這是根據讀取到的字串(代表編碼名稱)來返回對應的 Encoding 類型物件。
writer.WriteStringValue(value.EncodingName),這會把 Encoding 類型的 EncodingName(編碼名稱)寫入 JSON 中。
```
```這個 JsonEncodingConverter 類別是用來處理 Encoding 類型的自定義 JSON 序列化和反序列化過程。它實現了 JsonConverter<Encoding> 類別,這使得它能夠定義如何將 Encoding 類型的資料進行序列化(轉換成 JSON 格式)和反序列化(從 JSON 格式轉回 Encoding 類型)。讓我們逐行解釋這個類別的內容:
1. Read 方法(反序列化)
public override Encoding Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> Encoding.GetEncoding(reader.GetString()); // 直接返回 Encoding
功能:這個方法處理 JSON 反序列化過程,將 JSON 字串中的編碼名稱轉換為對應的 Encoding 類型。
詳細解釋:
reader.GetString():從 JSON 中讀取一個字串(該字串應該是編碼名稱,像是 "UTF-8" 或 "ASCII" 等)。
Encoding.GetEncoding():根據從 JSON 中讀取的編碼名稱,返回對應的 Encoding 類型對象。
例子:假設 JSON 中有這樣一個屬性:
{
"encoding": "UTF-8"
}
當反序列化時,這個方法會將 "UTF-8" 轉換為對應的 Encoding.UTF8。
2. Write 方法(序列化)
public override void Write(Utf8JsonWriter writer, Encoding value, JsonSerializerOptions options)
=> writer.WriteStringValue(value.EncodingName); // 只寫入 Encoding 的名稱
功能:這個方法處理 JSON 序列化過程,將 Encoding 類型轉換成它的編碼名稱字串,然後將這個字串寫入 JSON。
詳細解釋:
value.EncodingName:Encoding 類型有一個 EncodingName 屬性,它會返回該 Encoding 的名稱字串(例如,Encoding.UTF8.EncodingName 會返回 "UTF-8")。
writer.WriteStringValue():將這個編碼名稱字串寫入到 JSON 中。
例子:假設我們有一個 Encoding 對象 Encoding.UTF8,這段方法會將其序列化為字串 "UTF-8"。
3. 總結
這個 JsonEncodingConverter 實現了如何將 Encoding 類型與 JSON 進行互轉:
反序列化(Read):從 JSON 中讀取編碼名稱字串並轉換為對應的 Encoding 對象。
序列化(Write):將 Encoding 對象的編碼名稱寫入 JSON 中。
例子:如何在程式中使用
如果你想要在 JSON 序列化/反序列化過程中使用這個自定義的轉換器,你需要將它註冊到 JsonSerializerOptions:
var options = new JsonSerializerOptions();
options.Converters.Add(new JsonEncodingConverter());
var model = JsonSerializer.Deserialize<VM_Mail>(file, options); // 在反序列化時會使用自定義轉換器
這樣,當 VM_Mail 類別中包含 Encoding 類型的屬性時,JsonEncodingConverter 會被用來處理該屬性的序列化和反序列化。
```
- ReadOnlySpan<byte> 是 ref struct,這類型不適合在 JSON 中進行序列化或反序列化。
- System.Text.Json 嘗試對 Encoding 的所有屬性進行序列化時,遇到這些不支持的類型就會報錯。
- System.Text.Encoding 的內部屬性(例如 Preamble)是 ReadOnlySpan<byte> 類型。
https://stackoverflow.com/questions/78347638/jsonconverterattribute-is-not-used-by-jsonserializer
官方公告的System.Text.Json在.Net 8的改變
https://github.com/dotnet/docs/issues/37041
```
1. 以前(.NET 7)
在 .NET 7 中,System.Text.Json 的反射序列化器採用了「延遲加載」(lazy loading) 的方式來解析物件的屬性元資料。這意味著,如果 POCO(Plain Old CLR Object)包含某些不支援的屬性類型,反序列化可能仍會成功,只要該屬性在實際序列化/反序列化的過程中沒有被觸及。也就是說,即使 POCO 中有不支援的屬性,JSON 並沒有綁定到這些屬性時,反序列化依然能正常運行。
舉例來說,以下代碼在 .NET 7 中不會引發錯誤:
public class MyPoco
{
public int Value { get; set; }
public NestedValue Unsupported { get; set; }
}
public class NestedValue
{
public ReadOnlySpan<byte> Span => Array.Empty<byte>();
}
即使 ReadOnlySpan<byte> 類型不是支持的序列化類型,這段代碼仍能成功反序列化 JSON 字串。因為在反序列化過程中,Unsupported 屬性沒有被觸及,所以不會引發錯誤。
2. 現在(.NET 8)
在 .NET 8 中,這種「延遲加載」的方式被移除了,改為「提前解析」(eager loading) 所有屬性元資料。這意味著在序列化和反序列化過程中,所有屬性都會被立即解析,即便它們最終沒有被實際使用。
這個變更的目的,是為了更好地支持多個解析器(如源生成器)結合使用,這樣可以提高序列化的效能。但這樣的改變也帶來了兼容性問題。比如,在 .NET 8 中,當你嘗試反序列化包含不支援的屬性類型(如 ReadOnlySpan<byte>)的 POCO 時,會引發以下錯誤:
System.InvalidOperationException: The type 'System.ReadOnlySpan`1[System.Byte]' of property 'Span' on type 'NestedValue' is invalid for serialization or deserialization because it is a pointer type, is a ref struct, or contains generic parameters that have not been replaced by specific types.
這是因為 ReadOnlySpan<byte> 類型屬性不再被忽略,並且不再支持序列化或反序列化,因為它是一個「指標類型」(pointer type) 或是「引用結構」(ref struct),這是無法被直接序列化的。
3. 原因
這個改變是因為 .NET 8 引入了新的要求,特別是對於快速序列化支持。這需要對類型圖進行提前分析,以便能夠快速序列化。在這樣的變更下,原來的延遲加載行為不再能支持這樣的需求,並且會導致類似上述的錯誤。
4. 解決方案
官方提供了幾種解決方式來處理這個問題:
解決方案 1:移除不支援的屬性
如果某個屬性不支持序列化,最簡單的解決方式就是將該屬性移除:
public class MyPoco
{
public int Value { get; set; }
public NestedValue Unsupported { get; set; } // 移除這行
}
解決方案 2:自定義轉換器 (JsonConverter)
如果移除屬性不可行,你可以為不支援的屬性類型編寫一個自定義的 JsonConverter,來處理這些屬性的序列化和反序列化。例如,對於 ReadOnlySpan<byte> 類型,可以寫一個自定義的轉換器來進行處理。
解決方案 3:使用 JsonIgnore 屬性
如果你不想讓某些屬性參與序列化,最簡單的方法就是給這些屬性加上 JsonIgnore 屬性,這樣它們就不會被序列化或反序列化了。
public class MyPoco
{
public int Value { get; set; }
[JsonIgnore]
public NestedValue Unsupported { get; set; } // 加上 JsonIgnore,讓這個屬性不被序列化
}
5. 額外寫一個類別來處理
根據你遇到的問題,為了能正確處理不支援的類型(如 ReadOnlySpan<byte>),你需要編寫一個類別(如 JsonEncodingConverter)來處理這些特殊的序列化需求。這是因為在 .NET 8 中,這些不支援的類型(例如 ReadOnlySpan<byte>)不再被隱式忽略,而是會引發錯誤。這就需要你顯式處理這些類型,才能確保它們能夠正確地序列化或反序列化。
如果你不希望手動處理這些屬性類型,那麼使用 JsonIgnore 是最簡單的解決方案。
總結:
舊的行為:延遲加載,某些不支援的屬性不會影響反序列化。
新的行為:提前解析所有屬性,這導致不支援的屬性類型會引發錯誤。
解決方案:可以移除不支援的屬性,使用自定義的轉換器,或者使用 JsonIgnore 屬性來避免序列化這些屬性。
```
---
[.Net 8 Breaking Changes](https://learn.microsoft.com/en-us/dotnet/core/compatibility/8.0)
[黑暗執行緒 - .Net 8 升級 改變 ActivatorUtilitiesConstructor](https://blog.darkthread.net/blog/net8-issue-activatorutilitiesconstructor/)