Xem Cập nhật của tôi ở phía dưới để biết thêm.
Thỉnh thoảng tôi có các dự án mà tôi phải xuất một số dữ liệu dưới dạng tệp Excel (định dạng xlsx). Quá trình thường là:
Người dùng nhấp vào một số nút trong ứng dụng của tôi
Mã của tôi chạy truy vấn DB và xử lý kết quả bằng cách nào đó
Mã của tôi tạo tệp * .xlsx bằng thư viện xen kẽ Excel com hoặc một số thư viện của bên thứ ba (ví dụ: Aspose.Cells)
Tôi có thể dễ dàng tìm thấy các ví dụ mã cho cách thực hiện điều này trực tuyến, nhưng tôi đang tìm kiếm một cách mạnh mẽ hơn để làm điều này. Tôi muốn mã của tôi tuân theo một số nguyên tắc thiết kế để đảm bảo rằng mã của tôi có thể duy trì và dễ hiểu.
Đây là nỗ lực ban đầu của tôi để tạo tệp xlsx trông như sau:
var wb = new Workbook();
var ws = wb.Worksheets[0];
ws.Cells[0, 0].Value = "Header";
ws.Cells[1, 0].Value = "Row 1";
ws.Cells[2, 0].Value = "Row 2";
ws.Cells[3, 0].Value = "Row 3";
wb.Save(path);
Ưu điểm: Không nhiều. Nó hoạt động, vì vậy đó là tốt.
Nhược điểm:
- Tham chiếu ô được mã hóa cứng, vì vậy tôi có các số ma thuật rải rác trong mã của mình.
- Thật khó để thêm hoặc xóa các cột và hàng mà không cập nhật nhiều tham chiếu ô.
- Tôi cần học một số thư viện của bên thứ ba. Một số thư viện được sử dụng như các thư viện khác, nhưng vẫn có thể có vấn đề. Tôi gặp vấn đề trong đó các thư viện com interop sử dụng tham chiếu ô dựa trên 1 trong khi Aspose.Cells sử dụng tham chiếu ô dựa trên 0.
Đây là một giải pháp giải quyết một số nhược điểm tôi liệt kê ở trên. Tôi muốn coi một bảng dữ liệu là đối tượng của chính nó có thể được di chuyển và thay đổi mà không cần đào sâu vào thao tác tế bào và làm phiền các tham chiếu ô khác. Đây là một số mã giả:
var headers = new Block(new string[] { "Col 1", "Col 2", "Col 3" });
var body = new Block(new string[,]
{
{ "Row 1", "Row 1", "Row 1" },
{ "Row 2", "Row 2", "Row 2" },
{ "Row 3", "Row 3", "Row 3" }
});
body.PutBelow(headers);
Là một phần của giải pháp này, tôi sẽ có một số đối tượng BlockEngine lấy một khối Chứa và thực hiện các thao tác ô cần thiết để xuất dữ liệu dưới dạng tệp * .xlsx. Một đối tượng Block có thể có định dạng kèm theo nó.
Ưu điểm:
- Điều này loại bỏ hầu hết các số ma thuật mà mã ban đầu của tôi có.
- Điều này ẩn rất nhiều mã thao tác ô, mặc dù thao tác ô vẫn được yêu cầu trong đối tượng BlockEngine mà tôi đã đề cập.
- Việc thêm và xóa hàng dễ dàng hơn nhiều mà không ảnh hưởng đến các phần khác của bảng tính.
Nhược điểm:
- Vẫn còn khó để thêm hoặc xóa cột. Nếu tôi muốn trao đổi vị trí của cột hai và ba, tôi phải trao đổi trực tiếp nội dung ô. Trong trường hợp này, đó là tám lần chỉnh sửa và do đó có tám cơ hội để phạm sai lầm.
- Nếu tôi có bất kỳ định dạng nào cho hai cột đó, tôi cũng phải cập nhật.
- Giải pháp này không hỗ trợ vị trí khối ngang; Tôi chỉ có thể đặt một khối bên dưới một khối khác. Chắc chắn tôi có thể có
tableRight.PutToRightOf(tableLeft)
, nhưng điều đó sẽ gây ra vấn đề nếu tableRight và tableLeft có số lượng hàng khác nhau. Để đặt các bảng, động cơ sẽ phải nhận thức được mọi bảng khác. Điều này có vẻ phức tạp không cần thiết với tôi. - Tôi vẫn cần học mã của bên thứ ba, mặc dù thông qua một lớp trừu tượng thông qua các đối tượng Block và BlockEngine, mã sẽ được kết hợp chặt chẽ hơn với thư viện của bên thứ ba so với nỗ lực ban đầu của tôi. Nếu tôi muốn hỗ trợ nhiều tùy chọn định dạng khác nhau theo cách liên kết lỏng lẻo, có lẽ tôi phải viết rất nhiều mã; BlockEngine của tôi sẽ là một mớ hỗn độn lớn.
Đây là một giải pháp có một lộ trình khác nhau. Đây là quá trình:
Tôi lấy dữ liệu báo cáo của mình và tạo tệp xml theo một số định dạng mà tôi chọn.
Sau đó, tôi sử dụng một phép chuyển đổi xsl để chuyển đổi tệp xml thành tệp Bảng tính XML của Excel 2003.
Từ đó tôi chỉ cần chuyển đổi Bảng tính xml thành tệp xlsx bằng thư viện của bên thứ ba.
Tôi tìm thấy trang này mô tả một quy trình tương tự và bao gồm các ví dụ mã.
Ưu điểm:
- Giải pháp này đòi hỏi hầu như không có thao tác tế bào. Thay vào đó, bạn sử dụng xsl / xpath để thực hiện các thao tác của mình. Để hoán đổi hai cột trong một bảng, bạn di chuyển toàn bộ các cột trong tệp xsl không giống như các giải pháp khác của tôi sẽ yêu cầu hoán đổi ô.
- Mặc dù bạn vẫn cần một thư viện của bên thứ ba có thể chuyển đổi Bảng tính XML của Excel 2003 thành tệp xlsx, nhưng đó là về tất cả những gì bạn cần thư viện. Số lượng mã bạn cần viết sẽ gọi vào thư viện bên thứ ba là rất nhỏ.
- Tôi nghĩ giải pháp này là dễ hiểu nhất và đòi hỏi ít mã nhất.
- Mã tạo dữ liệu theo định dạng xml của riêng tôi sẽ đơn giản.
- Tệp xsl sẽ chỉ phức tạp vì Bảng tính XML của Excel 2003 phức tạp. Tuy nhiên, thật dễ dàng để kiểm tra đầu ra của tệp xsl: chỉ cần mở đầu ra trong Excel và kiểm tra các thông báo lỗi.
- Thật dễ dàng để tạo các tệp Bảng tính XML Excel 2003 mẫu: chỉ cần tạo một bảng tính trông giống như tệp xlsx mong muốn của bạn, sau đó lưu nó dưới dạng Bảng tính XML của Excel 2003.
Nhược điểm:
- Bảng tính XML Excel 2003 không hỗ trợ một số tính năng nhất định. Bạn không thể tự động độ rộng cột chẳng hạn. Bạn không thể bao gồm hình ảnh trong tiêu đề hoặc chân trang. Nếu bạn định xuất tệp xlsx kết quả sang pdf, bạn không thể đặt dấu trang pdf. (Tôi đã hack cùng một bản sửa lỗi cho việc này bằng cách sử dụng các nhận xét di động.). Bạn phải làm điều này bằng thư viện bên thứ ba của bạn.
- Yêu cầu một thư viện hỗ trợ Bảng tính XML Excel 2003.
- Sử dụng định dạng tệp MS Office 11 tuổi.
Lưu ý: Tôi nhận ra rằng các tệp xlsx thực sự là các tệp zip chứa các tệp xml, nhưng định dạng xml có vẻ quá phức tạp đối với mục đích của tôi.
Cuối cùng, tôi đã xem xét các giải pháp liên quan đến SSRS, nhưng dường như quá phình to cho mục đích của tôi.
Quay lại câu hỏi ban đầu của tôi, mẫu thiết kế tốt để tạo tệp Excel trong mã là gì?. Tôi có thể nghĩ ra một vài giải pháp, nhưng dường như không có giải pháp nào phù hợp. Mỗi cái đều có nhược điểm.
Cập nhật: Vì vậy, tôi đã thử cả giải pháp BlockEngine và giải pháp Bảng tính XML của mình để tạo các tệp XLSX tương tự. Dưới đây là ý kiến của tôi về họ:
Giải pháp BlockEngine:
- Điều này chỉ đơn giản là yêu cầu quá nhiều mã xem xét các lựa chọn thay thế.
- Tôi thấy quá dễ dàng để ghi đè một khối với khối khác nếu tôi có sai lệch.
- Ban đầu tôi đã nói rằng định dạng có thể được đính kèm ở cấp độ khối. Tôi thấy điều này không tốt hơn nhiều so với việc định dạng riêng biệt với nội dung khối. Tôi không thể nghĩ ra một cách hay để kết hợp nội dung và định dạng. Tôi cũng không thể tìm ra một cách tốt để giữ chúng tách biệt. Nó chỉ là một mớ hỗn độn.
Giải pháp bảng tính XML:
- Bây giờ tôi sẽ sử dụng giải pháp này.
- Nó lặp đi lặp lại rằng giải pháp này đòi hỏi ít mã hơn. Tôi đang thay thế BlockEngine một cách hiệu quả bằng chính Excel. Tôi vẫn cần hack cho các tính năng như dấu trang và ngắt trang.
- Định dạng Bảng tính XML rất khó, nhưng thật dễ dàng để thực hiện một thay đổi nhỏ và so sánh kết quả với một tệp hiện có trong chương trình Diff yêu thích của bạn. Và một khi bạn tìm ra một số đặc điểm riêng, bạn có thể đặt nó vào vị trí và quên nó từ đó.
- Tôi vẫn lo ngại rằng giải pháp này dựa trên định dạng tệp Excel cũ hơn.
- Tệp XSLT mà tôi đã tạo rất dễ làm việc. Xử lý định dạng ở đây đơn giản hơn nhiều so với giải pháp BlockEngine.