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;
}
}
}
数据的验证有二个方面,一个是实体层的验证,通常说的数据表的验证,常常是业务逻辑所需要。比如新增加一个公司时,输入公司的区域编码时,需要在系统的区域编码中验证一下,它的区域编码是否存在,如果不存在,则不允许保存。另一种验证是字段层的验证,比如输入公司联系方式的邮件地址时,需要验证邮件格式是否正确。
四种类型的接口,满足日常开发中所需要的大部分需求,再配合代码生成器,一天做十来个页面不是难事情,效率高,且质量也好,不会出错。