深入SQL Entity与EntityRef<T>内部

引言

最近在学习LINQ,已经逐步深入到LINQ to SQL和LINQ to Entity的部分。但是对SQL Entity专题中的EntityRef<T>很是迷惑,始终未能理解它是如何从数据库的1:1关系转变为C#中两个对象的相互引用的,于是有了这段摸索。

阅读本文,需要对SQL Entity的内部结构有一定程度的了解。

一切尽在不言中

线索

问题源自于下面我自己写的这段旨在测试引用关系的代码,Customers与Orders是通过“LINQ to SQL类”(.dbml)的方式,从数据库生成的SQL Entity类。其中,测试用例的客户Thomas Hardy的CustomerID为AROUT。

using (NorthwindDataContext db = new NorthwindDataContext())
{
    db.Log = Console.Out;
    Customers customer = db.Customers.Where(c => c.ContactName == "Thomas Hardy").SingleOrDefault();
    Console.WriteLine("the customer {0} lives in {1}", customer.City);
    Console.WriteLine("he gets {0} orders.", customer.Orders.Count);

    Orders order = db.Orders.Where(o => o.CustomerID == "AROUT").FirstOrDefault();
    Console.WriteLine("get the first order id is: {0}", order.OrderID);

    Console.WriteLine(object.ReferenceEquals("is the reference equal? {0}", customer, order.Customers));
}

 

运行的结果

SELECT [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactT
itle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Coun
try], [t0].[Phone], [t0].[Fax]
FROM [dbo].[Customers] AS [t0]
WHERE [t0].[ContactName] = @p0
-- @p0: Input NVarChar (Size = 4000; Prec = 0; Scale = 0) [Thomas Hardy]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.0.30319.1

the customer Thomas Hardy lives in London
SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [
t0].[RequiredDate], [t0].[ShippedDate], [t0].[ShipVia], [t0].[Freight], [t0].[Sh
ipName], [t0].[ShipAddress], [t0].[ShipCity], [t0].[ShipRegion], [t0].[ShipPosta
lCode], [t0].[ShipCountry]
FROM [dbo].[Orders] AS [t0]
WHERE [t0].[CustomerID] = @p0
-- @p0: Input NVarChar (Size = 4000; Prec = 0; Scale = 0) [AROUT]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.0.30319.1

he gets 13 orders.
SELECT TOP (1) [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[Order
Date], [t0].[RequiredDate], [t0].[ShippedDate], [t0].[ShipVia], [t0].[Freight],
[t0].[ShipName], [t0].[ShipAddress], [t0].[ShipCity], [t0].[ShipRegion], [t0].[S
hipPostalCode], [t0].[ShipCountry]
FROM [dbo].[Orders] AS [t0]
WHERE [t0].[CustomerID] = @p0
-- @p0: Input NVarChar (Size = 4000; Prec = 0; Scale = 0) [AROUT]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.0.30319.1

get the first order id is: 10355
is the reference equal? True

 

从结果看,对象customer与order在我们没有显式地建立彼此的引用的情况下,通过LINQ to SQL仍实现了完美的相互引用,从而使最后的引用比较得到了True的结果。

摸索过程

打断点+单步调试,这是深入Entity内部的简易方法。最后得出如下的一张流程图。

SQL Entity

从图中可以看出,

1. 子表对应的Entity对象并不会在父表Entity对象被填充时一并被构造并填充(除非显式地LoadWith)。EntitySet<T>与EntityRef<T>都是独立于作为其内部元素的Entity对象的数据结构。

2. 总是先生成SQL查询语句,随后才由LINQ构造并填充Entity对象。

3. attach/detach方法,是建立彼此引用的核心。

 

EntityRef<T>的结构

除了MSDN的帮助,我利用反射,获得了EntityRef<T>的结构图:

EntityRef

 

利用调试工具,还能看见其内部的私有成员:

EntityRef Member

 

到此,终于对LINQ to SQL Entity Class的数据加载,以及EntityRef<T>与EntitySet<T>的关系有了更深入的了解,心中的疑惑也终于有了一个答案。


LINQ to Entity还在等待着我……

posted @ 2011-07-21 01:17  没头脑的老毕  阅读(1591)  评论(1编辑  收藏  举报