Các cải tiến hiệu năng LINQ trong .NET 9.0
(blog.ndepend.com)-.NET 9.0 giảm mạnh thời gian thực thi so với .NET 8 trong nhiều kịch bản LINQ phổ biến, và trong một số benchmark còn loại bỏ cả cấp phát bộ nhớ
- Một trong những cải tiến cốt lõi là khi duyệt mảng hoặc
List<T>, nó lấyReadOnlySpan<T>quaTryGetSpan()để giảm chi phí lặp TryGetSpan()nhận diệnTSource[]vàList<TSource>bằng so sánh kiểu, nhưng cách lấy span từ mảng nội bộ củaList<T>là một tối ưu hóa thuộc nhóm Unsafe có thể bị vô hiệu khi dung lượng thay đổi- LINQ trong .NET 9 nhận diện các chuỗi gọi phổ biến để tạo iterator chuyên biệt, đồng thời áp dụng tối ưu hóa bổ sung ở các phương thức kết thúc như
Count(),First(),Last(),ElementAt(),Sum() - Chỉ cần di chuyển phiên bản và biên dịch lại là đã có thể nhận được một phần cải thiện hiệu năng LINQ, cùng với các tối ưu như tận dụng SIMD và phát hiện sớm chuỗi rỗng
Vì sao việc duyệt mảng và danh sách nhanh hơn
- Benchmark đầu tiên lưu
Enumerable.Range(1, 10_000).ToArray()dưới dạngIEnumerable<int>, rồi chạyCount,All,Any,First,Single,Lastđể so sánh .NET 8 và .NET 9 - Sử dụng BenchmarkDotNet, và dự án phải nhắm tới
net8.0;net9.0rồi biên dịch ở chế độ Release - Trong .NET 9, thời gian thực thi của nhiều phương thức giảm mạnh và cấp phát cũng biến mất
LinqCount: từ 16,198.490 ns xuống 3,043.563 ns, từ cấp phát 32 B xuống không còn cấp phátLinqAny: từ 17,096.735 ns xuống 2,483.927 ns, từ cấp phát 32 B xuống không còn cấp phátLinqFirst: từ 15,289.747 ns xuống 2,243.341 ns, từ cấp phát 32 B xuống không còn cấp phátLinqSingle: từ 21,684.114 ns xuống 4,884.329 ns, từ cấp phát 32 B xuống không còn cấp phátLinqAll: từ 10.588 ns xuống 2.562 ns, từ cấp phát 32 B xuống không còn cấp phátLinqLast: từ 15.967 ns xuống 6.918 ns
Khác biệt do TryGetSpan() tạo ra
- Nguyên nhân chính của mức tăng hiệu năng là việc dùng
TryGetSpan() - Nếu enumerable cần duyệt là mảng hoặc danh sách,
TryGetSpan()sẽ trả vềReadOnlySpan<T>để cho phép lặp nhanh hơn - Đoạn nhánh cốt lõi kiểm tra
source.GetType() == typeof(TSource[])hoặcsource.GetType() == typeof(List<TSource>), sau đó lấy span- Mảng được xử lý bằng
Unsafe.As<TSource[]>(source) - Danh sách dùng
CollectionsMarshal.AsSpan(Unsafe.As<List<TSource>>(source))để lấy span từ mảng nội bộ
- Mảng được xử lý bằng
- Trong mã nguồn,
source.GetType()được gọi hai lần và không xử lý bằng ép kiểu rồi kiểm tra null, nhưng đây là lựa chọn có cân nhắc của các chuyên gia hiệu năng .NET dựa trên tối ưu của trình biên dịch C# và JIT - Trong stack .NET đã được tối ưu hóa rất cao, vi tối ưu hóa có thể cho ra kết quả khác với vẻ bề ngoài
Các ràng buộc của CollectionsMarshal.AsSpan()
List<TSource>về bản chất tham chiếu đến một mảng, và khi cần tăng hoặc giảm dung lượng, danh sách sẽ tạo mảng mới rồi tham chiếu sang đóCollectionsMarshal.AsSpan(Unsafe.As<List<TSource>>(source))lấySpan<TSource>từ chính mảng nội bộ này- Nếu dung lượng của danh sách thay đổi theo bất kỳ cách nào, mảng lấy được bằng cách này có thể bị vô hiệu hóa
- Vì ràng buộc đó, một số thao tác
Enumerablecó duyệt trì hoãn nhưyieldkhó có thể dựa vào tối ưu hóa này - Chính cái tên
System.Runtime.CompilerServices.Unsafeđã cho thấy rủi ro đó
Phạm vi gọi TryGetSpan()
- Dùng NDepend để quét
System.Linq.dllvà xác định các caller trực tiếp/gián tiếp củaTryGetSpan() - Đường dẫn assembly được phân tích là
C:\Program Files\dotnet\shared\Microsoft.NETCore.App\9.0.0-rc.1.24431.7\System.Linq.dll - Từ
TryGetSpan(), tạo truy vấn mã để nhận diện caller, rồi xuất 56 phương thức khớp ra đồ thị phụ thuộc - Nhiều phương thức
Enumerabletiêu chuẩn sẽ thử duyệt bằng span khi collection là mảng hoặc danh sách - Tuy vậy, vì cách giữ mảng nội bộ của danh sách không an toàn, các phép toán cần thực thi trì hoãn vẫn còn bị hạn chế
Tối ưu hóa dựa trên iterator chuyên biệt
- Benchmark thứ hai dùng ví dụ trong PR Consolidate LINQ’s internal IIListProvider/IPartition into base Iterator class
- Các trường hợp được thử gồm
Distinct().First(),Append().Select().Last(),Reverse().Count(),DefaultIfEmpty().Select().ElementAt(),Skip().Take().ElementAt(),Union().First(),Select().Where().Select().Sum() - Trong .NET 9, một số chuỗi gọi nhanh lên rất mạnh
DistinctFirst: từ 65.318 ns xuống 11.192 ns, từ cấp phát 328 B xuống không còn cấp phátAppendSelectLast: từ 4,122.007 ns xuống 2.661 ns, từ cấp phát 144 B xuống không còn cấp phátDefaultIfEmptySelectElementAt: từ 4,090.818 ns xuống 5.724 ns, từ cấp phát 144 B xuống không còn cấp phátRangeUnionFirst: từ 66.309 ns xuống 6.193 ns, từ cấp phát 344 B xuống không còn cấp phátListSkipTakeElementAt: từ 6.268 ns xuống 2.916 nsRangeReverseCount: từ 11.024 ns xuống 6.134 ns
- Ngược lại,
SelectWhereSelectSumchậm hơn, từ 3,959.622 ns trên .NET 8 lên 4,460.008 ns trên .NET 9, và vẫn giữ cấp phát 112 B
Nhận diện các chuỗi LINQ phổ biến
- Nhóm hiệu năng .NET đã thiết kế mã để nhận diện các chuỗi gọi LINQ phổ biến
- Khi phát hiện một chuỗi cụ thể, hệ thống sẽ tạo iterator chuyên biệt để xử lý luồng công việc hiệu quả hơn
- Nếu chuỗi kết thúc bằng các phương thức như
Count(),First(),Last(),ElementAt(),Sum(), có thể áp dụng thêm tối ưu hóa - Ví dụ,
OrderBy(criteria).First()có thể được tối ưu để chạy nhưMin(criteria)
Cấu trúc Iterator<T> và các lớp dẫn xuất
- Bên trong LINQ có lớp cơ sở trừu tượng
Iterator<T>và 40 lớp dẫn xuất - Tất cả các lớp này đều được lồng bên trong lớp
Enumerable Iterator<T>là lớp trừu tượng nhưng các phương thức là virtual, nên các lớp dẫn xuất chỉ override những phương thức cần thiết- Cấu trúc này là nền tảng để chứa hành vi chuyên biệt theo từng chuỗi gọi
Trường hợp ListWhereSelectIterator<TSource, TResult>
ListWhereSelectIterator<TSource, TResult>xử lý chuỗiWhere(...).Select(...)trên danh sách bằng một iterator duy nhất- Iterator này được tạo trong
Select()override củaListWhereIterator<TSource, TResult> ListWhereIterator<TSource>được tạo khiEnumerable.Where()kiểm tra thấy nguồn làList<TSource>ListWhereSelectIterator<TSource, TResult>không override các phương thức nhưTryGetFirst()hayTryGetLast()- Điểm mấu chốt của cải thiện hiệu năng là trên danh sách, chuỗi rất phổ biến
Where(...).Select(...)được gộp thành một iterator thay vì hai iterator- Bên trong
MoveNext(), hai delegate_predicatevà_selectorđược gọi cùng nhau
- Bên trong
Trường hợp IListSkipTakeIterator<TSource>
IListSkipTakeIterator<TSource>là iterator chuyên biệt được tạo ra khi có thể áp dụngMoveNext()dùng_state - 1làm chỉ số 0-based của danh sách- Dùng một trường chỉ số riêng sẽ dễ đọc hơn, nhưng để giảm kích thước trường của iterator, giá trị được lưu lệch vào
_state - Tối ưu hóa của iterator này nằm ở việc không duyệt thừa các phần tử nằm ngoài khoảng
_minIndexInclusivevà_maxIndexInclusive
Các tối ưu bổ sung chỉ nhờ di chuyển phiên bản
- Trong .NET 9, nhiều kịch bản LINQ phổ biến trở nên nhanh hơn
- Để nhận được các cải tiến từ phiên bản .NET mới, việc cần làm chỉ là di chuyển phiên bản và biên dịch lại
- LINQ cũng được tối ưu bằng các cách khác
- Với các trường hợp như cộng tổng chuỗi số nguyên, nó dùng SIMD ở những nơi có thể
- Các chuỗi rỗng được phát hiện sớm để giảm chi phí liệt kê
- DeepDotnet videos là tài liệu học .NET có sự xuất hiện của Scott Hanselman và Stephen Toub
1 bình luận
Ý kiến trên Hacker News
Tôi cho rằng phần hữu ích nhất của LINQ không phải là cấu trúc mở rộng dựa trên cây cú pháp của
IQueryable, cũng không phải cú pháp tích hợp trong ngôn ngữ, mà là các phương thức mở rộngIEnumerableTrước đây nó được gọi hơi dễ gây nhầm lẫn là “LINQ to Objects”, và giúp viết C# theo phong cách hàm một cách ngắn gọn
Bài gốc chủ yếu nói về việc tối ưu hóa các phương thức mở rộng này
Chỉ sau khi học Haskell tôi mới thực sự cảm nhận được cách làm này, và nó cũng chia sẻ một phần các điểm mạnh lẫn cạm bẫy của Haskell như đánh giá lười
Nếu dùng bừa bãi thì có thể tạo ra mã khó hiểu và chậm, nên tôi không muốn khuyến nghị nếu trong nhóm không có ai biết các idiom hàm cơ bản và đánh giá lười
IEnumerablevàIQueryableDễ suy luận hơn, và trong những nơi như Entity Framework thì không phải lúc nào cũng là lựa chọn nhanh nhất, nhưng nhìn chung là một lựa chọn khá ổn
Tôi cũng thích dùng Dapper hơn EF
Tuy vậy, các dự án C# có xu hướng có quá nhiều tầng trừu tượng đến mức phi lý, và phát triển “enterprise” nhìn chung khá khó chịu
Có vài chỗ đặt tên hơi không theo chuẩn, nhưng có đủ mọi thứ cần thiết
Eric Lippert đã viết một loạt bài rất hay giải thích monad gắn với LINQ: https://ericlippert.com/2013/04/02/monads-part-twelve/
Tôi không thích việc có thêm một ngôn ngữ “tích hợp” khác bên trong ngôn ngữ chủ, và kết quả cuối cùng rốt cuộc cũng phải quay về C#
Ngay cả khi không dùng ORM như Entity Framework hay Dapper, tôi vẫn thích đặt logic truy cập dữ liệu, bao gồm SQL, trong một dự án được trừu tượng hóa riêng
Như vậy nó không lan ra toàn bộ ứng dụng, và có thể thay thế khi cần một RDBMS khác
Dù trong 20 năm thực tế chỉ có đúng một lần như vậy
Khi lập trình viên junior dùng LINQ, gắn profiler và debugger cho họ sẽ giúp hiểu chuyện gì đang diễn ra bên trong
Đôi khi cũng hữu ích khi để họ viết trước bằng vòng lặp
forvà logic C# thông thường rồi so sánh với bản triển khai LINQ, để thấy ưu nhược điểm của hai cách tiếp cậnCú pháp truy vấn không được hard-code riêng cho
IEnumerable; đó chỉ là hành vi mặc định, và gần như có thể dùng ở bất cứ đâuNó hoạt động hơi giống operator overloading
[1]: https://github.com/acple/ParsecSharp/blob/da8d0cb9ec39e28dd9...
Tôi không hiểu vì sao nhóm dotnet không đầu tư nhiều tài nguyên và thời gian hơn vào công cụ
Cần có doctest và sinh tài liệu, khả năng viết unit test tốt hơn và nhanh hơn ngay cạnh mã thật, khả năng truy cập mã nguồn, môi trường mà khi nhấn F12 không cần decompile DLL, và một hub trung tâm cho package và tài liệu như
pkg.go.devhaydocs.rsPhần lớn package NuGet hoặc hoàn toàn không có tài liệu, hoặc chỉ có README trên GitHub, hoặc một wiki ngắn
Các môi trường khác như Rust, Go, Java, Python đi trước vài năm ánh sáng ở mặt này
Nhưng điều kinh khủng là tôi cũng cảm thấy chuyện đó gần với sự thật
Ví dụ: https://learn.microsoft.com/en-us/dotnet/api/system.string.s...
Mã nguồn của package NuGet cũng có thể làm dễ dàng nếu bật Source Link, nhưng đây vẫn là tính năng tương đối mới nên chưa phải package nào cũng áp dụng
Tôi đoán phần lớn mã C# hiện vẫn được viết trong doanh nghiệp dưới dạng mã nguồn đóng
Nếu Microsoft tiếp tục đi theo hướng cởi mở như vài năm gần đây, tôi nghĩ mọi thứ sẽ dần tốt hơn theo thời gian
Một số tính năng này được các công cụ như Resharper cung cấp, nên tôi tự hỏi liệu có thỏa thuận rõ ràng hay ngầm hiểu nào về việc không lấn sân nhau hay không
Thành thật mà nói, phần lớn tài liệu tôi thấy trong các dự án C# có chất lượng thấp, nên cuối cùng vẫn phải xem mã nguồn
Kinh nghiệm của tôi là dù có nhiều công cụ tự động hoàn thành, chúng không giúp ích mấy cho việc đọc, mà chỉ giúp cho việc viết
https://github.com/EWSoftware/SHFB
Tôi không chắc có nên khuyến nghị không
Tôi đã thử rồi quay lại cách cũ, vì test dường như chạy lâu hơn, có lẽ do caching artifact build trở nên kém hơn
Nói chính xác thì không nên gọi là “cải thiện hiệu năng LINQ” mà là “cải thiện hiệu năng triển khai
Listcủa riêng họ”Có vẻ Microsoft dành thời gian cải thiện những phần họ cần hơn là các cải tiến nói chung
LINQ, đặc biệt là cú pháp truy vấn chứ không phải các extension method, cần được đầu tư
Chủ yếu cần giảm việc cấp phát lambda, và nếu có thể thì rút gọn lambda ở thời điểm biên dịch
Cần có lambda cục bộ kiểu giá trị, hoặc một chiến lược để việc cấp phát lambda không còn là overhead như hiện nay
Biến LINQ cũng nên được hỗ trợ wildcard (
_) từ lâu rồi, nhưng khi đưa vào lambda thì lại bị bỏ qua hoàn toànNgoài ra, ở mục cuối của biểu thức LINQ, cần có thể dùng các kiểu đã được lifting như
IEnumerable,Optionthay choselect ...Trong một số use case,
selecttạo ra overhead không cần thiết, và còn hạn chế những thứ như biểu thức LINQ đệ quy đuôiNhững thư viện đặt cược hoàn toàn vào LINQ như thư viện của tôi, nhưng không dùng
IEnumerable,IQueryablehay LINQ extension, vẫn tiếp tục bị phớt lờLý do là Microsoft chỉ tập trung cải thiện hiệu năng cho các dự án của chính họ
Một ví dụ điển hình là suy luận lambda đã được cải thiện
Nó được đẩy nhanh vì ASP.NET Core Minimal API cần đến
Có vẻ khá nhiều tính năng ngôn ngữ và framework được thúc đẩy bởi nhu cầu nội bộ của Microsoft hơn là nhu cầu của cộng đồng
Điều tệ nhất là tập hợp các phương thức ma thuật như
GetAwaiter, không chỉ các LINQ extensionSelect,SelectMany,Where, cứ tiếp tục phình raThay vì đưa vào các đặc tính higher-kinded thật sự cần thiết để hóa giải sự “ma thuật” này, Microsoft lại thêm tính năng vì lợi ích của chính họ, chủ yếu là vì trình biên dịch
Vì vậy mọi thứ vẫn ở trạng thái kiểu yếu, và trình biên dịch chỉ có thể phát hiện một cách đại khái
LINQ là một trong những điểm khác biệt cốt lõi giữa các ngôn ngữ, nhưng gần như bị bỏ mặc kể từ C# 3
Thật đáng tiếc khi đến giờ họ vẫn xem LINQ chỉ hữu ích cho việc duyệt danh sách, đặc biệt là duyệt triển khai danh sách của chính họ
Bản thân các cải thiện hiệu năng thì đáng cảm ơn và sẽ giúp ích cho nhiều người dùng, nhưng trọng tâm lúc nào cũng quá hẹp, làm hạn chế tiềm năng
[1] https://github.com/louthy/language-ext/
dotnet/runtimeNhiều cải thiện hiệu năng LINQ được nói trong bài đã được đưa vào theo cách đó
Có nhiều câu lệnh
using, điều này không phải vấn đề lớn với những người hiểu cách chia nhỏ dự án theo đơn vị cần thiết và tách biệt mối quan tâmNhưng phần lớn lập trình viên không cấu trúc dự án như vậy, và những chi tiết nhỏ này có thể trở thành rào cản với lập trình viên trung bình
Các lập trình viên junior thường đã gặp khó với cú pháp và phương thức LINQ chuẩn, đặc biệt là cả về hiệu năng
Việc README nhắc đến những điểm này là tốt
Thường thì các thư viện chỉ bận “bán” mình, nên tôi thích việc bạn thật sự ghi rõ nó mạnh ở đâu và mục đích là gì
Việc nói rằng nó không theo thông lệ cũng có thể là vấn đề với người học C#/.NET
Microsoft có lẽ muốn công cụ và ngôn ngữ tuân theo một số thông lệ nhất định, và cách đặt tên tự nhiên theo lập trình hàm có thể bị Microsoft xem là trở ngại khá lớn khi cân nhắc cải tiến
Tôi đã star repository này và rất quan tâm đến những gì bạn đã làm
Trong vài ứng dụng lớn gần đây, tôi đã dùng một kiểu
Resulttrông đại khái giốngOptionNhưng khi xem lại thư viện, tôi nhận ra dù từng nghĩ mình khá hiểu lập trình hàm, thực ra không phải vậy
Tôi khá giỏi C# và đã làm những việc phức tạp, nhưng lập trình hàm dù đọc nhiều vẫn khó nắm bắt, và F# For Fun And Profit là thứ giúp tôi hiểu rõ nhất
Tóm lại, điều đó không có nghĩa là bạn đang làm sai điều gì
Microsoft sẽ nhắm đến nhóm lập trình viên trung bình hoặc mới bắt đầu, vốn chiếm phần lớn hệ sinh thái của họ
Tôi hy vọng thư viện này có thể nhận được các cải tiến nội bộ
Có thể thấy bạn đã bỏ ra rất nhiều thời gian, và chỉ riêng số sao trên GitHub cũng đủ là tín hiệu rằng có người thật sự dùng và được nó giúp ích
Nếu nghe như tôi đang coi thường thì xin lỗi, nhưng công trình bạn làm rất thú vị và có vẻ tài liệu cũng đủ để tôi từ từ học các khái niệm mình chưa biết
C# càng vay mượn nhiều hơn từ F# thì càng tốt
Tôi đang chờ discriminated union cuối cùng cũng được đưa vào C# để có thể mô hình hóa domain một cách đúng đắn
Mỗi lần như thế tôi lại tự hỏi: “Sao không dùng luôn F#?”
C# đã phải đuổi theo trong nhiều năm
Nếu F# đang thúc đẩy nhiều đổi mới trong hệ sinh thái .NET và đã đi trước vài năm về mặt tính năng, tôi tự hỏi tại sao mọi người không thưởng cho nỗ lực đó bằng cách sử dụng nó
Nếu có một hướng phát triển ngôn ngữ mà bạn mong muốn, bạn nên khuyến khích nó bằng lựa chọn thực tế
Trong hệ sinh thái Java, cách đó đã có tác dụng, và giờ Java cũng đang được cải thiện
Khi thị trường lớn hơn, có thể tạo ra vòng phản hồi tích cực khiến nỗ lực kỹ thuật cũng tăng lên
Đọc các diễn đàn nhiều năm, tôi có cảm giác phe C# rất muốn “cứ” ở lại phe mình và chờ đợi
Trông hơi giống chủ nghĩa bộ lạc, như thể đội của họ là “C#”
Tôi không thấy văn hóa này nhiều ở các hệ sinh thái ngôn ngữ khác, và có cảm giác nếu F# nằm trong một hệ sinh thái khác ngoài .NET thì có lẽ nó đã phát triển mạnh từ lâu
Nó sẽ giúp việc bảo trì code kỹ thuật hoặc khoa học dễ dàng hơn rất nhiều
Ví dụ sử dụng thực tế: https://chrlschn.dev/blog/2024/07/csharp-discriminated-union...
[0] https://github.com/mcintyre321/OneOf
[1] https://github.com/domn1995/dunet
Điều tôi nhớ nhất khi làm việc với ngôn ngữ hay hệ sinh thái khác là LINQ
Việc có những tính năng như vậy trong thư viện chuẩn thật sự rất tuyệt, và nó được thiết kế đẹp trong các ràng buộc sẵn có
Trong bài viết thường niên dài cỡ một cuốn sách nói về mọi cải tiến hiệu năng của .NET 9 có một phần liên quan
https://devblogs.microsoft.com/dotnet/performance-improvemen...
Lạ là HN không cho phép gửi lại, nên bài viết không lên được front page và bị chìm
Khi đã quen với LINQ, và thường làm việc trong những domain nơi LINQ tỏa sáng, bạn sẽ không muốn quay lại cách khác nữa
LINQ sẽ níu giữ bạn, và khiến bạn oán trách những môi trường không có LINQ
Tôi tò mò liệu có cuốn sách hay tutorial tổng hợp nào tốt để học phát triển web end-to-end bằng dotnet không
Những thứ tôi tìm thấy phần lớn hoặc quá cơ bản, hoặc đã cũ, hoặc chất lượng thấp
Cá nhân tôi nghĩ nó sẽ đi theo con đường của Silverlight
Các công nghệ cũ hơn vẫn có trong .NET 9, vẫn hoạt động và được bảo trì
Ngày nay làm web bằng .NET nhìn chung là tạo HTTP/JSON/REST API rồi kết nối với framework frontend mà bạn muốn
Trường hợp của tôi thì dùng React hoặc NextJS
Từ khóa tìm kiếm nên là ASP.NET WebApi, hoặc hiện đại hơn là ASP.NET Minimal API
Bạn vẫn có thể render phía server bằng .NET MVC với Razor
Vì đây là ngôn ngữ markup của ASP.NET MVC, nên cứ tìm “ASP.NET MVC Razor” là được
Dù tốt hay xấu, cách làm web application trong .NET về cơ bản có vẻ là ASP.NET
Việc thiếu lựa chọn thay thế thì hơi đáng ngờ
Tôi đã nghe một podcast có Andrew Lock, tác giả của “ASP.NET Core in Action”, và ông ấy có vẻ là người rất hiểu vấn đề
Tôi chưa đọc sách, nhưng có thể đó là cuốn bạn đang tìm
1: https://dotnetcore.show/season-6/navigating-the-aspnet-core-...
2: https://www.manning.com/books/asp-net-core-in-action-third-e...
Ở phía server, bạn có thể chạy Giraffe trên ASP.NET; đó là một lớp lập trình hàm có hiệu năng tương tự C#
Ở frontend, bạn có thể viết React bằng một ngôn ngữ lập trình hàm thật sự
Tất nhiên cũng có thể chia sẻ mã F# giữa frontend và backend
Về sách thì có “C# 12 and .NET 8 - Modern Cross-Platform Development Fundamentals - Eighth Edition: Start building websites and services with ASP.NET Core 8, Blazor, and EF Core 8” của Mark J Price, và “Web API Development with ASP.NET Core 8: Learn techniques, patterns, and tools for building high-performance, robust, and scalable web APIs” của Xiaodi Yan
Tutorial thì có các series của IAmTimCorey và Shawn Wildermuth trên YouTube
Nếu là tổ hợp backend .NET và frontend JS, hãy tìm tài liệu dùng Minimal API
MVC cũng tốt, nhưng gánh nặng tương thích ngược nhiều nên Minimal API mới ra đời
Phải có cách nào tốt hơn đống mì chú thích này chứ
Mỗi lần nhìn mã .NET hiện đại là tôi đau mắt
Mã unit test và benchmarking thường trông hơi spaghetti ở một mức nào đó
Dù vậy, nếu PR trong business logic thực tế mà như thế thì tôi sẽ không duyệt
Nếu thật sự ghét, bạn vẫn có thể dùng những thứ như AspNetCore mà không đụng đến attribute nào cả
Tôi gần như không dùng attribute
Phần nói rằng có thể tối ưu thêm khi chain kết thúc bằng các phương thức như
Count(),First(),Last(),ElementAt(),Sum(), chẳng hạnOrderBy(criteria).First()có thể được tối ưu để chạy giốngMin(criteria), có thể hữu íchNhưng ngay từ đầu nên viết mã tốt hơn
Điều này có thể thú vị với các chain được tạo động, nhưng nếu là mã viết tay mà làm các thao tác như vậy thì tôi thấy giống một kiểu củng cố tích cực lệch lạc
Thư viện đang nhận diện và sửa các pattern kém hiệu quả
Ít nhất tôi hy vọng có phản hồi đề xuất cải thiện mã nền tảng