• 打造轻量级的实体类数据容器


        这里有三个关键词:轻量级实体类数据容器,还有一个潜在的关键词:通用。这几个名词之间有什么联系呢?

        一般来说,操作实体类往往伴随着一个实体类集合,而这些集合就是实体类的容器,在这里我将“容器”视作一个比集合更广泛的概念,例如Entity Framework做了一个重量级的容器ObjectContext,用于与作为对象(这些对象为 EDM 中定义的实体类型的实例)的数据进行交互。

        实体类与容器没有必然关系,例如DataSet也是一个容器,它存储并操作DataTable,而DataTable也可以看做是各个单元格数据的容器...

        但是,这些“数据容器”还是显得比较重量级,里面有太多要交互的子对象,为此我在PDF.NET(PWMIS数据开发框架)中定义了一个非常轻量级的实体数据容器,它存储数据的原则很简单,就是一个object[][],外加一个对应的字段名称数组,其它诸如表的元素据等信息都没有存储,也就是下面程序中的3个私有对象:

        /// <summary>
        
    /// 实体数据容器
        
    /// </summary>
        public class EntityContainer
        {
            
    private string[] fieldNames;
            
    private List<object[]> Values;
            
    private object[] currValue;

        }

    实体容器接收一个DataReader对象,将其中的数据读入Values 数组,下面是相应的方法代码:

            /// <summary>
            
    /// 执行DataReader查询,并将查询结果缓存
            
    /// </summary>
            
    /// <param name="reader">数据阅读器</param>
            
    /// <returns>结果行数</returns>
            public int Execute(IDataReader reader)
            {
                List
    <object[]> list = new List<object[]>();
                
    using (reader)
                {
                    
    if (reader.Read())
                    {
                        
    int fcount = reader.FieldCount;
                        fieldNames 
    = new string[fcount];
                        
    object[] values = null;

                        
    for (int i = 0; i < fcount; i++)
                            fieldNames[i] 
    = reader.GetName(i);

                        
    do
                        {
                            values 
    = new object[fcount];
                            reader.GetValues(values);
                            list.Add(values);
                        } 
    while (reader.Read());

                    }
                }
                
    this.Values = list;
                
    return list.Count;
            }

    程序中使用 reader.GetValues(values) 方法,它不必对每列进行数据读取,所以数据读取的效率较高。

    现在数据存放进去了,如何使用呢?为了做到通用,具体每个数据的使用还是交给使用者自己去处理吧,所以采用一个委托方法来处理:

            /// <summary>
            
    /// 采用自定义的映射方式,将数据容器中的数据映射到指定的类中 
            
    /// </summary>
            
    /// <typeparam name="TResult">结果类型</typeparam>
            
    /// <param name="fun">处理数据的方法</param>
            
    /// <returns></returns>
            public IEnumerable<TResult> Map<TResult>(Func<TResult> fun) where TResult : classnew()
            {
                
    if (this.Values != null && this.fieldNames != null)
                {
                    
    foreach (object[] itemValues in this.Values)
                    {
                        TResult t 
    = new TResult();
                        
    this.currValue = itemValues;
                        fun(t);
                        
    yield return t;
                    }
                }
                
    else
                {
                    
    throw new Exception("EntityContainer 错误,调用该方法前请先调用Execute 方法。");
                }
            }

    下面是该方法的使用示例:

                EntityContainer ec = new EntityContainer(q, db);
                ec.Execute();
               
     var mapUser2= ec.Map<User>((e) => 
                {
                    e.Age 
    = ec.GetItemValue<int>("Age");
                    e.ID 
    = ec.GetItemValue<int>("ID");
                    e.Name 
    = ec.GetItemValue<string>("name");//不区分大小写
                    return e; 
                }
                ).ToList ();

     除了可以使用 GetItemValue<T>(string fieldName) 方法来获取迭代的当前行的某列数据外,也可以使用 GetItemValue<T>(int fieldIndex) 方法。

    另外,还提供了一个将数据映射到PDF.NET实体类的方法,下面是方法的定义:

            /// <summary>
            
    /// 将数据从容器中映射到实体中
            
    /// </summary>
            
    /// <typeparam name="T"></typeparam>
            
    /// <returns></returns>
            public IEnumerable<T> Map<T>() where T : EntityBase{
                 //具体代码略
            }

    上面的测试例子中,User类是一个实体类,所以可以用下面的方式直接获取该类的实例对象集合:

                EntityContainer ec = new EntityContainer(q, db);
                ec.Execute();
                var mapUser1 
    = ec.Map<User>().ToList ();

    在Map方法中,可以映射出任意PDF.NET实体类,或者其它自定义的POCO实体类,而且没有映射次数限制。看到这里聪明的你也许要问了,上面的例子可以映射User之外的实体吗?答案是完全可以!

    先看一个例子,我们假设系统中还存在一个实体类 Group,我们使用PDF.NET的OQL表达式写一个支持两个实体连接查询的语句:

    OQL q=OQL.From(user)
             .
    InnerJoin(group) //连接Group实体
             .On(user.GroupID,
    group.ID)
             .Select(user.ID,user.Name,group.GroupName)
    //选取指定的字段

    下面就可以映射出两个实体集合了:

                EntityContainer ec = new EntityContainer(q, db);
                ec.Execute(); 
    //可以省略此行调用
                var mapUser1 
    = ec.Map<User>().ToList ();
                var mapGroup1
    = ec.Map<Group>().ToList();

    如果觉得这样分别使用两个实体对象集合( user和group)比较麻烦,那么再自定义一个“用户机构”类即可:

     class UserGroup 
    {
        
    int ID{get;set;}
        
    string Name{get;set;}
        
    string GroupName{get;set;}
    }

    之后,可以像下面这样来使用数据:

                EntityContainer ec = new EntityContainer(q, db);
                //ec.Execute();//可以省略此行调用

                var mapEntity
    = ec.Map<UserGroup>((e) => 
                {
                    e.GroupName 
    = ec.GetItemValue<int>("GroupName");
                    e.ID 
    = ec.GetItemValue<int>("ID");
                    e.Name 
    = ec.GetItemValue<string>("name");//不区分大小写
    /*

    //或者使用索引的方式,但必须明确上面OQL表达式Select方法里面属性的顺序 
                    e.GroupName = ec.GetItemValue<strng>(2);
                    e.ID = ec.GetItemValue<int>(0);
                    e.Name = ec.GetItemValue<string>(1);

    */
                    
    return e; 
                }
                ).ToList ();

      

    上面的写法没有LINQ那么完美,人家LINQ是近水楼台先得月,MS自家的苗子,可以依靠“编译器语法糖”来写出优美的LINQ程序,但我们的这个实现从原理上说非常轻巧,在众多非官方的ORM框架中,真正支持了实体类的多表连接查询!

    有关OQL的多实体连接查询仅在PDF.NET框架V4.1以后版本支持,该功能作为框架的一项重要功能扩展,已经在商业项目中开始使用,感兴趣的朋友可以一起研究。

    下面的代码是实际项目中的一段代码,我们来看看完整的调用方式:

    public string GetTradeTypeID(string foundAccount,string jjdm,string type)
            {
                
    /*执行下面的查询将使用如下类似的SQL:
                 select distinct a.tradetype tradetypeid from wft_customerfundtrade a
                     left join wft_customerfundtradedetails b on a.tradetype =b.tradetypeid
                          where   (a.fundaccount ='1185919705'  and a.jjdm ='KF0003')
                             and ( b.tradetype='定投' or b.tradetype='基金转换的记帐')
                 * 
                 
    */
                WFT_CustomerFundTrade trade 
    = new WFT_CustomerFundTrade() { FundAccount = foundAccount, JJDM = jjdm };
                WFT_CustomerFundTradeDetails detail 
    = new WFT_CustomerFundTradeDetails() { TradeType = type };
                OQLCompare cmpAll 
    = new OQLCompare(trade, detail);

                OQL q 
    = OQL.From(trade)
                    .LeftJoin(detail).On(trade.TradeType, detail.TradeTypeId)
                    .Select(trade.TradeType)
                    .Where(
                            (cmpAll.Comparer(trade.FundAccount) 
    & cmpAll.Comparer(trade.JJDM)) &
                            (cmpAll.Comparer(detail.TradeType) 
    | cmpAll.Comparer(detail.TradeType, "=""基金转换的记帐"))
                    )
                    .END;

                q.Distinct 
    = true;

                EntityContainer container 
    = new EntityContainer(q);
                var result 
    = container.Map<WFT_CustomerFundTrade>().ToList();
                
    if (result.Count == 0)
                    
    return "";
                
    else
                    
    return result[0].TradeType;
    }

    由这个例子可以看出,PDF.NET的ORM框架中的实体对象查询语言--OQL,已经可以完成很复杂的查询了,包括多实体类关联查询。

  • 相关阅读:
    gitlab pipeline optimize 效率优化
    gitlab pipeline 构建容器镜像方法
    软件学习目录索引
    面试题收集
    【docker】4种网络模式
    GeoIP库商业版调研支持IPV6
    MySQL在线开启GTID模式
    Linux IO分析
    一个宝藏级微服务开源项目,是真的牛批!
    powerdesigner 如何设置name和code不自动同步
  • 原文地址:https://www.cnblogs.com/bluedoctor/p/2054541.html
Copyright © 2020-2023  润新知