首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > .NET > .NET >

让ORM框架支持多表(多实业)连接查询

2012-12-19 
让ORM框架支持多表(多实体)连接查询很多ORM框架都说,ORM不是用来做复杂的多表查询的,主要是实现起来比较困

让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];


                        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 : 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<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)
         .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>().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");//不区分大小写
                return e; 
            }
            ).ToList ();


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

原文地址:
有关PDF.NET更多信息,请查看官方地址:http://www.pwmis.com/sqlmap

[最优解释]
不错不错!
[其他解释]
linq to sql
[其他解释]
再看了一遍。
[其他解释]
帖子写的不错。。。
[其他解释]
lz是强人,请教lz一个问题。

如何把 C# 中使用 yield return 的代码改写成 VB.NET 的代码。
[其他解释]
其实你的PDF.NET 也可以实现这种功能

就是想查哪张表 查哪张表

不需要lazyload  避免生成一些垃圾的SQL语句。

可以重写一下criteria接口。把QBC查询从新构造一遍 实现“自由查询”

避免一些不必要的SQL  从而提高效率。
[其他解释]
最近比较流行的数据框架:CYQ.Data 数据框架,已经开源了几个版本的源码。
[其他解释]
可以将多表做成一个视图,然后再ORM中作为一个对象查询啊!
[其他解释]
看不懂啊
[其他解释]
学习拿分

[其他解释]
呵呵 C# 啊 我看到標題以為是JAVAEE的,走錯了。
[其他解释]
用的好是关键
[其他解释]
不懂C#.
一个多表查询还要搞这么多东东,ORM什么玩意儿啊,
[其他解释]
学习 学习
[其他解释]
学习!还没看过这东西呢!
[其他解释]
还没有深入学习ORM,楼主的代码一点都看不明???
看来还要加倍努力!!
[其他解释]
学习。。。。
[其他解释]
收藏,学习!
------其他解决方案--------------------


不错,看看去
[其他解释]
学习学习
[其他解释]
该回复于2012-08-28 09:17:14被版主删除
[其他解释]
顶,正在学习,学着慢慢写一个自己的框架。不知是否有必要,还是直接学习使用Nhibernate就可以了?
[其他解释]
值得学习
[其他解释]

引用:
有关框架的入门使用,可以到这里下载:
http://download.csdn.net/source/2052993


谢谢,有学习的方向了
[其他解释]
引用:
引用:
lz是强人,请教lz一个问题。

如何把 C# 中使用 yield return 的代码改写成 VB.NET 的代码。

 参考 http://stackoverflow.com/questions/97381/yield-in-vb-net

C# code

