Mẫu thiết kế tốt để tạo tệp Excel (xlsx) trong mã là gì?


12

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à:

  1. Người dùng nhấp vào một số nút trong ứng dụng của tôi

  2. Mã của tôi chạy truy vấn DB và xử lý kết quả bằng cách nào đó

  3. 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:

  1. 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.

  2. 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.

  3. 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.

Câu trả lời:


7

Nếu bạn thực sự muốn thứ gì đó phù hợp với mình, thì tôi khuyên bạn nên làm quen với ý tưởng "phức tạp không cần thiết" ... đó là bản chất của việc xử lý các định dạng tệp Microsoft Office.

Tôi (sắp xếp) giống như ý tưởng của bạn về "các khối" ... Tôi sẽ tạo các đối tượng khối được phân lớp phụ, như Bảng, với Cột và Hàng độc lập với khái niệm các ô. Sau đó sử dụng công cụ khối của bạn để chuyển đổi các tệp này thành các tệp XSLS.

Tôi đã sử dụng SDK OpenXML thành công trong quá khứ, nhưng đừng cố đọc tài liệu và bắt đầu lại từ đầu. Thay vào đó, hãy tạo một bản sao chính xác trong Excel về những gì bạn muốn, lưu nó và kiểm tra nó bằng công cụ Reflector Tài liệu được cung cấp. Nó sẽ cung cấp cho bạn mã C # bạn cần để tạo tài liệu, sau đó bạn có thể học hỏi và sửa đổi.


Tài liệu văn phòng KHÔNG "phức tạp không cần thiết" - chúng đang thực hiện hoặc cho phép một loạt các hoạt động, định dạng, chức năng, v.v.
warren

5
Tôi không tranh luận rằng bản thân các định dạng tệp phức tạp không cần thiết nhiều như tôi đang tranh luận rằng làm việc với chúng là. Chẳng hạn, sử dụng SDK OpenXML, yêu cầu bạn biết thứ tự ma thuật để thêm các phần tử ... ví dụ, thêm bố cục trang chiếu vào bản trình bày, không hoạt động. Bạn phải thêm nó vào slide trước, sau đó đến phần trình bày. Tại sao? Bởi vì Microsoft đã mã hóa các thư viện theo cách đó. Có rất nhiều tài liệu tham khảo tròn kỳ lạ để quản lý, quá. Tôi hiểu định dạng cần sự phức tạp, nhưng làm việc với nó không nên quá đau đớn.
mgw854

3

Đây là một giải pháp tôi đã sử dụng thường xuyên trong quá khứ:

  • tạo một tài liệu Excel thông thường (thường ở định dạng xlsx) dưới dạng mẫu, chứa tất cả các tiêu đề cột, bao gồm tiêu đề và định dạng mặc định cho các cột và có thể định dạng cho các ô tiêu đề.

  • nhúng mẫu đó vào tài nguyên của chương trình của bạn. Trong thời gian chạy, bước đầu tiên là trích xuất mẫu dưới dạng tệp mới và đặt nó vào thư mục đích

  • sử dụng Interop hoặc thư viện của bên thứ ba để điền dữ liệu vào xlsx mới được tạo. Không tham khảo số cột được mã hóa cứng, thay vào đó hãy sử dụng một số siêu dữ liệu (ví dụ: tiêu đề cột) để xác định các cột chính xác.

Ưu điểm:

  • một cái gì đó giống như cách tiếp cận Block của bạn bây giờ hoạt động tốt hơn. Ví dụ: hoán đổi cột: không cần thay đổi bất cứ điều gì trong mã khối của bạn, vì các cột chính xác được xác định bởi các tiêu đề của chúng

  • miễn là các cột của bạn có định dạng duy nhất, hầu hết các định dạng có thể được thực hiện trực tiếp trong Excel, bằng cách thao tác với mẫu của bạn. Điều đó mang lại cho bạn cảm giác WYSIWYG, cùng với quyền tự do sử dụng bất kỳ tùy chọn định dạng nào có sẵn trong Excel mà không cần phải viết mã cho nó

Nhược điểm:

  • bạn vẫn cần sử dụng lib hoặc Interop của bên thứ ba. Tôi đã đề cập rằng Interop là chậm?

  • khi các tiêu đề cột thay đổi trong mẫu của bạn, bạn cũng cần điều chỉnh mã của mình (nhưng điều đó có thể dễ dàng phát hiện bằng cách có một thói quen xác nhận sẽ báo hiệu nếu thiếu các cột dự kiến)

  • Khi bạn cần định dạng động của các ô khác nhau trong cùng một cột, bạn vẫn phải xử lý mã đó trong mã

Như một gợi ý chung, bất kỳ cách tiếp cận nào bạn chọn: nó có lợi thế để tách bố cục khỏi nội dung và sử dụng các giải pháp khai báo.


0

Có hai điều cần xem xét:

  • Độ phức tạp của việc tạo tệp theo định dạng đã cho
  • Tính nhạy cảm của mã bị phá vỡ khi cấu trúc nội dung của tệp cần thay đổi.

Về thứ nhất:

Nếu bảng tính bạn cần tạo không chứa bất kỳ định dạng hoặc công thức nào , thì bạn hoàn toàn dễ dàng tạo tệp CSV hoặc Tab được phân cách bằng thẻ thay vì XLSX thực tế. Excel mở các tệp này, thường theo mặc định trên nhiều PC. Điều này sẽ không giúp bạn mã hóa cứng quanh các cột và hàng nhưng nó sẽ giúp bạn tiết kiệm thêm công việc thao tác mô hình đối tượng Excel.

Nếu bạn cần định dạng hoặc công thức, thì làm việc với mô hình đối tượng Excel là một cách hợp lý, đặc biệt là nếu bạn xây dựng một bảng tính không quá "mã hóa cứng". Nói cách khác, nếu bảng tính của bạn sử dụng các công thức tương đối và tên phạm vi phù hợp thì nó có thể đi cùng với việc mã hóa các số ma thuật ít khó hơn.

Về thứ hai:

Bạn có thể làm việc thông qua từng ô với các tham chiếu hàng và cột được mã hóa cứng hoặc bạn có thể làm việc với các mảng / Bộ sưu tập danh sách và forcác vòng lặp để tổng quát hóa dân số của các ô.


Tôi không rõ ràng trong câu hỏi ban đầu của mình rằng tôi muốn kiểm soát các tùy chọn định dạng và in ấn và như vậy trong giải pháp của mình. Liên quan đến điểm thứ hai, tôi nghĩ rằng những gì bạn đang đề cập đến là những gì tôi mô tả trong BlockEnginegiải pháp của mình . Tôi có thể lấy một cái IList<IBusinessObject>và nhổ ra một Blockvật. Những ưu và nhược điểm vẫn sẽ giống nhau.
2023861
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.