免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 5803 | 回复: 0
打印 上一主题 下一主题

打造轻量级的实体类数据容器 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-06-02 16:08 |只看该作者 |倒序浏览
打造轻量级的实体类数据容器


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

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

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

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

  1.     /// <summary>
  2.     /// 实体数据容器
  3.     /// </summary>
  4.     public class EntityContainer
  5.     {
  6.         private string[] fieldNames;
  7.         private List<object[]> Values;
  8.         private object[] currValue;

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

  1.         /// <summary>
  2.         /// 执行DataReader查询,并将查询结果缓存
  3.         /// </summary>
  4.         /// <param name="reader">数据阅读器</param>
  5.         /// <returns>结果行数</returns>
  6.         public int Execute(IDataReader reader)
  7.         {
  8.             List<object[]> list = new List<object[]>();
  9.             using (reader)
  10.             {
  11.                 if (reader.Read())
  12.                 {
  13.                     int fcount = reader.FieldCount;
  14.                     fieldNames = new string[fcount];
  15.                     object[] values = null;

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

  18.                     do
  19.                     {
  20.                         values = new object[fcount];
  21.                         reader.GetValues(values);
  22.                         list.Add(values);
  23.                     } while (reader.Read());

  24.                 }
  25.             }
  26.             this.Values = list;
  27.             return list.Count;
  28.         }
复制代码
程序中使用 reader.GetValues(values) 方法,它不必对每列进行数据读取,所以数据读取的效率较高。

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

  1.         /// <summary>
  2.         /// 采用自定义的映射方式,将数据容器中的数据映射到指定的类中
  3.         /// </summary>
  4.         /// <typeparam name="TResult">结果类型</typeparam>
  5.         /// <param name="fun">处理数据的方法</param>
  6.         /// <returns></returns>
  7.         public IEnumerable<TResult> Map<TResult>(Func<TResult> fun) where TResult : class, new()
  8.         {
  9.             if (this.Values != null && this.fieldNames != null)
  10.             {
  11.                 foreach (object[] itemValues in this.Values)
  12.                 {
  13.                     TResult t = new TResult();
  14.                     this.currValue = itemValues;
  15.                     fun(t);
  16.                     yield return t;
  17.                 }
  18.             }
  19.             else
  20.             {
  21.                 throw new Exception("EntityContainer 错误,调用该方法前请先调用Execute 方法。");
  22.             }
  23.         }
复制代码
下面是该方法的使用示例:


  1.             EntityContainer ec = new EntityContainer(q, db);
  2.             ec.Execute();
  3.             var mapUser2= ec.Map<User>((e) =>
  4.             {
  5.                 e.Age = ec.GetItemValue<int>("Age");
  6.                 e.ID = ec.GetItemValue<int>("ID");
  7.                 e.Name = ec.GetItemValue<string>("name");//不区分大小写
  8.                 return e;
  9.             }
  10.             ).ToList ();
  11. 除了可以使用 GetItemValue<T>(string fieldName) 方法来获取迭代的当前行的某列数据外,也可以使用 GetItemValue<T>(int fieldIndex) 方法。

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



  13.         /// <summary>
  14.         /// 将数据从容器中映射到实体中
  15.         /// </summary>
  16.         /// <typeparam name="T"></typeparam>
  17.         /// <returns></returns>
  18.         public IEnumerable<T> Map<T>() where T : EntityBase{
  19.              //具体代码略

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



  22.             EntityContainer ec = new EntityContainer(q, db);
  23.             ec.Execute();
复制代码
var mapUser1 = ec.Map<User>().ToList ();
在Map方法中,可以映射出任意PDF.NET实体类,或者其它自定义的POCO实体类,而且没有映射次数限制。看到这里聪明的你也许要问了,上面的例子可以映射User之外的实体吗?答案是完全可以!

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

  1. OQL q=OQL.From(user)
  2.          .JoinIn(group) //连接Group实体
  3.          .On(user.GroupID=group.ID)
  4.          .Select(user.ID,user.Name,group.GroupName) //选取指定的字段
  5. 下面就可以映射出两个实体集合了:



  6.             EntityContainer ec = new EntityContainer(q, db);
  7.             ec.Execute();
  8.             var mapUser1 = ec.Map<User>().ToList ();
  9.             var mapGroup1= ec.Map<Group>().ToList();
  10. 如果觉得这样分别使用两个实体对象集合( user和group)比较麻烦,那么再自定义一个“用户机构”类即可:



  11.             class UserGroup
  12.             {
  13.                int ID{get;set;}
  14.                string Name{get;set;}
  15.                string GroupName{get;set;}
  16.             }

  17.             EntityContainer ec = new EntityContainer(q, db);
  18.             ec.Execute();
  19.             var mapEntity= ec.Map<UserGroup>((e) =>
  20.             {
  21.                 e.GroupName = ec.GetItemValue<int>("GroupName");
  22.                 e.ID = ec.GetItemValue<int>("ID");
  23.                 e.Name = ec.GetItemValue<string>("name");//不区分大小写
  24.                 return e;
  25.             }
  26.             ).ToList ();

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



有关OQL的多实体连接查询仅在PDF.NET框架V4.1以后版本支持,该功能作为框架的一项重要功能扩展,尚未投入商用,感兴趣的朋友可以一起研究。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP