幾年前跟隨項目經理做的一個ERP小項目,自己業余時間整理的開發手冊,供參考。
開發環境配置:編程環境為Microsoft Visual Studio 2010,數據庫是SQL Server 2008 R2。設計架構Windows Forms+ .NET Remoting + SQL Server,所有程序的代碼量(框架,工具,業務邏輯)在5萬行以內。
1 SQL Server 數據庫表設計
設計供應商表Vendor, tb是通用前綴標識符號。FM是資金管理Finance Management。
CREATE TABLE [dbo].[tbFMVendor]( [Id] [bigint] IDENTITY(1,1) NOT NULL, [Code] [nvarchar](50) NULL, [Name] [nvarchar](50) NULL, [Description] [nvarchar](50) NULL, CONSTRAINT [PK_tbVendor] PRIMARY KEY CLUSTERED ( [Id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY]
iBatis框架要求每個表要以Id作為主鍵,類型為(C#:Int64, Sql:bigint)是個自增型。因為Id是唯一的,所以從表不需要設計多主鍵與主表關聯。
如果是從表(明細表),則需要添加對主表的Id列的外鍵引用。參考下面的腳本例子。
ALTER TABLE [dbo].[tbCustomerDiscBankAcct] ADD CONSTRAINT [FK_tbCustomerDiscBankAcct_tbCustomer] FOREIGN KEY ([IdCustomer]) REFERENCES [dbo].[tbCustomer] ([Id])
2 設計iBatis映射文件
添加Xml映射文件,放置於ErpMappingClass\Model\SqlMap目錄中,同時設置它的生成動作(Build Action)是嵌入式資源(Embedded Resource)
打開FMVendor.xml文件,增加內容如下所示,namespace的值為實體類型名稱
<sqlMap namespace="FMVendor" xmlns="http://ibatis.apache.org/mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <alias> <typeAlias alias="FMVendor" type="Erp.Model.FMVendor"/> </alias>
type的值為實體類型定義的完整名稱。
在Xml文件中增加不同的節(Element),用於SQL語句對數據的增刪查改。
數據操作語句 |
Xml片段寫法 |
讀取 |
<select id="Select" parameterClass="Hashtable" resultClass="FMVendor" > |
插入新數據 |
<insert id="Insert" parameterClass="FMVendor" resultClass="Erp.Model.BaseClass" > |
更新數據 |
<update id="Update" parameterClass="FMVendor" > |
刪除數據 |
<delete id="Delete" parameterClass="Hashtable"> delete $Tablename$ where Id=#Id# </delete> |
注意參數的寫法:ColumnName=#ColumnName# 以”#”表示列名對應的參數。
刪除數據的Xml代碼在BaseClass對應的Xml節中有配置,此處不用寫。
3 設計實體類型及數據增刪查改
實體類型的代碼根據Code Smith模板自動生成,同時加上Serializable以支持用於遠程調用時對象的可序列化。
3.1 根據數據庫表的列,生成實體類型定義
[Serializable] public class FMVendor: BaseClass { public string Code { get; set; } public string Name { get; set; } public string Description { get; set; } }
重寫BaseClass的tm_getTableName方法,返回實體映射的數據庫表名稱。
protected override string tm_getTableName() { return "tbFMVendor"; }
3.2 讀取一個實體對象(三個重載方法,前兩個方法傳入不同的參數調用第三個方法)
public static FMVendor getFMVendorById(Int64 _Id) { Hashtable htFilter = new Hashtable(); htFilter.Add("Id", _Id); return getFMVendorByFilter(htFilter); } public static FMVendor getFMVendorByCode(String _Code) { Hashtable htFilter = new Hashtable(); htFilter.Add("Code", _Code); return getFMVendorByFilter(htFilter); } public static FMVendor getFMVendorByFilter(Hashtable _htFilter) { FMVendor fmVendor = Select<FMVendor>(_htFilter); return fmVendor; }
3.3 讀取一個實體集合(兩個重載方法,前一個方法傳入空值調用第二個方法)
public static IList<FMVendor> getAllFormKind() { return getAllFormKindByFilter(null); } public static IList<FMVendor> getAllFormKindByFilter(Hashtable _htFilter) { IList<FMVendor> list = Global.QueryForList<FMVendor>("FMVendor.Select", _htFilter); return list; }
3.4 保存數據 保存前做數據驗證,驗證信息以DataResult對象傳回
public static DataResult saveFMVendor(ref FMVendor _fmVendor, String _userCode) { DataResult result = new DataResult(false); if (string.IsNullOrEmpty(_fmVendor.Code)) { result.Succeed = false; result.Message = "請輸入供應商類型代碼! "; return result; } if (string.IsNullOrEmpty(_fmVendor.Name)) { result.Succeed = false; result.Message = "請輸入供應商類型名稱! "; return result; } //數據校效 User user = User.getUserByCode(_userCode); FMVendor fmVendor = getFMVendorById(_fmVendor.Id); if (fmVendor == null) { _fmVendor.Id = 0; } FMVendor formkindExist = getFMVendorByCode(_fmVendor.Code); if (formkindExist != null && formkindExist.Id != _fmVendor.Id) { result.Succeed = false; result.Message = "供應商代碼重復,請重新輸入一個新代碼!"; return result; } try { Global.BeginTransaction(); _fmVendor.Save(); Global.CommitTransaction(); result.Succeed = true; return result; } catch (Exception ex) { Global.RollBackTransaction(); result.Succeed = false; result.Message = ex.Message; return result; } }
3.5 刪除數據
public static String deleteFMVendor(Int64 vendorId) { DataResult result = new DataResult(); try { FMVendor fmVendor = GsctErp.Model.FMVendor.getFMVendorById(vendorId); if (fmVendor != null) { fmVendor.Delete(); } return "刪除成功 ! "; } catch (Exception ex) { return "刪除失敗 ! " + ex.Message; } }
FMVendor.xml文件中不需要寫SQL刪除數據語句,基類型BaseClass中已經有刪除方法實現。
4 設計遠程對象(.NET Remoting)
遠程對象需要公開相應的數據訪問方法給客戶端界面調用。
public class NroFMVendor: BaseClass { }
遠程對象命名規則在原有的對象名稱前加Nro前綴,並繼承於BaseClass,它的方法是對實體對象的數據訪問的封裝,每個方法的第一行是用戶驗證代碼。
public FMVendor getFMVendorById(Int64 _Id) { User user = ValidateIdentity(); return FMVendor.getFMVendorById(_Id); } public FMVendor getFMVendorByCode(string Code) { User user = ValidateIdentity(); return FMVendor.getFMVendorByCode(Code); } public DataResult saveFMVendor(ref FMVendor vendor, String _userCode) { User user = ValidateIdentity(); return FMVendor.saveFMVendor(ref vendor, _userCode); }
修改類型Erp.Model. FlexFactory,在該類型中增加私有靜態變量
增加公共屬性,以用於客戶端的界面訪問,需要增加的代碼如下所示
public class FlexFactory { private static NroFMVendor nroFMVendor; public static NroFMVendor myNroFMVendor { get { if (nroFMVendor == null) nroFMVendor = ActivatorGetObject<NroFMVendor>(); return nroFMVendor; } } }
修改類型Erp.Model.FlextFactory的RegisterService方法,公開遠程服務。
public class FlexFactory { RemotingConfigurationRegisterWellKnownServiceType<NroFMVendor>();
5 界面開發
在項目中增加窗體FrmDtlVendor.cs,繼承於FrmDtlBase。
public partial class FrmDtlVendor : FrmDtlBase { public FrmDtlVendor() { InitializeComponent(); } private FMVendor _vendor; public void setVendor(FMVendor vendor) { _vendor = vendor; }
它是對單筆數據進行操作,界面如下所示
ERP系統中預定義的窗體基類型列表如下,可根據業務需要繼承。
類型名稱 |
用途 |
FrmLstBase |
以列表形式呈現數據 |
FrmDtlBase |
單筆數據的編輯(增刪查改)操作 |
FrmSchClass |
數據搜索窗體 |
FrmRptFlt |
報表參數值選擇 |
FrmScnClass |
查詢方案 |
FrmImpBase |
數據導入 |
回到FmVendorDtl窗體中,在OnLoad方法加載數據。
protected override void OnLoad(EventArgs e) { this.Text = SysParam.ClientSysTitle; setRight(); FMVendor fmVendor = _vendor == null ? null : GsctFactory.myNroFMVendor.getFMVendorById(_vendor.Id); if (fmVendor == null) clearForm(); else showVendor(fmVendor); bindProfile(); this.CenterToParent(); }
有二種方法啟動這個窗體,當從List列表進入時,根據傳入的對象值加載數據,並綁定到界面控件中,同時設置權限,對控件進行隱藏或是禁用處理。
增加數據保存代碼,示例方法如下所示
private void tsbSave_Click(object sender, EventArgs e) { DataResult result = saveVendor(); if (result.Succeed) { showVendor(_vendor); MessageBox.Show("保存成功!"); } else { MessageBox.Show("保存失敗!\r\n" + result.Message); } }
saveVendor方法中的對象保存代碼是調用遠程對象的方法,片段如下
try { vendor.Code = txtCode.Text.Trim(); vendor.Name = txtName.Text; result = GsctFactory.myNroFMVendor.saveFMVendor(ref vendor, User.currUser.Code); if (!result.Succeed)
throw new Exception(result.Message); } catch (Exception ex) { result.Message = ex.Message; return result; }
項目總結
1 iBatis是輕量型ORM,可以將SQL語句返回的結果綁定到對象實體,不過手寫SQL語句和增加實體定義文件這兩步需要開發人員自己完成。所以需要另外開發Code Smith模板生成代碼。
2 項目很小,僅限於公司內部員工使用,欠缺很多商業性ERP的特性,歡迎批評指正。