Cẩm nang thực địa về các định dạng dữ liệu .NET

Cẩm nang thực địa về các định dạng dữ liệu .NET

Trong Phần 1, chúng ta đã lập luận rằng mỗi định dạng dữ liệu đều phát triển ngôn ngữ riêng của nó vì hình dạng dữ liệu quyết định loại điều hướng nào có ý nghĩa. Phần này là tham chiếu mà bạn thực sự đánh dấu trang: một chuyến đi qua các định dạng mà nhà phát triển .NET thường gặp, ngôn ngữ gốc mà mỗi định dạng sử dụng, và một đoạn mã điển hình nhỏ cho mỗi định dạng để bạn có thể thấy hình dạng của mã nguồn, không chỉ lý thuyết.

Hãy coi đây là cẩm nang thực địa, không phải hướng dẫn — đủ để nhận ra phương ngữ của mỗi định dạng và biết nơi cần tìm tiếp theo.

CSV và văn bản phân tách

Ngôn ngữ gốc: không thực sự — chỉ là các quy tắc phân tích chuỗi,尽管 CsvHelper đã trở thành gần như một DSL thực tế để ánh xạ các hàng sang kiểu dữ liệu.

CSV là định dạng cũ nhất, ít cấu trúc nhất trong danh sách này. Không có ngôn ngữ truy vấn vì gần như không có schema — chỉ có các hàng và ký hiệu phân tách. Trong .NET hiện đại, CsvHelper là thư viện tiêu chuẩn, xử lý việc trích dẫn, thoát ký tự và ánh xạ kiểu dữ liệu mà mã String.Split thủ công thường mắc lỗi nhiều hơn mọi người tưởng.

using CsvHelper;
using System.Globalization;

using var reader = new StreamReader("orders.csv");
using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);

var orders = csv.GetRecords<Order>()
    .Where(o => o.Total > 100)
    .ToList();

Lưu ý rằng một khi CSV được phân tích thành các đối tượng Order, “việc truy vấn” chỉ là LINQ to Objects. Ngôn ngữ gốc của CSV thực sự chỉ là phân tích — việc truy vấn xảy ra sau khi chuyển đổi sang định dạng khác.

Sử dụng khi: bạn đang xử lý các tệp xuất, nhập hoặc nhật ký dữ liệu tự nhiên dạng bảng và không cần quan hệ giữa các bản ghi.

RDBMS: Ngôn ngữ truy vấn gốc

Ngôn ngữ gốc: SQL.

Cơ sở dữ liệu quan hệ là định dạng mà hầu hết nhà phát triển .NET gặp đầu tiên, và SQL là ngôn ngữ trưởng thành và biểu cảm nhất trong toàn bộ danh sách này. .NET có ba cách phổ biến để sử dụng nó, ở các mức trừu tượng hóa tăng dần:

// Raw ADO.NET — closest to the metal
using var connection = new SqlConnection(connectionString);
using var command = new SqlCommand(
    "SELECT * FROM Orders WHERE Total > @minTotal",
    connection);
command.Parameters.AddWithValue("@minTotal", 100);
connection.Open();
using var reader = command.ExecuteReader();

// Dapper — a thin, popular micro-ORM
var orders = connection.Query<Order>(
    "SELECT * FROM Orders WHERE Total > @minTotal",
    new { minTotal = 100 });

// Entity Framework Core — LINQ translated to SQL
var orders = dbContext.Orders
    .Where(o => o.Total > 100)
    .ToList();

Ví dụ EF Core đáng để suy nghĩ: bạn viết LINQ, nhưng bên dưới, nó được biên dịch thành SQL và gửi đến cơ sở dữ liệu. Đây là mô hình nhà cung cấp LINQ đang hoạt động — cùng cú pháp, được dịch sang ngôn ngữ gốc của định dạng. Điều này tiện lợi, nhưng đây cũng là nơi đầu tiên nhà phát triển gặp vấn đề “abstraction leak” mà loạt bài này sẽ quay lại ở Phần 3 — một số biểu thức LINQ không dịch sạch sẽ, và SQL được tạo ra không phải lúc nào cũng là thứ bạn viết bằng tay.

Sử dụng khi: dữ liệu của bạn có quan hệ mạnh mẽ, cần tính toàn vẹn giao dịch, hoặc được hưởng lợi từ các hoạt động tập hợp như JOIN và tổng hợp.

XML: Định dạng cây

Ngôn ngữ gốc: XPath, XQuery, và trong .NET, LINQ to XML.

Dữ liệu XML là một cây, vì vậy các ngôn ngữ của nó được xây dựng xung quanh việc duyệt — đi sâu vào các nút con, khớp theo đường dẫn, lọc theo thuộc tính.

using System.Xml.Linq;

XDocument doc = XDocument.Load("catalog.xml");

// LINQ to XML — tree traversal expressed as LINQ
var fictionBooks = doc.Descendants("book")
    .Where(b => (string)b.Attribute("category") == "fiction")
    .Select(b => (string)b.Element("title"));

// Equivalent XPath, for comparison
var xpathResult = doc.XPathSelectElements(
    "//book[@category='fiction']/title");

Cả hai đoạn mã đều làm cùng một việc, nhưng chúng đọc khác nhau — LINQ to XML cảm giác như truy vấn một collection, trong khi XPath cảm giác như đưa hướng dẫn qua một cây. Điều này không phải tình cờ: XPath được xây dựng purpose-built cho việc duyệt cây, và LINQ to XML là một wrapper sau này, mang tính C# hơn, bao quanh cùng việc duyệt cơ bản.

Sử dụng khi: bạn đang làm việc với các tệp cấu hình, dịch vụ SOAP, các định dạng trao đổi tài liệu (như Office Open XML), hoặc bất cứ thứ gì có schema chặt chẽ, có thể kiểm chứng (XSD).

JSON: Ngôn ngữ chung của Web

Ngôn ngữ gốc: không có tiêu chuẩn hình thức duy nhất, mà là sự kết hợp giữa deserialization mạnh kiểu, truy cập động, và JSONPath cho các truy vấn ad hoc.

JSON vượt qua XML cho các payload API vì nó nhẹ hơn và ánh xạ tự nhiên sang các đối tượng JavaScript. Mối quan hệ của .NET với JSON đã đi qua Newtonsoft.Json (Json.NET) đến System.Text.Json tích hợp sẵn.

using System.Text.Json;

string json = await httpClient.GetStringAsync(
    "https://api.example.com/orders");

// Strongly-typed — the common case
var orders = JsonSerializer.Deserialize<List<Order>>(json);
var bigOrders = orders.Where(o => o.Total > 100);

// Loosely-typed — when the shape isn't known ahead of time
using JsonDocument doc = JsonDocument.Parse(json);
JsonElement root = doc.RootElement;
foreach (var order in root.EnumerateArray())
{
    if (order.GetProperty("total").GetDouble() > 100)
    {
        Console.WriteLine(order.GetProperty("id").GetString());
    }
}

Khi JSON được deserialized thành các đối tượng, “ngôn ngữ” bạn sử dụng lại là LINQ to Objects — cùng mô hình chúng ta thấy với CSV. Đặc điểm riêng của JSON là nó đứng ở hai thế giới: deserialization mạnh kiểu, cứng nhắc khi bạn kiểm soát cả hai đầu, và điều hướng lỏng lẻo, dựa trên đường dẫn (JsonDocument, hoặc các thư viện JSONPath) khi bạn đang xử lý tài liệu có hình dạng bạn không kiểm soát hoàn toàn.

Sử dụng khi: bạn đang xây dựng hoặc sử dụng các HTTP API, hoặc lưu trữ dữ liệu bán cấu trúc nơi schema cứng nhắc nhiều hơn rắc rối.

Cơ sở dữ liệu tài liệu: Môi trường sống gốc của JSON

Ngôn ngữ gốc: các phương ngữ giống SQL được xây dựng purpose-built cho tài liệu JSON — SQL API của Cosmos DB, ngôn ngữ truy vấn của MongoDB.

Cơ sở dữ liệu tài liệu lưu trữ các tài liệu hình dạng JSON nhưng truy vấn chúng bằng thứ gì đó trông giống SQL — một lựa chọn thiết kế có ý thức để ngôn ngữ dễ tiếp cận, dù mô hình bên dưới không phải quan hệ.

using Microsoft.Azure.Cosmos;

Container container = client.GetContainer("SalesDb", "Orders");

var query = new QueryDefinition(
    "SELECT * FROM o WHERE o.total > @minTotal")
    .WithParameter("@minTotal", 100);

using FeedIterator<Order> iterator = container.GetItemQueryIterator<Order>(query);
while (iterator.HasMoreResults)
{
    FeedResponse<Order> response = await iterator.ReadNextAsync();
    foreach (var order in response) { /* ... */ }
}

Đây là minh họa tốt về mối quan hệ định dạng-ngôn ngữ trở nên mờ đi có chủ đích: ngôn ngữ truy vấn của Cosmos DB mượn cú pháp SQL để dễ quen thuộc, nhưng nó hoạt động trên các tài liệu JSON, không phải các hàng quan hệ — không có JOIN giữa các container, hành vi đánh dấu index khác, mô hình chi phí khác. Từ vựng được mượn; ngữ pháp bên dưới không giống nhau.

