LLBL Gen 实体映射工具技术原理剖析

应用LLBL Gen作为ORM工具时,经常会遇到想查一个实体所代表的数据库表名,或是想已知一个数据库表名,想知道它在程序中对应的实体名,这两者之间相互查找,这个需求经常会碰到。

前一种需求产生于,系统报错时,会显示调用的堆栈和错误信息,依据最后一层堆栈提供的对象参数,可以查到表名,以此追查数据为什么会出错。

后一种需求,常常想知道业务逻辑算法。比如单价的计算方法,总金额的计算方法,因此需要从数据库中追踪到实体表及其属性,再跟踪属性之间的运算逻辑。

 

第一种解决方案,可以修改LLBL Gen生成的源代码的模板,把表名生成到实体映射文件的注释中,修改模板

  1. entityAdapter.template  #31行
    ///Mapped on table: <[ElementTargetObjectName]></summary>
  2. 这种方案,当鼠标放到实体的名称上时,会显示它映射的表名。
  3. 继续修改LLBL Gen模板,让它能显示出属性对应的数据库字段。
    1. entityIncludeAdatper.template #549行
      ///Mapped on <[ CaseCamel TargetType ]> field: <[SourceObjectName]>.<[SourceColumnName]></summary>

第二种方案,解析LLBL Gen生成的源项目文件,它的存储格式如下

internal class PersistenceInfoProviderCore : PersistenceInfoProviderBase
    {
        /// <summary>Initializes a new instance of the <see cref="PersistenceInfoProviderCore"/> class.</summary>
        internal PersistenceInfoProviderCore()
        {
            Init();
        }

        /// <summary>Method which initializes the internal datastores with the structure of hierarchical types.</summary>
        private void Init()
        {
            this.InitClass((158 + 0));
            InitAccountEntityMappings();
                                 }
}

具体的每个实体的映射

    /// <summary>Inits AccountEntity's mappings</summary>
        private void InitAccountEntityMappings()
        {
            this.AddElementMapping( "AccountEntity", "MIS", @"dbo", "Account", 58 );
            this.AddElementFieldMapping( "AccountEntity", "AcctName", "ACCT_NAME", false, "NVarChar", 50, 0, 0, false, "", null, typeof(System.String), 0 );
            this.AddElementFieldMapping( "AccountEntity", "AcctNo", "ACCT_NO", false, "NVarChar", 30, 0, 0, false, "", null, typeof(System.String), 1 );
        
}
 

分析这个结果的数据存放格式和地方,不过有些遗憾。目前我只能做到,根据实体生成SQL语句,也就是框架的做法,对于根据SQL表名或是字段名称,得到它对应的实体名称或是属性名,这个还没有做到。

 

第三种技术方案,也就是这里的方案,主动的解析LLBL Gen的设计器项目文件,Framework.llblgenproj

image

有三个网格,第一个是显示表名和对应的实体名称,下面的一个,显示这个表的主键,右边的则显示表的所有字段,如果字段是主键,则用阴影背景显示。

解析Framework.llblgenproj项目文件,它是Xml文件,首推Linq to xml技术。

首先找到表与实体的映射关联。EntityMappings节点下面的内容,是表与实体映射内容

EntityName=":Attachment" TargetName="Framework:dbo:Attachment"

这一层最容易查找,于是填充第一个网格。

 

其次找字段,在节点EntityDefinitions下面,分解它的源代码如下所示

foreach (XElement field in item.Elements("Fields").Elements())
{
 EntityMappingClass mappingClass = new EntityMappingClass()
       {
       FieldIndex = Convert.ToInt16(field.Attribute("FieldIndex").Value),
       Name = Convert.ToString(field.Attribute("Name").Value),
       Type = Convert.ToString(field.Attribute("Type").Value)
                                                          };
       if (field.Attribute("MaxLength")!=null)
             mappingClass.MaxLength = Convert.ToDecimal(field.Attribute("MaxLength").Value);
                    
       if (field.Attribute("Precision")!=null)
             mappingClass.Precision = Convert.ToInt16(field.Attribute("Precision").Value);

       if (field.Attribute("IsOptional")!=null)
           mappingClass.IsOptional = Convert.ToBoolean(field.Attribute("IsOptional").Value);
                    
       fields.Add(mappingClass);
}

对于不一定会存在的节点属性(attribute),要行判断是否存在。

经过上面一层的查找,已经找到实体及其所有的属性,还需要找实体的属性对应的数据库字段,于是继续分析EntityMappings节点。

foreach (XElement field in entity.Elements().First().Elements())
{
    //<FieldMapping FieldName="AttmFile" TargetFieldName="File" 
     var items = (from item in targetfields
                         where item.Name == field.Attribute("FieldName").Value
                                     select item).First();

      items.TargetFieldName = field.Attribute("TargetFieldName").Value;
  }

如上代码所示,找到属性映射的字段值之后,更新它所映射的字段名称。

到此,第三个网格,显示实体的属性,已经完成。这里再加点功能,给是主键的字段背景颜色,以方便识别。

于是,继续查找节点TargetDatabases,它的子节点中有存储字段是否为主键的信息。

foreach (XElement field in xElement.Elements().First().Elements())
{
        IsPrimaryKey = false;
         //<Field Name="Recnum" Ordinal="1"  IsPrimaryKey="true" IsIdentity="true" DbType="5" Precision="8" />
        if (field.Attribute("IsPrimaryKey") != null)
                  IsPrimaryKey = Convert.ToBoolean(field.Attribute("IsPrimaryKey").Value);

        var items = (from item in targetfields
                      where item.TargetFieldName == field.Attribute("Name").Value
                      select item).First();
        items.IsPrimarykKey = IsPrimaryKey;
 }

于是,第二个网格的数据已经找到。

至此,我已经完整的设计出Entity Mapping功能:根据实体名找出数据库表名,根据表名称,找出对应的程序中的实体名称,二者相互查找。

Linq技术读取Xml文件确实相当方便,但是不可以用XPath功能,不可以从根节点直接定位到某一层的子节点,Linq to Xml只能一层层的钻取,First/Last然后Elements,再钻取。有时候为了验证代码的正确性,常常借助于Visual Studio的Debugger来写代码,在Immediate Window中输入预想的代码,让它来验证代码是否正确。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值