在SQL中,要在一个一对多关系中查询数据,不免要使用JOIN关键字。在Entity Framework中,由于引入了Navigation属性的概念,我们可以通过Navigation属性,直接在多个实体之间进行查询而不必过多的关心 主键、外键约束……本文将通过一个Demo,来展现如何通过Entity SQL,方便在1对多关系的实体间进行查询。
进入正题之前,让我们来看一下当前数据库的情形,以及我们将要完成的任务。
本文主要将涉及到两个表的查询:Warehouse和Notebook。Warehouse中有三个仓库的记录;Notebook中有6台笔记本的记录;每个笔记本至多属于一个仓库,每个仓库可以存储0个或者多个笔记本。于是,在笔记本与仓库之间,构成N:1的关系。
本文将要进行两组查询,分别给出SQL查询与Entity SQL的查询语句,以方便比较。第一组查询,以1端为条件,查询多端结果;第二组查询,以多端为条件,反过来查询1端的结果。
实例1 查询出全部存放在仓库的城市为"SH"的笔记本——N到1查询
SQL实现如下:
Select n.* FROM Notebook as n INNER JOIN Warehouse as w ON n.WarehouseId=w.Id Where w.City='SH' |
我们把Notebook与Warehouse通过仓库ID内联以后,以1端,即Warehouse端作为条件,查询出全部在上海的笔记本。下面的Entity SQL语句,可以达到相同的效果:
Select VALUE n FROM DemoDbEntities.Notebook AS n Where n.Warehouse.City = 'SH' |
我们通过Notebook的Navigation属性——Warehouse,访问到对应的仓库所在的城市,进而来过滤出附合条件的笔记本。我们来看一个ObjectQuery的实现接口:
private static void RelNav1() { string query = "Select VALUE n FROM DemoDbEntities.Notebook AS n" + " Where n.Warehouse.City = 'SH'"; using (ObjectContext oc = new ObjectContext(ConnectionString)) { oc.Connection.Open(); foreach(Notebook notebook in new ObjectQuery<Notebook>(query,oc)) { Console.WriteLine("{0} {1}", notebook.Brand, notebook.Type); } } } |
其中,ConnectionString是连接字符串。这一查询模式我们在前文已经介绍过,这里不再赘述了。
实例2 查询出全部的存有品牌为"ThinkPad"或者"Lenovo"的仓库——1到N查询
SQL实现如下:
Select DISTINCT w.* FROM Notebook AS n INNER JOIN Warehouse AS w ON n.WarehouseId = w.Id Where n.Brand='ThinkPad' OR n.Brand='Lenovo' |
在SQL中实现的思路与实例1一样,都是把Notebook和Warehouse作内连接,只不过,把Where条件修改为n的品牌;而Select的是w.*。
Entity SQL在作1到N的查询时,稍有一点点不大一样,不过,仍然十分方便:
Select VALUE DISTINCT w FROM DemoDbEntities.Warehouse AS w, w.Notebook as nbs Where nbs.Brand = 'ThinkPad' OR nbs.Brand='Lenovo' |
我 们首先选出所有Warehouse,记为w,然后,把w.Notebook记为nbs,然后,设置查询条件nbs.Brand为"ThinkPad"或 者"Lenovo"。这里有一个顺序,在FROM子句中,允许访问在先前已经定义的别名。这就是为什么我们在定义了w之后,可以马上访问 w.Notebook。
ObjectQuery实现接口与实例1中的接口一致,唯一差别在于修改了查询串:
private static void RelNav2() { string query = "Select VALUE DISTINCT w FROM DemoDbEntities.Warehouse AS w, w.Notebook as nbs " + " Where nbs.Brand = 'ThinkPad' or nbs.Brand='Lenovo'"; using (ObjectContext oc = new ObjectContext(ConnectionString)) { oc.Connection.Open(); foreach (Warehouse w in new ObjectQuery<Warehouse>(query, oc)) { Console.WriteLine("{0} {1}", w.Name, w.City); } } } |
我们看到,通过Entity SQL,我们需要关心的,不再是关系模型中表与表之间的关系、主键、外键、连接,而只要处理好Navigation属性即可。只要建立了Navigation属性,哪怕不存在外键约束,一样可以在多个实体集之间进行联合查询。
另外,需要说明的是,Where子句里,要求提供的是集合,而不是单个值。举例说明,当存在一个如下的查询语句:
Select VALUE DISTINCT w FROM DemoDbEntities.Notebook AS n, n.Warehouse as w Where w.City='SH' |
由于Notebook与Warehouse是多对一的关系,因此,n.Warehouse,即w必定是1,而不是集合。然后,很不凑巧,w.City又出现在了Where子句里。因此,会得到一个与下述类似的错误信息:
The specified expression must be of CollectionType. |
对于以上的语句,第一,可以修改成实例1中所述的形式。另外,还有一个快速的Workaround,虽然不推荐使用,但这有利于大家理解Where子句中必须提供集合:
Select VALUE DISTINCT w FROM DemoDbEntities.Notebook AS n, {n.Warehouse} as w Where w.City='SH' |
我们把{n.Warehouse}记为w,w便成了一个集合,便可以出现在Where子句中了。
小结:
本文简单的介绍了Entity SQL在一对多查询中的应用;我们举例说明了1对多以及多对1的关系场景,并且了解到,在Entity SQL中,不再需要过多的考虑关系模型中的主外键约束情况,而更多的使用Navigation属性。
Demo下载
1. 数据库文件
2. 源代码(C#版)
Little knowledge is dangerous.