Sử dụng khi: dữ liệu của bạn tự nhiên hình tài liệu, schema của bạn phát triển thường xuyên, hoặc bạn cần quy mô ngang mà RDBMS truyền thống làm đắt đỏ.

Dữ liệu đồ thị: Quan hệ như công dân hạng nhất

Ngôn ngữ gốc: GraphQL (cho việc truy vấn API), Cypher/Gremlin (cho cơ sở dữ liệu đồ thị).

Đồ thị là định dạng mà các quan hệ giữa các bản ghi quan trọng như chính các bản ghi — mạng xã hội, hệ thống gợi ý, phát hiện gian lận. Trong .NET, hỗ trợ GraphQL thường đến qua thư viện như HotChocolate, xuất một schema mà khách hàng truy vấn bằng cú pháp riêng của GraphQL:

query {
  order(id: "1001") {
    id
    total
    customer {
      name
      previousOrders(last: 5) {
        total
      }
    }
  }
}

Lưu ý rằng GraphQL, bất chấp tên gọi, thực sự là ngôn ngữ truy vấn API hơn là ngôn ngữ duyệt đồ thị thực sự — nó cho phép khách hàng yêu cầu chính xác hình dạng dữ liệu lồng nhau mà nó muốn, điều này tình cờ phù hợp tự nhiên với các quan hệ dạng đồ thị nhưng không giống với việc truy vấn trực tiếp cơ sở dữ liệu đồ thị. Các cơ sở dữ liệu đồ thị thực sự như Neo4j sử dụng các ngôn ngữ duyệt như Cypher (MATCH (o:Order)-[:PLACED_BY]->(c:Customer) WHERE o.total > 100 RETURN c), mà các ứng dụng .NET thường gọi qua driver hơn là viết bằng cú pháp C# gốc.

Sử dụng khi: các quan hệ trong dữ liệu của bạn là mục tiêu chính — không chỉ là thuộc tính để tra cứu, mà là thứ bạn thực sự cố gắng duyệt và phân tích.

Định dạng nhị phân và Serialized

Ngôn ngữ gốc: không có, theo thiết kế.

Các định dạng như Protocol Buffers (protobuf) và MessagePack là những kẻ ngoại lệ trong danh sách này — chúng được tối ưu hóa cho tính compact và tốc độ, không phải khả năng đọc của con người hay việc truy vấn ad hoc. Không có ngôn ngữ truy vấn vì bạn không được phép truy vấn dạng nhị phân trực tiếp; bạn cần deserialize nó thành các đối tượng trước, sau đó truy vấn các đối tượng đó như bất kỳ thứ gì khác.

Các định dạng nhị phân thú vị vì chúng cố tình tránh trở thành biểu diễn có thể đọc được bởi con người. Mục tiêu của chúng không phải là được khám phá bằng truy vấn mà là di chuyển dữ liệu qua ranh giới tiến trình hoặc mạng một cách hiệu quả nhất có thể. Một khi được deserialize, chúng ngay lập tức trở lại là các đối tượng CLR bình thường.

// Protobuf — schema-defined, then generated C# classes
Order order = Order.Parser.ParseFrom(binaryData);

// From here, it's just an object again
if (order.Total > 100) { /* ... */ }

Sử dụng khi: hiệu suất và kích thước payload quan trọng hơn khả năng đọc — giao dịch nội bộ giữa các dịch vụ, luồng sự kiện thông lượng cao, các lớp đệm.

Đối tượng trong bộ nhớ: Điểm đến chung

Ngôn ngữ gốc: LINQ to Objects.

Đây là mô hình đã âm thầm chạy qua hầu hết mọi ví dụ ở trên: bất kể dữ liệu bắt đầu ở định dạng nào — hàng CSV, tài liệu JSON, hàng cơ sở dữ liệu, protobuf đã deserialize — một khi nó được tải vào C#, nó trở thành các đối tượng, và ngôn ngữ bạn sử dụng để truy vấn nó là giống nhau: LINQ to Objects.

var bigRecentOrders = orders
    .Where(o => o.Total > 100 && o.Date > DateTime.Now.AddDays(-30))
    .OrderByDescending(o => o.Total)
    .Select(o => new { o.Id, o.Total });

