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

这里有三个关键词:轻量级实体类数据容器,还有一个潜在的关键词:通用。这几个名词之间有什么联系呢?
一般来说,操作实体类往往伴随着一个实体类集合,而这些集合就是实体类的容器,在这里我将“容器”视作一个比集合更广泛的概念,例如Entity Framework做了一个重量级的容器ObjectContext,用于与作为对象(这些对象为 EDM 中定义的实体类型的实例)的数据进行交互。
实体类与容器没有必然关系,例如DataSet也是一个容器,它存储并操作DataTable,而DataTable也可以看做是各个单元格数据的容器...
但是,这些“数据容器”还是显得比较重量级,里面有太多要交互的自对象,为此我在PDF.NET(PWMIS数据开发框架)中定义了一个非常轻量级的实体数据容器,它存储数据的原则很简单,就是一个object[][],外加一个对应的字段名称数组,其它诸如表的元素据等信息都没有存储,也就是下面程序中的3个私有对象:
/// /// 实体数据容器 /// public class EntityContainer { private string[] fieldNames; private List Values; private object[] currValue; }
实体容器接收一个DataReader对象,将其中的数据读入Values 数组,下面是相应的方法代码:
/// /// 执行DataReader查询,并将查询结果缓存 /// /// 数据阅读器
/// 结果行数 public int Execute(IDataReader reader) { List list = new List(); 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) 方法,它不必对每列进行数据读取,所以效率较高。
现在数据存放进去了,如何使用呢?为了做到通用,具体每个数据的使用还是交给使用者自己去处理吧,所以采用一个委托方法来处理:
/// /// 采用自定义的映射方式,将数据容器中的数据映射到指定的类中 /// /// 结果类型 /// 处理数据的方法
/// public IEnumerable Map(Func fun) where TResult : class, new() { 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((e) => { e.Age = ec.GetItemValue("Age"); e.ID = ec.GetItemValue("ID"); e.Name = ec.GetItemValue("name");//不区分大小写 return e; } ).ToList ();
除了可以使用 GetItemValue(string fieldName) 方法来获取迭代的当前行的某列数据外,也可以使用 GetItemValue(int fieldIndex) 方法。
另外,还提供了一个将数据映射到PDF.NET实体类的方法,下面是方法的定义:
/// /// 将数据从容器中映射到实体中 /// /// /// public IEnumerable Map() 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) .JoinIn(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>();
如果觉得这样分别使用两个实体对象集合( 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("GroupName"); e.ID = ec.GetItemValue("ID"); e.Name = ec.GetItemValue("name");//不区分大小写 return e; } ).ToList ();

上面的写法没有LINQ那么完美,人家LINQ是近水楼台先得月,MS自家的苗子,可以依靠“编译器语法糖”来写出优美的LINQ程序,但我们的这个实现从原理上说非常轻巧,在众多非官方的ORM框架中,真正支持了实体类的多表连接查询!
有关OQL的多实体连接查询仅在PDF.NET框架V4.1以后版本支持,该功能作为框架的一项重要功能扩展,尚未投入商用,感兴趣的朋友可以一起研究。

Tags:  实体类的作用 抽象类继承实体类 什么是实体类 实体类 轻量级容器

延伸阅读

最新评论

发表评论