解析大型.NET ERP系統 設計通用Microsoft Excel導入功能


做企業管理軟件很難避免與Microsoft Excel打交道,常常是軟件做好了,客戶要求說再做一個Excel導入功能。導入Excel數據的功能的難度不大,從Excel列數據欄位的取值,驗證值,再導入到數據庫表中。然而一直是在做重復工作,寫過不計其數的Excel導入程序,每次只是滿足於問題解決,后來終於找到一個方法,實現通用的Excel數據導入。

設計通用的Excel導入功能,第一個實現要求是不能依賴Excel,客戶的電腦或服務器很有可能沒有安裝Excel,所以微軟的Office Interop一概不考慮。第二個實現要求是需要高度抽象化,也就是不依賴於具體的數據庫表,這樣實現了從具體表導入到通用表導入的抽象,可重復用的程度高。

第一步是生成Excel模板文件,先看字段選擇界面,傳入一個數據庫表,可枚舉表的字段,供生成Excel模板文件:

image

這個功能的作用是生成Excel文件,供用戶輸入數據。因為表名是由不同的功能窗體傳遞過來,實現了通用化的第一步。

生成的Excel文件,再加上我們要填寫的數據,參考下面的表格。

Department.Dept Department.Description
R&D 開發部
CAM 編程
CNC 計算機鑼
EDM 火花機
WC 線切割
SG 磨床
OM 其它加工
PO 打光
QC 質量控制
ASSM 模具裝配
INJE 注塑部
AM 行政及管理

看Excel的表頭,它包含字段定義,字段前面有加表,這樣可支持主從表導入。

 

第二步是在上面的步驟生成的Excel文件中輸入數據,再到這個界面中點擊Import即可完成數據導入。

先來看一下,如何調用這個通用的導入界面功能:

   protected override void SetupImportTemplate(EntityImportArgument argument)
        {
            base.SetupImportTemplate(argument);

            List<string> columnsList;

            // EntityManager
            argument.EntityManager = this._departmentManager;

            // EntityName            
            argument.RootEntity = EntityType.DepartmentEntity;

            #region HiddenColumns

            #endregion

            #region Required Columns, columns must be selected as export columns.

            #region Item
            columnsList = new List<string>();
            columnsList.Add(DepartmentFields.Dept.Name);
            columnsList.Add(DepartmentFields.Description.Name);

            argument.RequiredColumns.Add(EntityType.DepartmentEntity, columnsList);
            #endregion

            #endregion
        }
 
         
        

先設計好上面要調用接口,代碼中解釋了以下幾個重要的方法:

1  要導入表  ORM映射的好處是根據實體可以找到它映射的表,根據表也可以找到它映射的實體。要跑數據驗證,必須通過實體的驗證類來實現,這樣節省了很多驗證代碼。

2  保存表的方法 Manager類實現了把Excel數據保存到數據庫中,對於這樣通用的結構,保存方法也必須要求方法名稱高度一致,比如表名是SalesOrder,它映射的實體名稱是SalesOrderEntity,則對應的保存方法一定是SaveSalesOrder,這是調用時的契約,由系統強制約定。

3 值驗證 如果數據庫沒有做強制要求輸入(可空null),但是邏輯上要求一定要輸入值,則需要跑實體驗證。

 

數據導入使用的的第三方類庫是Infragistics Excel,首先打開Excel文件,讀取第一行字段名和相應的值。

using Infragistics.Documents.Excel;

// Load workbook with data
Workbook workbook = Workbook.Load("department.xls");
Worksheet worksheet = workbook1.Worksheets["Sheet1"];

string columnName=worksheet.Rows[0].Cells[0].Value;
 
 

構造一個內存DataSet,根據第一列的字段定義,構造表結構,例如上面的Excel文件表格,我們可以構造如下表

Department(Dept,Description) 

從Excel文件中我們只能獲取字段名稱信息,還需要連接到數據庫中,獲取字段的類型信息。

SELECT * FROM sys.columns WHERE object_id=OBJECT_ID('Department')

根據這個查詢,完善前面的表定義結構,類似於這樣。

Department(Dept nvarchar(8),Description nvarchar(40))

這個時候就可以做基本的數據類型驗證了,比如表字段的類型是數字,但Excel中的值是字符串,可拋出異常。

 

通過了基本的類型驗證之后,我們還需要做邏輯驗證,比如數據庫中已經定義了R&D部門,第二次執行又插入一筆R&D的部門編碼時,需要及時拋出異常,這種業務邏輯上的驗證,借助於ORM框架的功能實現。

LLBL Gen Pro提供的實體類型定義,都匹配有一個驗證類型,當發生值改變前,數據保存前或是刪除前都可以跑驗證,我們將這種復雜的驗證邏輯通過實體調用來完成。

根據前面Department表的定義 ,查找系統元數據可知道它映射的實體是DepartmentEntity,我們根據數據表的值記錄,構造DepartmentEntity,上面表格中的Excel數據有多行記錄,則構造一個List<DepartmentEntity>,借助於反射,把內存數據庫DataTale中的字段值(DataRow行)轉化為實體(Entity對象)。網上有很多關於DataTable與List<Entity>轉化的例子代碼。

我們在保存Department方法前,主動調用Department的驗證類型:

DepartmentValidator validator = new DepartmentValidator();
validator.ValidateRequiredFields(department);

最后,調用EntityManager接口中的Save方法即可:

ReflectionHelper.InvokeMethod(entityManager,”SaveDepartment”, typeof(DepartmentEntity), _department);
 

 

這個過程中,所操作的數據庫對象是通過接口完成,實現了可擴展性,實際應用中還可導入主從表數據,需要加關聯行關聯Excel的每行數據之間的關系。

目前還沒有實現導入三層表數據。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM