zoukankan      html  css  js  c++  java
  • 接触LLBL Gen Pro 对象关系映射框架后 前途变的一片光明

    时间回到2010年,那时候还是熟悉代码生成+基础框架这种模式,基本的开发思路是通过代码生成器生成实体,再生成接口与实现类,最后拖拉控件,写界面数据绑定代码。基本上就是动软代码生成器给出的模式,或是微软的Repository Factory模式的实践,迷恋于微软的Enterprise Libray,这个框架是从Application Block演化而来。我也是算是.NET技术推广以来,第一批学习.NET技术的开发人员。

    一直在寻找一种界面与逻辑分离的技术,也没有思路,上面代码生成造成的结果是逻辑代码分布在系统的各个地方,改一个字段或是增加字段都需要重新生成一次,给系统的稳定性带来困扰。用《企业应用架构模式》中的一种模式总结,就是事务脚本(Transaction Script),不过这种模式好理解,也没有复杂的技术堆栈,通过对这种模式的掌握,由.NET学习者变成熟练的.NET代码工人。

    第一次看到LLBL Gen Pro,它长成这个样子:

    clip_image002

    LLBL Gen Pro 2.5/2.6是它发展历史上很经典的一个版本,查询接口稳定成熟,遇到问题了去tinyform上发个帖子,过一会就有专业的人员响应回复。经过大半年的学习,熟悉了这个ORM框架的用法,开始高级一点的定制开发,它的模板编辑器如下面的图所示:

    clip_image002[5]

    LLBL Gen Pro从3.x开始,把原来二进制的项目文件lgp改成Xml格式的文件llblgenproj。这是一个很重要的变化,

    因为数据库属性最终映射的实体属性可以在设计器中修改,所以必须读取LLBL Gen Pro的项目文件才能确定最终映射的属性名称。 我的辅助开发工具中也依赖于llblgenproj项目文件的这个特性,在LLBL Gen Pro 2.x时代这是不可能的。

     

    当时我的同事做了一个基于ORM的代码生成工具,用于生成实体接口与实现代码,解释如下:

    数据库表SalesOrder –> 实体SalesOrderEntity  -> 接口ISalesOrderManager –> 接口实现SalesOrderManager

    后面两个步骤就是需要做的工作,同事设计的工具的原型如下:

    clip_image002[7]

    有接近3年的时间,我都迷恋于这个工具产生的接口与实现类代码。直到后来有客户不断提出对接口与实现中细节的修改,我慢慢无法忍受用.NET代码写代码生成器,还要编译的苦恼。当时同事们都极力推荐模板生成技术,于是用Code Smith写下了模板代码,一直延续到今天。分享一下Code Smith生成接口的代码:

    <%@ 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%>);    
        }
    }

    再后来微软推出了T4模板代码生成工具,曾经有一段时间想把Code Smith转换成T4的模板,Code Smith 5.x不支持.NET3.5,一些.NET类库写的扩展方法,Code Smith模板不能用,这是想转成T4代码模板的原因。然而在网上找一个带智能提示,语法高亮的T4模板编辑器相当困难,在国外找到一个也是试用版,国内也没有破解版,再后来就没有完全没有动力去折腾了。Code Smith 6.x完全支持.NET 3.5,一直延续用到今天。

    借助于LLBL Gen Pro,再加上以前积累的一些公共代码类库,一套原始的ERP系统成型,参考下面的视图:

    clip_image002[9]

    这个项目中,抽象出了三个公共基类库,公共方法Common,公共控件WinUI,公共程序Core。后来硬盘丢失,实在找不到这个项目的源代码,不过设计思路与项目的架构已经了然于胸。

    到2012年的时候,接触到Infragistics界面控件包,它几乎重写了整个WinForms的控件,提供的属性非常丰富。当时公司购买了这套控件的许可,可查看到控件的所有源代码。不过大部分时间都没有去看源代码,只有遇到不可理解的错误时,才会跟踪进入源代码查看参数传递是否合理正确。

    有了实体和支持强类型对象的控件,这两者的结合,深远的影响了后来的程序设计生涯。虽然现有偶尔也会用DataTable,但大面积使用的开发模式仍旧是使用实体+数据绑定。

    .NET数据绑定是需要深入学习的另一个领域,有了数据绑定,下面代码可以省略:

    //Get value from control
    string refNo=txtRefNo.Text;
    
    //set value to control
    txtRefNo.Text="SO201507190001";

    只需要将实体绑定给BindingSource控件,整个界面上的控件就全都有了值,不用上面的代码逐个赋值。

    protected override void BindControls(EntityBase2 entity)
    {
         base.BindControls(entity);
    
         InventoryMovementEntity inventoryMovement = (InventoryMovementEntity)entity;
         inventoryMovementBindingSource.DataSource = inventoryMovement;
    }
     

    对于WinForms开发,大量的取值和赋值操作代码都省略了,减少了代码,提高系统可维护性。

    基本上到这里,我已经可以独立开发系统,系统的各个部件都可以处理好,我的开发步骤如下:

    1  设计数据库表。找过很多case工具以辅助生成SQL Server数据表,最后还是回归SQL Server Management Studio,这是最好用的最简洁的工具,也方便与同事交流。当两个人用的数据库设计工具不同,而发生一些微小的错误或差异时,常常会令人抓狂。

    2  LLBL Gen Pro生成实体,设置实体间关系。基本上就是连接到数据库,刷新实体,生成或更新实体文件。

    3  生成实体读写的接口与实现类。借用Code Smith模板,效率高

    4  拖拉界面,绑定数据源控件。即使没有学过编程,也可以经过短暂的培训快速上手开发界面。

    5  给实体增加业务逻辑代码,界面与逻辑分离。这是要手写代码的地方,写业务逻辑,包含计算逻辑与验证逻辑。

  • 相关阅读:
    iOS——归档对象的创建,数据写入与读取
    iOS——plist的创建,数据写入与读取
    SQL SERVER 2005快捷键
    图片放大源码
    验证url 地址是否是图片
    JS三大经典变量命名法
    载入锁频
    SQL Server 查询分析器键盘快捷方式
    关于ajax get方式请求 url地址参数怎么变成空了的问题
    SQL计算表的列数
  • 原文地址:https://www.cnblogs.com/JamesLi2015/p/4703681.html
Copyright © 2011-2022 走看看