ERP開發中有大量的代碼是可以用代碼生成器來生成。選擇代碼生成器有二種思路
- .NET代碼開發 優點是可集成到ERP開發工具中,定制化的開發,生成的代碼有爭對性
- 使用第三方的工具,比如Code Smith或是T4,優點是借助於模板生成,靈活性高。缺點是要推廣技術的話,相應的代碼生成器也要熟悉,並且會有版權的問題。
ERP程序中,有四種類型的代碼,可借助於Code Smith來生成。
-
Interface 數據訪問接口
-
接口與實現分離,可增加系統的靈活性。接口代碼的例子如下
using System.Collections.Generic; using System.Data; using System.Text; using SD.LLBLGen.Pro.ORMSupportClasses; using Foundation; using Foundation.FactoryClasses; using Foundation.EntityClasses; using Foundation.HelperClasses; using Foundation.InterfaceClasses; using Foundation.DatabaseSpecific; namespace Foundation.InterfaceClasses { public interface ICompanyManager { CompanyEntity GetCompany(System.String Companycode); CompanyEntity GetCompany(System.String Companycode,IPrefetchPath2 prefetchPath); CompanyEntity GetCompany(System.String Companycode,IPrefetchPath2 prefetchPath,ExcludeIncludeFieldsList fieldList); EntityCollection GetCompanyCollection(IRelationPredicateBucket filterBucket); EntityCollection GetCompanyCollection(IRelationPredicateBucket filterBucket,ISortExpression sortExpression); EntityCollection GetCompanyCollection(IRelationPredicateBucket filterBucket,ISortExpression sortExpression, IPrefetchPath2 prefetchPath); EntityCollection GetCompanyCollection(IRelationPredicateBucket filterBucket, ISortExpression sortExpression, IPrefetchPath2 prefetchPath, ExcludeIncludeFieldsList fieldList); CompanyEntity SaveCompany(CompanyEntity company); CompanyEntity SaveCompany(CompanyEntity company ,EntityCollection entitiesToDelete); CompanyEntity SaveCompany(CompanyEntity company, EntityCollection entitiesToDelete, string seriesCode); void DeleteCompany(CompanyEntity company); bool IsCompanyExist(System.String Companycode); bool IsCompanyExist(IRelationPredicateBucket filterBucket); int GetCompanyCount(IRelationPredicateBucket filterBucket); CompanyEntity CloneCompany(System.String Companycode); void PostCompany(System.String Companycode); void PostCompany(CompanyEntity company); } }
Code Smith基於模板生成代碼,把你需要實現的代碼做成模板,再替換需要改變的值,很輕松的就完成模板編程。
Implementation 數據訪問實現
針對接口,實現對應的接口方法。
using System; using System.Collections.Generic; using System.Data; using System.Text; using SD.LLBLGen.Pro.ORMSupportClasses; using Foundation; using Foundation.FactoryClasses; using Foundation.EntityClasses; using Foundation.HelperClasses; using Foundation.InterfaceClasses; using Foundation.DatabaseSpecific; using Foundation.Managers; using Foundation.Common; namespace Foundation.Managers { public class CompanyManager : Foundation.Common.ManagerBase, ICompanyManager { public CompanyEntity GetCompany(System.String Companycode) { return GetCompany(Companycode,null); } public CompanyEntity GetCompany(System.String Companycode,IPrefetchPath2 prefetchPath) { return GetCompany(Companycode,prefetchPath,null); } public CompanyEntity GetCompany(System.String Companycode,IPrefetchPath2 prefetchPath,ExcludeIncludeFieldsList fieldList) { CompanyEntity _Company=new CompanyEntity(Companycode); using (DataAccessAdapterBase adapter=GetCompanyDataAccessAdapter()) { bool found=adapter.FetchEntity(_Company, prefetchPath, null, fieldList); if (!found) throw new Foundation.Common.RecordNotFoundException("Invalid Company"); } return _Company; } public EntityCollection GetCompanyCollection(IRelationPredicateBucket filterBucket) { return GetCompanyCollection(filterBucket,null); } public EntityCollection GetCompanyCollection(IRelationPredicateBucket filterBucket,ISortExpression sortExpression) { return GetCompanyCollection(filterBucket,sortExpression,null); } public EntityCollection GetCompanyCollection(IRelationPredicateBucket filterBucket,ISortExpression sortExpression, IPrefetchPath2 prefetchPath) { return GetCompanyCollection(filterBucket,sortExpression,prefetchPath,null); } public EntityCollection GetCompanyCollection(IRelationPredicateBucket filterBucket, ISortExpression sortExpression, IPrefetchPath2 prefetchPath, ExcludeIncludeFieldsList fieldList) { EntityCollection CompanyCollection =new EntityCollection(new CompanyEntityFactory()); using (DataAccessAdapterBase adapter =GetCompanyDataAccessAdapter()) { adapter.FetchEntityCollection(CompanyCollection, filterBucket, 0,sortExpression, prefetchPath, fieldList); } return CompanyCollection ; } public CompanyEntity SaveCompany(CompanyEntity Company) { return SaveCompany(Company,null); } public CompanyEntity SaveCompany(CompanyEntity Company ,EntityCollection entitiesToDelete) { return SaveCompany(Company,entitiesToDelete,string.Empty); } public CompanyEntity SaveCompany(CompanyEntity Company, EntityCollection entitiesToDelete, string seriesCode) { using (DataAccessAdapterBase adapter =GetCompanyDataAccessAdapter()) { try { adapter.StartTransaction(IsolationLevel.ReadCommitted, "SaveCompany"); adapter.SaveEntity(Company, true, false); adapter.Commit(); } catch { adapter.Rollback(); throw; } } return Company; } public void DeleteCompany(CompanyEntity Company) { using (DataAccessAdapterBase adapter =GetCompanyDataAccessAdapter()) { if (!adapter.IsEntityExist<CompanyEntity>(Company)) return; try { adapter.StartTransaction(IsolationLevel.ReadCommitted, "DeleteCompany"); adapter.DeleteEntity(Company); adapter.Commit(); } catch { adapter.Rollback(); throw; } } } public bool IsCompanyExist(System.String Companycode) { RelationPredicateBucket filterBucket = new RelationPredicateBucket(); filterBucket.PredicateExpression.Add(CompanyFields.Companycode==Companycode); return IsCompanyExist(filterBucket); } public bool IsCompanyExist(IRelationPredicateBucket filterBucket) { return (GetCompanyCount(filterBucket) > 0); } public int GetCompanyCount(IRelationPredicateBucket filterBucket) { using (DataAccessAdapterBase adapter =GetCompanyDataAccessAdapter()) { return adapter.GetDbCount<CompanyEntity>(filterBucket); } } public CompanyEntity CloneCompany(System.String Companycode) { CompanyEntity source = this.GetCompany(Companycode); CompanyEntity _Company = (CompanyEntity)CloneEntity(source); return _Company; } public void PostCompany(System.String Companycode) { return; } public void PostCompany(CompanyEntity Company) { return; } } }
有幾個要注意的地方:
1 重載方法 滿足不同的接口的需求。我這里實現的接口,相當於通用的接口方法,可以滿足今后各種需求。
舉例說明,增加一個字段,接口和實現不需要作任何改變。如果是只讀取被增加的字段值,請傳入參數ExcludeIncludeFieldsList以作為需要讀取的值。
減少字段也不需要作任何的變更;如果刪除了表,刪除相應的Interface類型和Manager類型即可。
2 所有的CRUD的接口方法,均已經實現。
Create,Update => Save接口
Remove => Delete接口
UI 界面層代碼生成
private ICompanyManager _CompanyManager = null ; private CompanyEntity _Company = null ; protected override void OnLoad(EventArgs e) { if(!DesignMode) this._CompanyManager = ClientProxyFactory.CreateProxyInstance<ICompanyManager>(); base.OnLoad(e); } protected override void InitNavigator(InitNavigatorArgs args) { base.InitNavigator(args); args.SortExpression.Add(CompanyFields.Companycode | SortOperator.Ascending); } protected override EntityBase2 LoadData(Dictionary<string, string> refNo) { base.LoadData(refNo); string Companycode=string.Empty; if(refNo.TryGetValue("Companycode", out Companycode)) { IPrefetchPath2 prefetchPath = new PrefetchPath2((int)EntityType.CompanyEntity); _Company = _CompanyManager.GetCompany(Companycode,prefetchPath); } else { _Company = new CompanyEntity(); } return _Company; } protected override void BindControls(EntityBase2 entity) { base.BindControls(entity); this.currencyBindingSource.DataSource = entity; } protected override EntityBase2 Add( ) { base.Add(); this._Company = new CompanyEntity(); return _Company; } protected override EntityBase2 Save(EntityBase2 entityToSave, EntityCollectionNonGeneric entitiesToDelete, string SeriesCode) { CompanyEntity _Company = (CompanyEntity)entityToSave; this._Company =this._CompanyManager.SaveCompany(_Company); return _Company; } protected override void Delete(EntityBase2 entityToDelete) { base.Delete(entityToDelete); CompanyEntity Company = (CompanyEntity)entityToDelete; this._CompanyManager.DeleteCompany(Company); } protected override object Clone(Dictionary<string, string> refNo) { base.Clone(refNo); string ccy = string.Empty; refNo.TryGetValue("Ccy", out ccy); return null; } protected override void ReleaseResources( ) { base.ReleaseResources(); try { _Company = null; _CompanyManager= null; } catch { } }
界面層的增刪查改接口,數據綁定接口都已經生成。需要修改的地方,則是定制化代碼的區域。比如增加一個公司時,它的創建日期應該為當前系統時間,創建人應該是當前登陸系統的用戶。
Validation 驗證代碼
using System; using System.Collections.Generic; using System.Data; using System.Text; using SD.LLBLGen.Pro.ORMSupportClasses; using Foundation; using Foundation.FactoryClasses; using Foundation.EntityClasses; using Foundation.HelperClasses; using Foundation.InterfaceClasses; using Foundation.DatabaseSpecific; namespace Foundation.ValidatorClasses { [Serializable] public partial class CompanyEntityValidator : ValidatorBase { public override void ValidateEntityBeforeSave(IEntityCore involvedEntity) { base.ValidateEntityBeforeSave(involvedEntity); CompanyEntity company = (CompanyEntity)involvedEntity; if (company.IsNew) { } } public override void ValidateEntityBeforeDelete(IEntityCore involvedEntity) { base.ValidateEntityBeforeDelete(involvedEntity); CompanyEntity company = (CompanyEntity)involvedEntity; } public override bool ValidateFieldValue(IEntityCore involvedEntity, int fieldIndex, object value) { bool result = base.ValidateFieldValue(involvedEntity, fieldIndex, value); if (!result) return false; CompanyEntity company = (CompanyEntity)involvedEntity; switch ((CompanyFieldIndex)fieldIndex) { case SalesmanFieldIndex.Rank: return this.ValidateRank((decimal)value); case SalesmanFieldIndex.Supervisor: return this.ValidateSupervisor((string)value, salesman); } return true; } } }
數據的驗證有二個方面,一個是實體層的驗證,通常說的數據表的驗證,常常是業務邏輯所需要。比如新增加一個公司時,輸入公司的區域編碼時,需要在系統的區域編碼中驗證一下,它的區域編碼是否存在,如果不存在,則不允許保存。另一種驗證是字段層的驗證,比如輸入公司聯系方式的郵件地址時,需要驗證郵件格式是否正確。
四種類型的接口,滿足日常開發中所需要的大部分需求,再配合代碼生成器,一天做十來個頁面不是難事情,效率高,且質量也好,不會出錯。