public IEnumerator<mList> GetEnumerator(……


那是因为 List 本身支持 yield。

要实现 yield 需要2点,一个是对堆栈的保护和恢复,一个是实现一个附加的线程。

尤其是将递归转换成迭代器,貌似很难。
[其他解释]
http://www.jiamaocode.com/ProCts/2011/03/11/1796/1796.html

以前也有过封装..只是自已在用..
[其他解释]
不错~~~~~~~
[其他解释]
我也来支持一下吧
[其他解释]
没有发言权 帮顶
[其他解释]
orm框架用的好就非常清松 
[其他解释]
蹭楼来了
[其他解释]
学习  一下了  谢谢、、、
[其他解释]
 8错!  支持一下。。
[其他解释]
56789786535467865432
[其他解释]
   怎么用C#写的呢,java的呢?
[其他解释]
3QQ
[其他解释]
学习 一下了 谢谢、、、
[其他解释]
我还要去打酱油……

[其他解释]
这个没用过。。
[其他解释]
sddwdd
[其他解释]
ORM不是用来做复杂的多表查询的
[其他解释]
还没认真看,mark一个
[其他解释]
太奇怪了,这个ENTITY不应该是简化程序的吗?但怎么感觉对一些复杂的逻辑还不如ADO.NET来得方便,现在一直都没有使用!
[其他解释]
对于复杂的SQL没有银弹。
C#中的数据查询做得像SQL,有必要吗?
[其他解释]
!!!!!!!!!!!!!!!11
[其他解释]
不错不错。。。。


[其他解释]
学习学习
[其他解释]
学习飘过拿分
[其他解释]
我自己写的ORM框架也能实现多实体连接分页查询 :)
不过写法没LZ写的那样简洁
[其他解释]
该回复于2011-05-31 08:32:04被版主删除
[其他解释]
[img=http://C:\Documents and Settings\Administrator][/img]
[其他解释]

引用:
引用:
sql语句更是麻烦

如果有某种语言能够比结构化查询语言简单,那也轮不到你我来发明,
换一种表达:如果真是这样,关系型数据库的地位早就颠覆了

多表联合查询也属于简单查询,
实际上,如果稍微做一些设计,基本上就不需要所谓多表联合查询,
你们太小看"结构化查询"了,

很多开发"内容提供系统"的程序员不懂什么是数据库开发……


讲的不错。楼主的东西也不错。
[其他解释]
[img=http://C:\Documents and Settings\Administrator][/img]
[其他解释]
标记下
[其他解释]
引用:
多表查询不外乎可以写成视图或存储过程嘛.只要你的ORM支持这些.都不是问题呀.

1,视图不可以在定义中带参数;
2,可能会有很多的连表查询,条件都不一样,不可能都定义成视图;
3,为连表查询都去定义一个存储过程,调用和维护都很麻烦。

所以,为简化编程,ORM应该直接支持连表查询。

[其他解释]
其实如果把EF里的“某些”查询方法 转移到Nhibernate里 

那Nhibernate 就更完美了

比如 include path

->emp->depatment.Name

这种的话 Nhibernate 会不会是最强大的呢?
[其他解释]
ICriteria接口

喝多了 不好意思。


[其他解释]
该回复于2011-05-27 08:49:44被版主删除
[其他解释]
引用:
lz是强人,请教lz一个问题。

如何把 C# 中使用 yield return 的代码改写成 VB.NET 的代码。

 参考 http://stackoverflow.com/questions/97381/yield-in-vb-net

public IEnumerator<mList> GetEnumerator()
{
    foreach (mList column in mItems)
        yield return column;
    yield break;
}



Public Function GetEnumerator() As IEnumerator(Of mList) Implements IEnumerable(Of mList).GetEnumerator
    Return mItems.GetEnumerator
End Function


[其他解释]
引用:
可以将多表做成一个视图,然后再ORM中作为一个对象查询啊!

如果系统中有很多多表查询的SQL,且通用性很少,都做成视图,这样开发起来就很不灵活了,另外有一些多表查询需要查询条件(参数),在视图里面是没法包含参数的。

[其他解释]
引用:
其实如果把EF里的“某些”查询方法 转移到Nhibernate里 

那Nhibernate 就更完美了

比如 include path

->emp->depatment.Name

这种的话 Nhibernate 会不会是最强大的呢?

使用Lambda表达式即可达到这种效果吧?



PDF.NET没有使用lazyload,在调用EntityQueru<Entity>.QueryList(OQL)的时候就立即执行查询了,可以认为OQL就是对象化的SQL,它们之间有直接的映射关系,不会生成垃圾的SQL语句。

[其他解释]

引用:
顶,正在学习,学着慢慢写一个自己的框架。不知是否有必要,还是直接学习使用Nhibernate就可以了?

自己写一个框架是有必要的,不要怕被人嘲笑造轮子,如果你不开始尝试去做一个,你永远造不出更好的轮子。

[其他解释]
有关框架的入门使用,可以到这里下载:
http://download.csdn.net/source/2052993
[其他解释]
引用:
呵呵 C# 啊 我看到標題以為是JAVAEE的,走錯了。

PDF.NET的思想是通用的,理论上Java也能实现一个,现在只实现了.NET平台的,所以才叫做PDF.NET.

[其他解释]
修改:那是因为 List 本身支持枚举。
[其他解释]
引用:
http://www.jiamaocode.com/ProCts/2011/03/11/1796/1796.html

以前也有过封装..只是自已在用..

http://www.jiamaocode.com/ 是你的个人网站吗?内容很丰富啊

[其他解释]
该回复于2011-05-27 13:29:52被版主删除
[其他解释]
该回复于2011-05-27 15:56:54被版主删除
[其他解释]
引用:
对于复杂的SQL没有银弹。
C#中的数据查询做得像SQL,有必要吗?

主要的任务应该是减少代码量,减少耦合性;对于不好处理的SQL问题应该集中在一起,不应该分散到各处。
[其他解释]
123
[其他解释]
引用:
引用:
你说的复杂逻辑用ADO.NET意思就是写复杂的SQL语句吧?
在程序里面直接用SQL语句有很多缺点:
1,语法容易写错(对新手而言);
2,字段名不好记,不好写,容易写错;
3,没有编译时候检查SQL语句;
4,大量的SQL语句分散在程序中,不好管理;
5,无法避免拼接SQL语句,有可能造成SQL注入问题;
6,要求开发人员属性数据库……

当SQL语句复杂时,是条不归路,再看看你的代码,以上优点还有几条。

当SQL语句复杂时,使用ORM的连接查询,优势仍然存在,只是你不觉得SQL语句本身复杂而已。
实际上,ORM的设计理念是OOAD的,不是给你去实现复杂SQL查询的,设计理念和出发点都是不一样的,如果遵循OOAD,那么系统很少会真正用上复杂的查询,复杂的查询是无法缓存的,但将复杂查询拆分成多次的简单查询,缓存命中率就很高了。

我们的DBA说SQLSERVER有缓存,但经过实验发现,同样的SQL语句,不管复杂不复杂,多次执行的时间还是多次的,而不是近与一次的,这种情况就是想尽办法优化数据库也不可能整体上提升系统性能。

[其他解释]
引用:
太奇怪了,这个ENTITY不应该是简化程序的吗?但怎么感觉对一些复杂的逻辑还不如ADO.NET来得方便,现在一直都没有使用!

你说的复杂逻辑用ADO.NET意思就是写复杂的SQL语句吧?
在程序里面直接用SQL语句有很多缺点:
1,语法容易写错(对新手而言);
2,字段名不好记,不好写,容易写错;
3,没有编译时候检查SQL语句;
4,大量的SQL语句分散在程序中,不好管理;
5,无法避免拼接SQL语句,有可能造成SQL注入问题;
6,要求开发人员属性数据库结构;
7,由于上面的问题,导致写SQL语句效率低下

使用Entity可以避免上面的缺点,使得开发迅速,安全,Bug少,易维护易扩展。



[其他解释]
引用:
你说的复杂逻辑用ADO.NET意思就是写复杂的SQL语句吧?
在程序里面直接用SQL语句有很多缺点:
1,语法容易写错(对新手而言);
2,字段名不好记,不好写,容易写错;
3,没有编译时候检查SQL语句;
4,大量的SQL语句分散在程序中,不好管理;
5,无法避免拼接SQL语句,有可能造成SQL注入问题;
6,要求开发人员属性数据库结构;
7,由于上面的问题,导致写SQL语句效率低下

使用Entity可以避免上面的缺点,使得开发迅速,安全,Bug少,易维护易扩展。

当SQL语句简单时,这些是优点。
当SQL语句复杂时,是条不归路,再看看你的代码,以上优点还有几条。
[其他解释]
1,与SQL语句基本相似,甚至可以说看起来更复杂了。
------------
前面说过,框架没有使用LINQ技术,LINQ能够写得那么流畅,依靠的是编译器语法糖,而我的框架,走的是全函数式风格;程序中用的SQL语句是字符串,程序代码与字符串相比较,自然没有字符串那么灵活,所以要准确的表达跟SQL完全一致的语义,程序表达起来“显得有点复杂”,不过相信大家也看得出来,复杂度没有增加太多。



2,另外从你的ORM中没有看出任何与缓存有关的东西
------------
目前框架没有做缓存,因为现在已经有很多成熟的强大的缓存产品,PDF.NET只做自己最专注的。

3,所以说SQLSERVER缓存与优化数据库扯不上太多关系。
-----------
这两者是没有太大关系,但那种认为只要数据库强大(例如很强劲的服务器上运行一个NB的数据库系统),数据库内存足够多(好缓存嘛,我们的DBA说的),系统性能就能够有显著提升的看法,是站不住脚的。
[其他解释]

当SQL语句复杂时,使用ORM的连接查询,优势仍然存在,只是你不觉得SQL语句本身复杂而已。
实际上,ORM的设计理念是OOAD的,不是给你去实现复杂SQL查询的,设计理念和出发点都是不一样的,如果遵循OOAD,那么系统很少会真正用上复杂的查询,复杂的查询是无法缓存的,但将复杂查询拆分成多次的简单查询,缓存命中率就很高了。

我们的DBA说SQLSERVER有缓存,但经过实验发现,同样的SQL语句,不管复杂不复杂,多次执行的时间还是多次的,而不是近与一次的,这种情况就是想尽办法优化数据库也不可能整体上提升系统性能。


对于ORM来说,连接查询确实是比较容易实现的东西。问题是我看你的连接查询,与SQL语句基本相似,甚至可以说看起来更复杂了。
系统很少会真正用上复杂的查询?难道所有的SQL语句都只是简单单一的insert,update,delete。没有同步更新的表格?没有事务?
另外从你的ORM中没有看出任何与缓存有关的东西,更不要说缓存的同步自动更新了。
你们的DBA说的没错,SQLSERVER确实有缓存,而且对于内存的胃口大的惊人。不过内存再大,缓存也有淘汰策略,真正有效的缓存不应该数据库做的,做程序的应该知道对于哪些数据的缓存需求更迫切,所以说SQLSERVER缓存与优化数据库扯不上太多关系。
[其他解释]
该回复于2011-05-28 09:00:43被版主删除
[其他解释]
引用:
引用:
你说的复杂逻辑用ADO.NET意思就是写复杂的SQL语句吧?
在程序里面直接用SQL语句有很多缺点:
1,语法容易写错(对新手而言);
2,字段名不好记,不好写,容易写错;
3,没有编译时候检查SQL语句;
4,大量的SQL语句分散在程序中,不好管理;
5,无法避免拼接SQL语句,有可能造成SQL注入问题;
6,要求开发人员属性数据库……

sql语句更是麻烦
[其他解释]
该回复于2011-05-30 11:21:43被版主删除
[其他解释]
我自己写的ORM框架也能实现多实体连接分页查询 :)
不过写法没LZ写的那样简洁
[其他解释]
学习了啊,谢谢呀
[其他解释]
引用:
sql语句更是麻烦

如果有某种语言能够比结构化查询语言简单,那也轮不到你我来发明,
换一种表达:如果真是这样,关系型数据库的地位早就颠覆了

多表联合查询也属于简单查询,
实际上,如果稍微做一些设计,基本上就不需要所谓多表联合查询,
你们太小看"结构化查询"了,

很多开发"内容提供系统"的程序员不懂什么是数据库开发
[其他解释]
实在是好帖阿.!!!顶一个.耶耶耶
[其他解释]
好东西,当然要收藏好,谢谢了

[其他解释]
学习。
[其他解释]
引用:
我自己写的ORM框架也能实现多实体连接分页查询 :)
不过写法没LZ写的那样简洁

兄弟可以秀一下?

[其他解释]
学习学习
[其他解释]
还不错
[其他解释]
该回复于2011-05-30 17:13:25被版主删除
[其他解释]
这些 不是java的hibernate吗
[其他解释]
看了,不过一致没用过,
[其他解释]
引用:
这些 不是java的hibernate吗

PDF.NET框架借鉴了Hibernate,iBatis的思想,所以看起来有点类似,欢迎支持使用!
[其他解释]
看看的。
[其他解释]
顶一下,谢谢!
------其他解决方案--------------------


该回复于2011-05-31 10:25:14被版主删除
[其他解释]
该回复于2011-05-31 10:19:58被版主删除
[其他解释]
该回复于2011-06-07 08:41:57被版主删除
[其他解释]
该回复于2011-05-31 10:25:16被版主删除
[其他解释]
null
[其他解释]
该回复于2011-06-01 10:20:05被版主删除
[其他解释]
多表查询不外乎可以写成视图或存储过程嘛.只要你的ORM支持这些.都不是问题呀.

热点排行