• 企业管理软件开发架构之三 系统分层组织结构


    我给MIS类型的软件分四个实现层次,三层架构。

    BusinessLogic 业务实体 由LLBL Gen 生成业务实体,代码生成器生成

    Interface 数据访问接口 根据实体产生的数据访问接口,由Code Smith生成

    Manager 接口实现 根据实体产生的数据访问接口的实现代码,由Code Smith生成

    UI 界面层 拖拉控件,绑定数据到界面中

    image_thumb2

    Business Logic 业务实体层

    以ORM作为数据访问基础技术,业务实体中包含数据之间的关系逻辑,而不再是用于填充数据的实体。

    image

    以上结构由LLBL Gen自动生成,它已经为我们生成了实体,实体验证类型,数据访问接口和相关的辅助类型。

    公司注册中的公司实体,它的定义如下代码所示

    [Serializable]
    public partial class CompanyEntity : CommonEntityBase
            // __LLBLGENPRO_USER_CODE_REGION_START AdditionalInterfaces
            // __LLBLGENPRO_USER_CODE_REGION_END    
    {
            #region Class Member Declarations
            private EntityCollection<ModuleEntity> _modules;
    
            // __LLBLGENPRO_USER_CODE_REGION_START PrivateMembers
            // __LLBLGENPRO_USER_CODE_REGION_END
            #endregion
    
            #region Statics
            private static Dictionary<string, string>    _customProperties;
            private static Dictionary<string, Dictionary<string, string>>    _fieldsCustomProperties;
    
            /// <summary>All names of fields mapped onto a relation. Usable for in-memory filtering</summary>
            public static partial class MemberNames
            {
                /// <summary>Member name Modules</summary>
                public static readonly string Modules = "Modules";
            }
            #endregion
            
            /// <summary> Static CTor for setting up custom property hashtables. Is executed before the first instance of this entity class or derived classes is constructed. </summary>
            static CompanyEntity()
            {
                SetupCustomPropertyHashtables();
            }
            
            /// <summary> CTor</summary>
            public CompanyEntity():base("CompanyEntity")
            {
                InitClassEmpty(null, null);
            }
    
    .....
    }

    LLBL Gen设计器生成的实体代码有几个特点

    • 生成有多个用途的构造方法(ctor)。我们经常用到的是不带参数的构造方法,和带有主键值参数的方法。
    [EditorBrowsable(EditorBrowsableState.Never)]
    protected CompanyEntity(SerializationInfo info, StreamingContext context) : base(info, context)
            {
                if(SerializationHelper.Optimization != SerializationOptimization.Fast) 
                {
                    _modules = (EntityCollection<ModuleEntity>)info.GetValue("_modules", typeof(EntityCollection<ModuleEntity>));
                    this.FixupDeserialization(FieldInfoProviderSingleton.GetInstance());
                }
                // __LLBLGENPRO_USER_CODE_REGION_START DeserializationConstructor
                // __LLBLGENPRO_USER_CODE_REGION_END
    }
     

    这个构造方法用在序列化对象时发生,比如.net Remoting远程返回对象时。

    • 生成包含自定义属性的字段 自定义属性常用用属性的特殊设置。比如CompanyEntity.CompanyCode,实际中为了不区分CompanyCode的大小写,统一要求为大写,我们可以在此添加自定义属性RequiredCap,再到程序运行时读取此属性,并设置控件的字母大小写特性。
    private static Dictionary<string, string>    _customProperties;
    private static Dictionary<string, Dictionary<string, string>>    _fieldsCustomProperties;
    
    
    private static void SetupCustomPropertyHashtables()
            {
                _customProperties = new Dictionary<string, string>();
                _fieldsCustomProperties = new Dictionary<string, Dictionary<string, string>>();
                Dictionary<string, string> fieldHashtable;
                fieldHashtable = new Dictionary<string, string>();
                _fieldsCustomProperties.Add("CompanyCode", fieldHashtable);
    ......
    
    
    
    
    Dictionary<string, string> fieldCustomProperties = CompanyEntity.FieldsCustomProperties["CompanyCode"];
    string requiredCap = fieldCustomProperties["RequiredCap"];

    读取自定义属性RequiredCap的值为true时,设置控件的CharachterCasing属性。

    image

    界面和逻辑分离

    再来看业务实体的业务计算如何发生。示例代码如下所示

      protected override void OnFieldValueChanged(object originalValue, IEntityField2 field)
            {
                base.OnFieldValueChanged(originalValue, field);
    
                switch ((CompanyFieldIndex)field.FieldIndex)
                {
                    case CompanyFieldIndex.DriverAssembly:
                        OnChangeDriverAssembly((string)originalValue);
                        break;
                }
            }
    
            private void OnChangeDriverAssembly(string originalValue)
            {
                if (this.DriverAssembly == originalValue || String.IsNullOrEmpty(DriverAssembly)) return;
    
                this.DriverType = BaseCommon.GetProjectName(ModuleType.BusinessLogic, DriverAssembly);
            }

    当我在界面中改变当前界面插件程序集时,它会为我自动读取这个程序集的类型信息,项目命名信息。要理解这种方式,需要先理解.NET开发中的数据绑定技术。数据源控件相当于一个桥梁,连接数据实体和界面控件,当给数据源控件赋值时,控件会读取数据实体的值,当界面中的控件值发生改变时,借助于数据源控件,自动把更改后的数据回写到数据实体中。所以,当数据实体中值发生改变后,我们可以注册相应的改变事件,作出业务逻辑处理,数据源控件会读取改变之后的数据实体值,呈现在界面上。几乎所有的业务逻辑是依照此方式编程,也实现了界面和逻辑分离。

    界面和逻辑分离后,界面中的作用就是将控件绑定到数据源控件,再以Code Smith来生成数据读写接口:

     public override EntityBase2 LoadEntity(string refNo)
            {
                IItemManager manager = ClientProxyFactory.CreateProxyInstance<IItemManager>();
                ItemEntity customer = manager.GetItem(refNo);
                return customer;
            }
    
            public override void DeleteEntity(EntityBase2 entity)
            {
                ItemEntity user = (ItemEntity)entity;
                IItemManager manager = ClientProxyFactory.CreateProxyInstance<IItemManager>();
                manager.DeleteItem(user);
            }
    
            public override void SaveEntity(EntityBase2 entity)
            {
                ItemEntity user = (ItemEntity)entity;
                IItemManager manager = ClientProxyFactory.CreateProxyInstance<IItemManager>();
                manager.SaveItem(user);
            }

    系统中所有与数据库读写相关的界面代码均是以此方式实现。

    Interface/Implementation 接口层和接口实现层

    接口与它的实体均以Code Smith模板生成,效率高。如下所示的供应商接口

     public interface IVendorManager
        {
            VendorEntity GetVendor(System.String VendorNo);
            VendorEntity GetVendor(System.String VendorNo, IPrefetchPath2 prefetchPath);
            VendorEntity GetVendor(System.String VendorNo, IPrefetchPath2 prefetchPath, ExcludeIncludeFieldsList fieldList);
    
            EntityCollection GetVendorCollection(IRelationPredicateBucket filterBucket);
            EntityCollection GetVendorCollection(IRelationPredicateBucket filterBucket, ISortExpression sortExpression);
            EntityCollection GetVendorCollection(IRelationPredicateBucket filterBucket, ISortExpression sortExpression, IPrefetchPath2 prefetchPath);
            EntityCollection GetVendorCollection(IRelationPredicateBucket filterBucket, ISortExpression sortExpression, IPrefetchPath2 prefetchPath, ExcludeIncludeFieldsList fieldList);
    
            VendorEntity SaveVendor(VendorEntity vendor);
            VendorEntity SaveVendor(VendorEntity vendor, EntityCollection entitiesToDelete);
            VendorEntity SaveVendor(VendorEntity vendor, EntityCollection entitiesToDelete, string seriesCode);
            void SaveCollection(EntityCollection vendors);
    
            void DeleteVendor(VendorEntity vendor);
    
            bool IsVendorExist(System.String VendorNo);
            bool IsVendorExist(IRelationPredicateBucket filterBucket);
            int GetVendorCount(IRelationPredicateBucket filterBucket);
    
            VendorEntity CloneVendor(System.String VendorNo);
            void PostVendor(System.String VendorNo);
            void PostVendor(VendorEntity vendor);
            void ApprovalItem(EntityCollection vendors);
        }

    实现接口的Manager类型代码例子如下

     public class VendorManager : Foundation.Common.ManagerBase, IVendorManager
        {
            public VendorEntity GetVendor(System.String VendorNo)
            {
                return GetVendor(VendorNo, null);
            }
    
            public VendorEntity GetVendor(System.String VendorNo, IPrefetchPath2 prefetchPath)
            {
                return GetVendor(VendorNo, prefetchPath, null);
            }
    
            public VendorEntity GetVendor(System.String VendorNo, IPrefetchPath2 prefetchPath, ExcludeIncludeFieldsList fieldList)
            {
                VendorEntity _Vendor = new VendorEntity(VendorNo);
                using (DataAccessAdapterBase adapter = GetCompanyDataAccessAdapter())
                {
                    bool found = adapter.FetchEntity(_Vendor, prefetchPath, null, fieldList);
                    if (!found) throw new Foundation.Common.RecordNotFoundException("Invalid Vendor");
                }
                return _Vendor;
            }

    界面层中或是实体层,使用下面的接口来访问接口:

    ICompanyManager  _companyManager = ClientProxyFactory.CreateProxyInstance<ICompanyManager>();
    CompanyEntity _company = _companyManager.GetCompany(“Kingston”)

    如果没有采用分布式技术(.net Remoting,WCF),CreateProxyInstance方法直接返回ICompanyManager接口的实体类型的实例,供接口调用。如果有应用.net Remoting技术,则先以下面的方法产生服务器对象:客户端产生的实体对象会是一个远程代理,指向远程对象:

    RemotingConfiguration.RegisterActivatedServiceType(type);

    接口与实现分离的好处在这里体现的很明显,简单的切换部署模式(单机,分布式)不需要改变代码。

  • 相关阅读:
    二叉树的存储结构 数据结构和算法45
    二叉树的遍历 数据结构和算法46
    二叉树的存储结构 数据结构和算法45
    二叉树的遍历 数据结构和算法46
    二叉树2 数据结构和算法44
    二叉树 数据结构和算法43
    树的存储结构2 数据结构和算法42
    ASP.NET 常用的一些DOS命令
    Linq to DataSet
    上传图片生成略缩图“最佳解决”
  • 原文地址:https://www.cnblogs.com/JamesLi2015/p/3135033.html
Copyright © 2020-2023  润新知