日期:2014-05-20  浏览次数:21014 次

让ORM框架支持多表(多实体)连接查询
    很多ORM框架都说,ORM不是用来做复杂的多表查询的,主要是实现起来比较困难,另外很多大牛也说用ORM实现多表查询有违OOAD的精神,即便LINQ TO SQL出来了也被大家说成这不是心目中的ORM。说归说,做归做,我们用一个简单的方案来实现ORM多表查询的问题。

    相信大家写一个多表连接的SQL语句不困难,做一个可以多个实体连接的东西也不难,难就难在查询的结果如何映射的问题,人家LINQ有“投影”机制,使用select 语句将结果投射到一个新的对象即可,如果我们自己实现一个类似的功能比较复杂,毕竟LINQ使用了编译器语法糖,除非我们自己也实现一个Linq Provider,除了这条路,还有别的方式吗?
前面说了,ORM多表查询的问题包含一个连表(连实体)查询,另外一个问题就是结果映射,由此可以得到还有一个问题就是结果的存放,要有一个容器来处理这些问题。

    一般来说,操作实体类往往伴随着一个实体类集合,而这些集合就是实体类的容器,在这里我将“容器”视作一个比集合更广泛的概念,例如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];