Điều này đáng để gọi ra rõ ràng, vì nó định nghĩa lại toàn bộ cẩm nang thực địa: hầu hết các định dạng này thực sự không phải là các ngôn ngữ cạnh tranh mà bạn sử dụng thay vì LINQ — chúng là các ngôn ngữ bạn sử dụng để đưa dữ liệu vào hình dạng mà LINQ có thể tiếp quản. RDBMS và XML thực sự là hai định dạng duy nhất trong danh sách này có ngôn ngữ truy vấn độc lập, trưởng thành thực sự cạnh tranh với LINQ trên sân của riêng nó, đó chính là lý do Phần 3 sẽ tập trung vào những trường hợp đó khi nó xem xét nơi abstraction của LINQ hoạt động và nơi nó không.

Cẩm nang thực địa cái nhìn nhanh

Định dạng Ngôn ngữ gốc Công cụ .NET phổ biến Kiểu truy vấn
CSV / phân tách Không có (quy tắc phân tích) CsvHelper Theo hàng, sau đó LINQ
RDBMS SQL ADO.NET, Dapper, EF Core Dựa trên tập hợp (lọc, join, tổng hợp)
XML XPath, XQuery LINQ to XML, XDocument Duyệt cây
JSON Không hình thức; JSONPath cho ad hoc System.Text.Json, Newtonsoft.Json Deserialize mạnh kiểu hoặc dựa trên đường dẫn
Cơ sở dữ liệu tài liệu Phương ngữ giống SQL Cosmos SDK, MongoDB driver Giống SQL, phạm vi tài liệu
Đồ thị GraphQL, Cypher/Gremlin HotChocolate, Neo4j driver Duyệt quan hệ
Nhi phân/serialized Không có (không truy vấn trực tiếp) protobuf-net, MessagePack Deserialize, sau đó LINQ
Đối tượng trong bộ nhớ LINQ to Objects Tích hợp trong .NET Hàm (lọc, map, reduce)

Đặt chúng cạnh nhau như thế này và mô hình từ Phần 1 lại trở nên rõ ràng: định dạng càng có cấu trúc và nhiều quan hệ (RDBMS, XML, đồ thị), ngôn ngữ gốc của nó càng trưởng thành và độc lập. Định dạng càng ít cấu trúc (CSV, nhị phân), “ngôn ngữ” thực sự chỉ là bước phân tích trên đường trở thành đối tượng — lúc đó LINQ tiếp quản.

Tiếp theo là gì

Bây giờ chúng ta có cả mô hình tinh thần (Phần 1) và tham chiếu thực tế (phần này) cho bối cảnh định dạng-ngôn ngữ trong .NET. Trong Phần 3, chúng ta sẽ quay lại LINQ cụ thể và hỏi nó hoạt động tốt thế nào như một lớp thống nhất xuyên suốt tất cả điều này — nơi nó thành công, nơi abstraction bị rò rỉ, và làm thế nào một định dạng mới hơn, vector embeddings và tìm kiếm tương đồng, đã buộc iteration tiếp theo của cùng mô hình này.

Đó là tất cả cho bây giờ. Mong rằng ý định của bạn rõ ràng và tâm trí bạn tĩnh lặng. Với lời chúc yên bình này, tôi đặt bút và trở về sự im lặng.

Tác giả: Bipin Joshi

Bipin Joshi là nhà tư vấn phần mềm độc lập, giảng viên và tác giả, chuyên về các công nghệ phát triển web Microsoft. Đón nhận lối sống yoga, ông cũng hướng dẫn chọn lọc cá nhân về Ajapa Gayatri và các thực hành thiền định liên quan. Kết hợp các kỷ luật của mã code và ý thức, ông đã thiền định, lập trình, viết và giảng dạy hơn 31 năm. Là tác giả sản xuất, ông chia sẻ những hiểu biết về cả phát triển phần mềm và trí tuệ yoga thông qua các trang web của mình.

Đăng ngày: 12 tháng 8 năm 2026

Phổ biến từ danh mục này
Hiểu về Access Modifiers trong VB.NET
Con trỏ và xử lý bộ nhớ không quản lý trong VB.NET
Ứng dụng N-Tier và .NET
Giới thiệu về .NET Reflection
Nén và giải nén tệp theo lập trình
Tạo Collection Classes trong C#
Lập trình hướng đối tượng với VB.NET và C#
Thực thi ứng dụng bên ngoài
Giới thiệu về .NET assemblies
Serialization XML bằng C#

Copyright © binaryintellect.net. Bảo lưu mọi quyền.

Đọc Copyright và Điều khoản sử dụng pháp lý

Được lưu trữ bởi DiscountASP.net

Facebook Twitter LinkedIn RSS