zoukankan      html  css  js  c++  java
  • 解析大型.NET ERP系统 窗体、查询、报表二次开发

    详细介绍Enterprise Solution 二次开发的流程步骤,主要包括数据输入窗体(Entry Form),查询(Query/Enquiry),报表(Report)三个重要的二次开发项目。

    数据输入窗体开发 Entry Form

    当涉及到字段的增加或增加数据库表时,开发新功能界面有以下几个主要步骤。

    1  修改数据库增加表或字段,用LLBL Gen 生成新的实体映射类型定义文件。

    image

    LLBL Gen 可检测到字段的变化,增加字段或减少字段可自动更新实体映射定义文件。需要设定参数选项,如下图所示:

    image

    LLBL Gen 在生成实体的属性时,对于数据库字段可为空值的类型,会生成.NET的可空类型(nullable)类型。在我的实际项目中常常会取消这一特性,LLBL Gen会智能的知道实体哪些属性值发生变化,对于可空类型映射的属性不会生成UPDATE语句块,让它保持数据库中的空值即可(NULL)。

    2  增加功能编码,增加权限,增加菜单项。

    以销售合同功能(SLSOCE)为例,通过下面的脚本可增加系统功能(ADFUNC), 增加菜单项(ADMNUD)和增加权限(ADAUTD)。

    
    
    DECLARE
    
    @LastLineNo INTEGER,
    
    @ModuleCode NVARCHAR(4),
    
    @FunctionCode NVARCHAR(8),
    
    @FunctionDesc NVARCHAR(40),
    
    @SeriesCode NVARCHAR(8),
    
    @SeriesOption NVARCHAR(1)
    
    
    
    SET @ModuleCode = 'SLSO'
    
    SET @FunctionCode = 'SLSOCE'
    
    SET @FunctionDesc = N'Sales Contract Entry'
    
    SET @SeriesCode = ''
    
    SET @SeriesOption = N'N'
    
    
    
    -- Check for Function
    
    IF NOT EXISTS ( SELECT * FROM [ADFUNC] WHERE Module_Code = @ModuleCode AND Function_Code = @FunctionCode)
    
    BEGIN
    
    
    
     UPDATE [ADMODU] SET [Last_Line_No] = [Last_Line_No] + 1
    
     WHERE [Module_Code] = @ModuleCode
    
    
    
     SELECT @LastLineNo = Last_Line_No FROM ADMODU WHERE Module_Code = @ModuleCode
    
    
    
     IF @LastLineNo IS NOT NULL
    
     BEGIN
    
      INSERT [ADFUNC]
    
       ([Module_Code], [Function_No], [Function_Code], [Description],
    
       [Suspended], [Series_Option], [Series_Code],
    
       [Created_Date], [Created_By], [Revised_Date], [Revised_By],
    
       [OWNER_BRANCH],[SOURCE_BRANCH],[Icon])
    
      VALUES
    
       (@ModuleCode, @LastLineNo, @FunctionCode, @FunctionDesc, N'N', @SeriesOption, @SeriesCode,
    
       GETDATE(), 'MIS', GETDATE(), 'MIS', N'', N'','16')
    
    
    
      IF 'EMP'='EMP' OR '%SYS%'='EMPT' BEGIN
    
       INSERT [ADAUTD]
    
        ([User_Group], [Module_Code], [Function_No], [Function_Code], [Description],
    
        [Suspended], [Allow_Read], [Allow_Create], [Allow_Update], [Allow_Delete], [Allow_Print],
    
        [Allow_Post], [Allow_All_Tran])
    
       VALUES
    
        ('SYSADM', @ModuleCode, @LastLineNo, @FunctionCode, @FunctionDesc,
    
        'N' ,'Y', 'Y', 'Y', 'Y', 'Y',
    
        'Y', 'Y')
    
    
    
       INSERT [ADMNUD]
    
        ([User_Group], [Process_Code], [Function_Code], [Description], [Menu_Type], [Menu_Code],
    
        [Response_Type], [Suspended])
    
       VALUES
    
        ('SYSADM', 'M3_REP', @FunctionCode, @FunctionDesc, 'DOC', '921',
    
        'STDFUNC', 'N')
    
      END
    
    
    
     END
    
    END
    
    GO
    

    使用SQL语句的好处是可支持自动化更新部署,当系统检查到有版本更新时可自动跑SQL语句,当客户数量比较多时这个方法可提高效率。

    3 生成接口文件与接口实现类。

    Code Smith模板化的代码生成极大的方便了代码生成功能的开发,参考如下的接口模板文件Interface.cst文件,源文件如下所示:

    <%@ CodeTemplate Language="C#" TargetLanguage="C#" Src="" Inherits="" Debug="True" Description="Template description here." %>
    
    <%@ Property Name="EntityPicker" Type="ISL.Extension.EntityPickerProperty" Optional="False" Category="Project" Description="This property uses a custom modal dialog editor." %>
    
    <%@ Property Name="AssemblyFile" Type="System.String" Default="" Optional="False" Category="Project" Description="" 
    
     Editor="System.Windows.Forms.Design.FileNameEditor"%>
    
     
    
    <%@ Assembly Name="System.Data" %>
    
    <%@ Import Namespace="System.Data" %>
    
    <%@ Assembly Name="ISL.Empower.Extension" %>
    
    <%@ Import Namespace="ISL.Extension" %>
    
    <%@ Import Namespace="System.Collections.Generic" %>
    
    <%@ Assembly Name="SD.LLBLGen.Pro.ORMSupportClasses.NET20" %> 
    
    <%@ Import Namespace="SD.LLBLGen.Pro.ORMSupportClasses" %>
    
    
    
    <script runat="template">
    
    
    
    public string EntityName
    
    {
    
        get 
    
        {	   
    
    	   return EntityPicker.EntityName;
    
        }
    
    }
    
    
    
    public string ShortEntityName
    
    {
    
        get 
    
        {
    
                 return EntityName.Substring(0,EntityName.Length-6);
    
        }
    
    }
    
    
    
    public string FullEntityName
    
    {
    
        get 
    
        {
    
          return string.Format("{0}.EntityClasses.{1}", BusinessLogicProjectName, EntityName);	
    
        }
    
    }
    
    
    
    private string _businessLogicProjectName;
    
    
    
    public string BusinessLogicProjectName
    
    {
    
        get 
    
        {
    
            if(string.IsNullOrWhiteSpace(_businessLogicProjectName))
    
                  _businessLogicProjectName=EntityClassHelper.PrefixProjectName(AssemblyFile);	
    
            return _businessLogicProjectName;
    
        } 
    
    }
    
    
    
    public string EntityParamerList
    
    {
    
        get
    
        {
    
            IEntity2 policy = EntityClassHelper.GetEntityObject(AssemblyFile, EntityPicker.EntityName);
    
            string parm = string.Empty;
    
            List<string> parms=new List<string>();
    
            foreach (IEntityField2 field in policy.PrimaryKeyFields)
    
            {
    
                 parm = string.Format("{0} {1}", field.DataType.Name, field.Name);
    
                 parms.Add(parm);
    
            }
    
            return string.Join(",", parms.ToArray());
    
        }
    
    }
    
    
    
      public  string EntityLowercaseName
    
      {
    
        get
    
            {
    
                return  EntityPicker.EntityName.Substring(0, 1).ToLower() + EntityPicker.EntityName.Substring(1);
    
            }
    
      }
    
            
    
    </script>
    
    
    
    using System;
    
    using System.Collections.Generic;
    
    using System.Data;
    
    using System.Text;
    
    using SD.LLBLGen.Pro.ORMSupportClasses;
    
    
    
    using <%=BusinessLogicProjectName%>;
    
    using <%=BusinessLogicProjectName%>.FactoryClasses;
    
    using <%=BusinessLogicProjectName%>.EntityClasses;
    
    using <%=BusinessLogicProjectName%>.HelperClasses;
    
    using <%=BusinessLogicProjectName%>.InterfaceClasses;
    
    using <%=BusinessLogicProjectName%>.DatabaseSpecific;
    
    
    
    namespace <%=BusinessLogicProjectName%>.InterfaceClasses
    
    {
    
        public interface I<%=ShortEntityName%>Manager
    
    	{
    
    	     <%=EntityName%> Get<%=ShortEntityName%>(Guid sessionId,<%=EntityParamerList %>);
    
    	     <%=EntityName%> Get<%=ShortEntityName%>(Guid sessionId,<%=EntityParamerList %>,IPrefetchPath2 prefetchPath);
    
                  <%=EntityName%> Get<%=ShortEntityName%>(Guid sessionId,<%=EntityParamerList %>,IPrefetchPath2 prefetchPath,ExcludeIncludeFieldsList fieldList);
    
    	
    
    	     EntityCollection Get<%=ShortEntityName%>Collection(Guid sessionId,IRelationPredicateBucket filterBucket);
    
                  EntityCollection Get<%=ShortEntityName%>Collection(Guid sessionId,IRelationPredicateBucket filterBucket,ISortExpression sortExpression);
    
    	     EntityCollection Get<%=ShortEntityName%>Collection(Guid sessionId,IRelationPredicateBucket filterBucket,ISortExpression sortExpression, IPrefetchPath2 prefetchPath);
    
                  EntityCollection Get<%=ShortEntityName%>Collection(Guid sessionId,IRelationPredicateBucket filterBucket, ISortExpression sortExpression, IPrefetchPath2 prefetchPath, ExcludeIncludeFieldsList fieldList);
    
    	
    
    	     <%=EntityName%>  Save<%=ShortEntityName%>(Guid sessionId,<%=EntityName%>  <%=EntityLowercaseName%>);
    
    	     <%=EntityName%>  Save<%=ShortEntityName%>(Guid sessionId,<%=EntityName%>  <%=EntityLowercaseName%> ,EntityCollection entitiesToDelete);
    
    	     <%=EntityName%>  Save<%=ShortEntityName%>(Guid sessionId,<%=EntityName%> <%=EntityLowercaseName%>, EntityCollection entitiesToDelete, string seriesCode);
    
    	
    
    	     void Delete<%=ShortEntityName%>(Guid sessionId,<%=EntityName%>  <%=EntityLowercaseName%>);
    
    	
    
    	     bool Is<%=ShortEntityName%>Exist(Guid sessionId,<%=EntityParamerList %>);
    
    	     bool Is<%=ShortEntityName%>Exist(Guid sessionId,IRelationPredicateBucket filterBucket);
    
    	     int  Get<%=ShortEntityName%>Count(Guid sessionId,IRelationPredicateBucket filterBucket);
    
    		
    
    	     <%=EntityName%> Clone<%=ShortEntityName%>(Guid sessionId,<%=EntityParamerList %>);
    
    	     void Post<%=ShortEntityName%>(Guid sessionId,<%=EntityParamerList %>);
    
    	     void Post<%=ShortEntityName%>(Guid sessionId,<%=EntityName%> <%=EntityLowercaseName%>);	
    
    	}
    
    }
    

    在Code Smith代码生成器中跑Interface.cst模板,可得到如下的接口文件:

    
    
    using System;
    
    using System.Collections.Generic;
    
    using System.Data;
    
    using System.Text;
    
    using SD.LLBLGen.Pro.ORMSupportClasses;
    
    
    
    using ISL.BusinessLogic;
    
    using ISL.BusinessLogic.FactoryClasses;
    
    using ISL.BusinessLogic.EntityClasses;
    
    using ISL.BusinessLogic.HelperClasses;
    
    using ISL.BusinessLogic.InterfaceClasses;
    
    using ISL.BusinessLogic.DatabaseSpecific;
    
    
    
    namespace ISL.BusinessLogic.InterfaceClasses
    
    {
    
        public interface ISalesContractManager
    
        {
    
            SalesContractEntity GetSalesContract(Guid sessionId, String ContractNo);
    
            SalesContractEntity GetSalesContract(Guid sessionId, String ContractNo, IPrefetchPath2 prefetchPath);
    
            SalesContractEntity GetSalesContract(Guid sessionId, String ContractNo, IPrefetchPath2 prefetchPath, ExcludeIncludeFieldsList fieldList);
    
    
    
            EntityCollection GetSalesContractCollection(Guid sessionId, IRelationPredicateBucket filterBucket);
    
            EntityCollection GetSalesContractCollection(Guid sessionId, IRelationPredicateBucket filterBucket, ISortExpression sortExpression);
    
            EntityCollection GetSalesContractCollection(Guid sessionId, IRelationPredicateBucket filterBucket, ISortExpression sortExpression, IPrefetchPath2 prefetchPath);
    
            EntityCollection GetSalesContractCollection(Guid sessionId, IRelationPredicateBucket filterBucket, ISortExpression sortExpression, IPrefetchPath2 prefetchPath, ExcludeIncludeFieldsList fieldList);
    
    
    
            SalesContractEntity SaveSalesContract(Guid sessionId, SalesContractEntity salesContractEntity);
    
            SalesContractEntity SaveSalesContract(Guid sessionId, SalesContractEntity salesContractEntity, EntityCollection entitiesToDelete);
    
            SalesContractEntity SaveSalesContract(Guid sessionId, SalesContractEntity salesContractEntity, EntityCollection entitiesToDelete, string seriesCode);
    
    
    
            void DeleteSalesContract(Guid sessionId, SalesContractEntity salesContractEntity);
    
    
    
            bool IsSalesContractExist(Guid sessionId, String ContractNo);
    
            bool IsSalesContractExist(Guid sessionId, IRelationPredicateBucket filterBucket);
    
            int GetSalesContractCount(Guid sessionId, IRelationPredicateBucket filterBucket);
    
    
    
            SalesContractEntity CloneSalesContract(Guid sessionId, String ContractNo);
    
            void PostSalesContract(Guid sessionId, String ContractNo);
    
            void PostSalesContract(Guid sessionId, SalesContractEntity salesContractEntity);
    
        }
    
    }
    

    接口的实现类(Manager.cst) 也只需要借助于Code Smith生成即可。生成完成之后,需要增加主从表的处理,比如主表的保存和删除方法需要做少量代码修改。

    保存方法增加单据编码功能代码,如下代码片段所示:

     public SalesContractEntity SaveSalesContract(Guid sessionId, SalesContractEntity SalesContract, EntityCollection entitiesToDelete, string seriesCode)
    
            {
    
                string currentRefNo = string.Empty;
    
                using (DataAccessAdapterBase adapter = GetCompanyDataAccessAdapter(sessionId))
    
                {
    
                    try
    
                    {
    
                        adapter.StartTransaction(IsolationLevel.ReadCommitted, "Save SalesContract");
    
                        if (SalesContract.IsNew && seriesCode != string.Empty)
    
                        {
    
                            currentRefNo = SalesContract.ContractNo;
    
                            IDocumentSerializationManager serializationManager = ClientProxyFactory.CreateProxyInstance<IDocumentSerializationManager>();
    
                            SalesContract.ContractNo = serializationManager.GetNextSerialNo(sessionId, seriesCode, SalesContract.ContractNo, SalesContract);
    
                        }

    保存方法增加子表的保存方法

     ISalesContractDetailManager contractDetailManager = ClientProxyFactory.CreateProxyInstance<ISalesContractDetailManager>();
    
                        if (entitiesToDelete != null)
    
                        {
    
                            foreach (IEntity2 entity in entitiesToDelete)
    
                            {
    
                                if (entity is SalesContractDetailEntity)
    
                                    contractDetailManager.DeleteSalesContractDetail(sessionId, (SalesContractDetailEntity)entity);
    
                            }
    
                        }
    
                     
    
                        adapter.SaveEntity(SalesContract, true, false);
    
    
    
                        foreach (SalesContractDetailEntity salesContractDetailEntity in SalesContract.SalesContractDetail)
    
                        {
    
                            contractDetailManager.SaveSalesContractDetail(sessionId, salesContractDetailEntity);
    
                        }
    

    遵守数据库表保存的基本原则,保存时先保存主表,再保存子表,删除时是先删除子表,再删除主表。

    删除方法增加删除子表的代码:

     public void DeleteSalesContract(Guid sessionId, SalesContractEntity SalesContract)
    
            {
    
                using (DataAccessAdapter adapter = GetCompanyDataAccessAdapter(sessionId))
    
                {
    
                    if (!adapter.IsEntityExist<SalesContractEntity>(SalesContract))
    
                        return;
    
    
    
                    try
    
                    {
    
                        adapter.StartTransaction(IsolationLevel.ReadCommitted, "Delete SalesContract");
    
    
    
                        ISalesContractDetailManager contractDetailManager = ClientProxyFactory.CreateProxyInstance<ISalesContractDetailManager>();
    
                        foreach (SalesContractDetailEntity salesContractDetailEntity in SalesContract.SalesContractDetail)
    
                        {
    
                            contractDetailManager.DeleteSalesContractDetail(sessionId, salesContractDetailEntity);
    
                        }
    

    LLBL Gen没有采取级联的方式来做数据的删除,也不推荐这样处理,这样对于数据的验证相对难于处理。数据库在确认删除与应用程序的验证方法方面难于沟通,所以LLBL Gen 推荐实体相关的操作都由应用程序控制,要删除数据,要验证逻辑,都交给应用程序来实现。

    4 增加界面文件,拖放控件,绑定数据源,用界面代码生成器(EntryForm.cst)生成界面实现文件。

    自从用上了Visual Studio 2010, 窗体设计器的性能比之前的版本提升很大,Visual Studio 2012/2013的窗体设计器性能更加卓越,期待着即将发布的Visual Studio 2015能持续改善窗体设计器的性能。Visual Studio在全球有众多的客户,提高性能就意味着加快工作效率,也就是实现了节约能源(电力消耗),Google也有很多程序改善,提高程序性能可环保,节约电能开支。

    image

    从Visual Studio 2010迁移到Visual Studio 2012/2013的一个很主要的理由也是因为它的窗体设计器效率高。

    再来看一下销售合同界面的主要代码,下面的代码是用Code Smith模板生成的,实现了界面代码自动生成:

        [FunctionCode("SLSOCE")]
    
        public partial class SalesContractEntry : EntryForm
    
        {
    
            private ISalesContractManager _salesContractEntityManager = null;
    
            private SalesContractEntity _salesContractEntity = null;
    
           
    
            public SalesContractEntry()
    
            {
    
                InitializeComponent();
    
            }
    
            
    
            protected override void OnLoad(EventArgs e)
    
            {
    
                if (!DesignMode)
    
                    this._salesContractEntityManager = ClientProxyFactory.CreateProxyInstance<ISalesContractManager>();
    
                base.OnLoad(e);
    
            }
    
    
    
            protected override void InitNavigator(InitNavigatorArgs args)
    
            {
    
                base.InitNavigator(args);
    
                args.SortExpression.Add(SalesContractFields.ContractNo | SortOperator.Ascending);
    
                args.PredicateBucket.PredicateExpression.Add(SalesContractFields.Closed == false);
    
            }
    
    
    
            protected override EntityBase2 LoadData(Dictionary<string, string> refNo)
    
            {
    
                base.LoadData(refNo);
    
                string ContractNo = string.Empty;
    
                if (refNo.TryGetValue("ContractNo", out ContractNo))
    
                {
    
                    IPrefetchPath2 prefetchPath = new PrefetchPath2((int) EntityType.SalesContractEntity);
    
                    prefetchPath.Add(SalesContractEntity.PrefetchPathSalesContractDetail);
    
                    _salesContractEntity = _salesContractEntityManager.GetSalesContract(Shared.CurrentUserSessionId, ContractNo, prefetchPath);
    
                }
    
                else
    
                {
    
                    _salesContractEntity = new SalesContractEntity();
    
                }
    
                return _salesContractEntity;
    
            }
    
    
    
            protected override void InitializeLayout()
    
            {
    
                base.InitializeLayout();
    
                gridSalesOrder.OverrideReadOnlyAppearance(SalesContractDetailFields.OrderDate.Name,true);
    
                gridSalesOrder.OverrideReadOnlyAppearance(SalesContractDetailFields.DueDate.Name, true);
    
                gridSalesOrder.OverrideReadOnlyAppearance(SalesContractDetailFields.Closed.Name, true);
    
                gridSalesOrder.OverrideAllowEditForNewOnlyColumn(SalesContractDetailFields.EntryNo.Name, true);
    
            }
    
    
    
            protected override void BindControls(EntityBase2 entity)
    
            {
    
                base.BindControls(entity);
    
                this.salesContractBindingSource.DataSource = entity;
    
            }
    
    
    
            protected override EntityBase2 Add()
    
            {
    
                base.Add();
    
                this._salesContractEntity = new SalesContractEntity();
    
                return _salesContractEntity;
    
            }
    
    
    
            protected override EntityBase2 Save(EntityBase2 entityToSave, EntityCollection entitiesToDelete)
    
            {
    
                SalesContractEntity SalesContractEntity = (SalesContractEntity)entityToSave;
    
                this._salesContractEntity = this._salesContractEntityManager.SaveSalesContract(Shared.CurrentUserSessionId, SalesContractEntity, entitiesToDelete, SeriesCode);
    
                return this._salesContractEntity;
    
            }
    
    
    
            protected override void Delete(EntityBase2 entityToDelete)
    
            {
    
                base.Delete(entityToDelete);
    
                SalesContractEntity SalesContractEntity = (SalesContractEntity)entityToDelete;
    
                this._salesContractEntityManager.DeleteSalesContract(Shared.CurrentUserSessionId, SalesContractEntity);
    
            }
    
    
    
            protected override object Clone(Dictionary<string, string> refNo)
    
            {
    
                base.Clone(refNo);
    
                return null;
    
            }
    
    
    
            protected override void ReleaseResources()
    
            {
    
                base.ReleaseResources();
    
                try
    
                {
    
                    _salesContractEntity = null;
    
                    _salesContractEntityManager = null;
    
                }
    
                catch
    
                {
    
    
    
                }
    
            }
    
    
    
            private void btnPost_Click(object sender, EventArgs e)
    
            {
    
                this.PerformPost(true);
    
            }
    
    
    
            protected override void Post(EntityBase2 entityToPost)
    
            {
    
                base.Post(entityToPost);
    
                SalesContractEntity resignEntity = entityToPost as SalesContractEntity;
    
                _salesContractEntityManager.PostSalesContract(Shared.CurrentUserSessionId, resignEntity);
    
                ISL.WinUI.Shared.ShowInfo("Transaction ({0}) is posted successfully", new object[] { resignEntity.ContractNo });
    
            }
    
        }

    编译运行一下程序,如下所示,一个基本的增删查改的功能就完成了,开发效率非常高,代码自动化程度也高。

    image 

    5 增加类型初始化,验证,查找与钻取,自动带值,主从表事件关联

    在实体类型定义文件中,增加初始化代码,比如单据默认值(单据的创建日期,单据的默认状态),系统默认值(单据的创建用户和期间):

    public partial class SalesContractEntity
    
    	{
    
    		protected override void OnInitialized()
    
    		{
    
    			base.OnInitialized();
    
    
    
    			// Assign default value for new entity
    
    			if (Fields.State == EntityState.New)
    
    			{
    
    				#region DefaultValue
    
    				
    
    				// __LLBLGENPRO_USER_CODE_REGION_START DefaultValue
    
    			    this.Fields[(int) SalesContractFieldIndex.Closed].CurrentValue = false;
    
    			    // __LLBLGENPRO_USER_CODE_REGION_END
    
    				#endregion
    
    			}
    
    
    
    			InitEventHandlers();
    
    			this.Validator = Singleton<SalesContractValidator>.Instance;
    
    		}

    增加从表默认值,比如主表的主键是参考编号,从表则用两个主键,前一个是参考编号,后一个主键是依次增长的自动序列号(10,20,30…..或1,2,3……):

     private void JobOrderAmendmentRemarks_EntityAdded(object sender, CollectionChangedEventArgs e)
    
            {
    
                if (e.InvolvedEntity.IsNew)
    
                {
    
                    SalesContractDetailEntity remark = (SalesContractDetailEntity)e.InvolvedEntity;
    
                    decimal max = 0;
    
                    foreach (SalesContractDetailEntity jobOrderAmendmentRemark in this.SalesContractDetail)
    
                    {
    
                        if (jobOrderAmendmentRemark.EntryNo > max)
    
                        {
    
                            max = jobOrderAmendmentRemark.EntryNo;
    
                        }
    
                    }
    
                    remark.EntryNo = max + Shared.CompanySetting.AutoIncBy;
    
                }
    
            }

    如果你有Enterprise Solution 开发框架的例子代码,这些代码都是可以从原有的文件中直接拷贝过来稍做修改后即可,无任何技巧,唯手熟练。

    查找与钻取

    给客户编号属性值增加查找,设置Lookup属性即可。

    image

    增加查找一般都需要增加验证,增加客户编号验证。

       [Serializable]
    
        public partial class SalesContractValidator : ValidatorBase
    
        {
    
            // Add your own validation code between the two region markers below. You can also use a partial class and add your overrides in that partial class.
    
    
    
            // __LLBLGENPRO_USER_CODE_REGION_START ValidationCode
    
            public override bool ValidateFieldValue(IEntityCore involvedEntity, int fieldIndex, object value)
    
            {
    
                bool result = base.ValidateFieldValue(involvedEntity, fieldIndex, value);
    
                if (!result) return false;
    
    
    
                switch ((SalesContractFieldIndex) fieldIndex)
    
                {
    
                    case SalesContractFieldIndex.CustomerNo:
    
                        return this.ValidateCustomerNo((string) value);
    
                }
    
    
    
                return true;
    
            }
    
    
    
            private bool ValidateCustomerNo(string value)
    
            {
    
                if (!string.IsNullOrEmpty(value))
    
                {
    
                    ICustomerManager customerManager = ClientProxyFactory.CreateProxyInstance<ICustomerManager>();
    
                    customerManager.ValidateCustomerNo(Shared.CurrentUserSessionId, value);
    
                }
    
    
    
                return true;
    
            }

    增加客户编号钻取,在界面中双击此控件可直接跳转到相应的主档功能。

    image

    这样就完成了新功能的开发,想要做到基于EntryForm应用程序的高效率开发,必须通晓框架的功能,知道在哪里插入什么样的代码,这在Enterprise Solution技术培训中会详细讲解。

    查询窗体 Query/Enquiry

    有两种类型的查询功能,一个是数据输入窗体查询,这种查询需要继承原有的输入窗体(EntryForm),修改属性即可实现,参考下面的销售合同查询功能的实现。

    销售合同查询功能全部的代码如下,只需要重写几个方法即可:

     [FunctionCode("SLSOCQ")]
    
        public partial class SalesContractEnquiry : SalesContractEntry
    
        {
    
            public SalesContractEnquiry()
    
            {
    
                InitializeComponent();
    
    
    
                this.SupportAdd = false;
    
                this.SupportEdit = false;
    
                this.SupportDelete = false;
    
            }
    
    
    
            protected override void InitNavigator(FunctionFormBase.InitNavigatorArgs args)
    
            {
    
                args.PredicateBucket.PredicateExpression.Add(SalesContractFields.Closed == true);
    
                if (!AllowViewAllTransaction)
    
                    args.PredicateBucket.PredicateExpression.Add(SalesContractFields.CreatedBy ==ISL.BusinessLogic.Shared.CurrentUserSession.UserId);
    
    
    
                args.SortExpression.Add(SalesContractFields.ContractNo | SortOperator.Ascending);
    
            }
    
    
    
            protected override void InitializeLayout()
    
            {
    
                base.InitializeLayout();
    
                this.btnClose.Visible = false;
    
                this.txtContractNo.Lookup.FilterName = "Posted";
    
            }
    
        }

    第二种查询是自定义查询,可查询任意条件过滤的多个表的数据。

    EntryForm没有使用LLBL Gen的Typeed Lists或Typed Views,而是通过自定义的查询(Custom Query)来实现数据查询。

    来看一下送货日记的查询界面,这个界面的主要布局是上面是过滤条件,下面是要根据过滤条件查询到的结果数据。

    image

    送货日记的全部源代码如下所示,

     [FunctionCode("SQMEGS")]
    
        public sealed partial class ShipmentJournalEnquiry : EnquiryForm
    
        {
    
            private IUserDefinedQueryManager _udqManager;
    
            private bool _show;
    
    
    
            public ShipmentJournalEnquiry()
    
            {
    
                InitializeComponent();
    
            }
    
    
    
            protected override void InitializeLayout()
    
            {
    
                base.InitializeLayout();
    
    
    
                this.grdShipment.DisplayLayout.Bands[0].Columns["Year"].Format = "####";
    
                this.grdShipment.DisplayLayout.Bands[0].Columns["Month"].Format = "##";
    
            }
    
    
    
            protected override void OnLoad(EventArgs e)
    
            {
    
                base.OnLoad(e);
    
    
    
                if (!DesignMode)
    
                {
    
                    _udqManager = ClientProxyFactory.CreateProxyInstance<IUserDefinedQueryManager>();
    
                    _show = true;
    
                }
    
            }
    
    
    
            protected override void InitNavigator(ISL.WinUI.Forms.FunctionFormBase.InitNavigatorArgs args)
    
            {
    
                base.InitNavigator(args);
    
    
    
                if (_show)
    
                    this.FetchShipment();
    
            }
    
    
    
            private void FetchShipment()
    
            {
    
                IPredicateExpression predicateExpression = null;
    
                IRelationPredicateBucket filterBucket = new RelationPredicateBucket();
    
    
    
                if (Shared.CurrentUserSession.AllowAccessAllCustomers)
    
                    filterBucket.PredicateExpression.Add(Shared.GetAllowedCustomerNoPredicateExpression(ShipmentFields.CustomerNo));
    
    
    
                if (!AllowViewAllTransaction)
    
                    filterBucket.PredicateExpression.Add(ShipmentFields.CreatedBy == Shared.CurrentUser.UserId);
    
    
    
                predicateExpression = this.efcCustomerNo.GetPredicateExpression();
    
                if (predicateExpression != null)
    
                    filterBucket.PredicateExpression.Add(predicateExpression);
    
    
    
                predicateExpression = this.efcShipFrom.GetPredicateExpression();
    
                if (predicateExpression != null)
    
                    filterBucket.PredicateExpression.Add(predicateExpression);
    
    
    
                predicateExpression = this.efcPostedFilter.GetPredicateExpression();
    
                if (predicateExpression != null)
    
                    filterBucket.PredicateExpression.Add(predicateExpression);
    
    
    
                predicateExpression = this.efcReturnedFilter.GetPredicateExpression();
    
                if (predicateExpression != null)
    
                    filterBucket.PredicateExpression.Add(predicateExpression);
    
    
    
                ResultsetFields fields = new ResultsetFields(16);
    
                fields.DefineField(ShipmentFields.RefNo, 0);
    
                fields.DefineField(ShipmentFields.ShipmentDate, 1);
    
    
    
                DbFunctionCall dbYear = new DbFunctionCall("Year", new object[] { ShipmentFields.ShipmentDate });
    
                EntityField2 Year = new EntityField2("Year", dbYear);
    
                fields.DefineField(Year, 2);
    
    
    
                DbFunctionCall dbMonth = new DbFunctionCall("Month", new object[] { ShipmentFields.ShipmentDate });
    
                EntityField2 Month = new EntityField2("Month", dbMonth);
    
                fields.DefineField(Month, 3);
    
    
    
                fields.DefineField(ShipmentFields.Posted, 4);
    
                fields.DefineField(ShipmentFields.Returned, 5);
    
                fields.DefineField(ShipmentFields.CustomerNo, 6);
    
                fields.DefineField(ShipmentFields.CustomerName, 7);
    
                fields.DefineField(ShipmentFields.Ccy, 8);
    
                fields.DefineField(ShipmentFields.TotItemAmt, 9);
    
                fields.DefineField(ShipmentFields.Etd, 10);
    
                fields.DefineField(ShipmentFields.ShipFrom, 11);
    
                fields.DefineField(ShipmentFields.PostedBy, 12);
    
                fields.DefineField(ShipmentFields.PostedDate, 13);
    
                fields.DefineField(ShipmentFields.CreatedBy, 14);
    
                fields.DefineField(ShipmentFields.CreatedDate, 15);
    
    
    
                ISortExpression sortExpression = new SortExpression(ShipmentFields.RefNo | SortOperator.Ascending);
    
    
    
                DataTable table = QueryHelper.GetQueryResult(Shared.CurrentUserSession.CompanyCode, fields, filterBucket, sortExpression, null, true);
    
                this.shipmentBindingSource.DataSource = table;
    
                this.grdShipment.SetDataBinding(this.shipmentBindingSource, null, true, true);
    
            }
    
    
    
            private void btnRefresh_Click(object sender, EventArgs e)
    
            {
    
                this.PerformRefresh();
    
    
    
                //clear filter 
    
                this.efcShipFrom.Clear();
    
                this.efcCustomerNo.Clear();
    
            }
    
    
    
            private void grdShipment_BeforeDrillDown(object sender, DrillDownEventArgs e)
    
            {
    
                if (Shared.StringCompare(this.grdShipment.ActiveCell.Column.Key, ShipmentFields.RefNo.Name) == 0)
    
                {
    
                    DataRowView row = this.shipmentBindingSource.Current as DataRowView;
    
                    if (row != null)
    
                    {
    
                        bool isPosted = Convert.ToBoolean(row[ShipmentFields.Posted.Name]);
    
                        e.DrillDownValue.FunctionCode = (isPosted ? "SQMETS" : "SLSHSE");
    
                    }
    
                }
    
            }
    
    
    
            protected override void OnBeforeGridExport(CancelableGridDataExportEventArgs args)
    
            {
    
                base.OnBeforeGridExport(args);
    
                args.Grid = this.grdShipment;
    
            }
    
    
    
            protected override void OnBeforeGridPrint(CancelableGridDataExportEventArgs args)
    
            {
    
                base.OnBeforeGridPrint(args);
    
                args.Grid = this.grdShipment;
    
            }
    
    
    
            protected override void ReleaseResources()
    
            {
    
                try
    
                {
    
                    this._udqManager = null;
    
                    this.btnRefresh.Click -= new EventHandler(btnRefresh_Click);
    
                    this.grdShipment.BeforeDrillDown -= new ISL.WinUI.DrillDownEventHandler(this.grdShipment_BeforeDrillDown);
    
                }
    
                catch
    
                {
    
                }
    
                base.ReleaseResources();
    
            }

    这个功能的数据读取代码没有封装到BackgroundWorker后台线程组件中,当数据量多时会发生界面死锁,用户体验性不好。

    报表 Report

    水晶报表已经是报表行业的工业标准,完善的功能与强大的客户化开发功能,水晶报表的市场占有率一直很高。微软的后起之秀Reporting Services也相当优秀,Enterprise Solution对这两种类型的报表都有完备的支持。

    Enterprise Solution解决了报表开发中令开发人员头疼的界面传参问题,它可以依据一个设定自动生成报表参数界面,通过ReportViewer自动传递到报表文件中。

    如下图所示,当开发完成水晶报表之后,需要在报表对话框中增加一个参数设定,用于生成报表的参数:

    image

    依据上面的三个参数,报表执行引擎产生一个参数输入界面,如下图所示

    image

    用户在界面中输入值,点击查看按钮,报表引擎将用户输入的值传递到报表中,这个过程不需要开发人员的编程或设定。

    image

    此外,报表引擎还处理了水晶报表运行库的问题,报表中的标签也会自动翻译。

    另外,工作流与计划任务的二次开发也相当常见,因相对复杂需要单独成篇,暂不介绍。

    工作流(Workflow)

    计划任务(Scheduling)

  • 相关阅读:
    iis7 下配置 ASP.NET MVC 项目遇到的问题 (WIN7 64位 旗舰版 第一次配置站点)
    C# .NET 使用 NPOI 读取 .xlsx 格式 Excel
    C# .NET 使用 NPOI 生成 .xlsx 格式 Excel
    SQL和T-SQL之间的区别
    .net core的服务器模式和工作站模式
    Windows Mysql8 设置大小写敏感
    每个国家对应的语言Locale和国家代码对照表(转)
    IL指令集
    使用 nvm 管理不同版本的 node 与 npm
    SSIS 包部署 Package Store 后,在 IS 中可以执行,AGENT 执行却报错
  • 原文地址:https://www.cnblogs.com/JamesLi2015/p/4635159.html
Copyright © 2011-2022 走看看