LINQ : 谈谈LINQ TO SQL中的直接加载和延迟加载

有时会在存储过程中处理一些XML格式的数据,所以会用到sp_xml_preparedocument,他可以将XML数据进行读取,然后使用
MSXML 分析器 (Msxmlsql.dll)
对其进行分析。我们就可以很容易的在存储过程中得到XML中我们想要的数据。下面的代码就是使用sp_xml_preparedocument读取XML:复制代码 代码如下: DECLARE @hdoc int DECLARE
@doc varchar(1000) SET @doc =’ ROOT Customer CustomerID=”VINET”
ContactName=”Paul Henriot” Order CustomerID=”VINET” EmployeeID=”5″
OrderDate=”1996-07-04T00:00:00″ OrderDetail OrderID=”10248″
ProductID=”11″ Quantity=”12″/ OrderDetail OrderID=”10248″ ProductID=”42″
Quantity=”10″/ /Order /Customer Customer CustomerID=”LILAS”
ContactName=”Carlos Gonzlez” Order CustomerID=”LILAS” EmployeeID=”3″
OrderDate=”1996-08-16T00:00:00″ OrderDetail OrderID=”10283″
ProductID=”72″ Quantity=”3″/ /Order /Customer /ROOT’ EXEC
sp_xml_preparedocument @hdoc OUTPUT, @doc
上面只是读取了XML,要想获取XML数据还需要使用OPENXML,代码如下: 复制代码 代码如下: SELECT * FROM
openxml(@hdoc,’/ROOT/Customer’,1) WITH (CustomerID
VARCHAR(40),ContactName VARCHAR(40)) OPENXML有三个参数:
第一个是sp_xml_preparedocument读取是的OUTPUT参数,在本示例中就是@hdoc;
第二个是一个XPath表达式,用来获取指定位置的数据;
第三个是一个可选项,用来表示获取的方式,有0,1,2,8四种取值,详细解释请看
FROM后面的WITH也是可选的,用来指定获取哪些数据字段,上面代码中只取了CustomerID和ContactName。上面的查询结果如下:
CustomerID ContactName —————————————- —————————————- VINET Paul Henriot
LILAS Carlos Gonzlez
如果不指定WITH子句,查询出来的是一个默认的表结构,如下:

现在的信息系统越来越复杂,它们的数据库中的数据结构也越来越复杂,表和字段变多了,表间关系也越来越多。面对如此复杂的数据结构,各大报表工具各显神通,想尽一切办
法誓要从中获得数据。

在LINQ TO SQL中,有两种主要的加载模式:直接加载和延迟加载.
这是有一个选项来控制的;DeferredLoadingEnabled。这是DataContext的一个属性,默认为true

表格列的解释说明:

    首先考察数据结构的复杂性的特点。在这里以微软的演示数据库
nwind.mdb为例子进行分析,现在要出订单明细报表,则涉及到的数据结构如图所示
可以发现这5张表的数据组成了一个3层的树状结构。第一层是Customers的数据,第二层是Orders,Employees组成的数据,第三层是OrderDetails,Products组成的。其意思就是说
数据库中存在好几个客户,一个客户有多个订单,一个订单有多个货物。

那么,什么叫延迟加载?简单的说就是按需加载,也就是只有在需要的时候才加载。这样当然是有些好处的。

列名

   
面对这种比较复杂的数据,传统的报表工具由于采用两层的数据源模型,因此需要一次性获取数据,采用眉毛胡子一起抓的思想,这就导致可能需要编写复杂的SQL语句,例如对于
订单明细报表,SQL语句可以为”SELECT Customers.CompanyName,
Customers.ContactName, Customers.Phone,
OrderDetails.*, Orders.OrderDate,
Products.ProductName, Employees.FirstName, Employees.LastName
FROM ((Orders 
INNER JOIN (OrderDetails 
INNER JOIN Products ON OrderDetails.ProductID = Products.ProductID) 
ON Orders.OrderID = OrderDetails.OrderID) 
INNER JOIN Customers ON Orders.CustomerID = Customers.CustomerID) 
INNER JOIN Employees ON Orders.EmployeeID =
Employees.EmployeeID”,如此复杂的SQL语句我是写不出,也不想写,个人认为复杂SQL语句坏处多多,尤其是JOIN
子语句,应尽量避免。SQL语句写出来后,在报表模板中还需要进行多次分组才能组织出报表样式。

但是,关键是说,如果某部分数据我们确定就是要加载的,那么如果再去延迟加载可能就不合适了,此时就应该选用直接加载。

数据类型

  
若采用多层报表数据源模型,则采用了眉毛胡子分别抓的指导思想,处理起来从容不迫,此时获取数据的原理如图所示

来看这么一个例子

说明

图片 1

static void Main(string[] args)
{
    NorthwindDataContext db = new NorthwindDataContext();

idbigint文档节点的唯一 ID。

其详细步骤为

    var custQuery =
        from cust in db.Customers
        where cust.City == “London”
        select cust;

根元素的 ID 值为 0。保留负 ID 值。

  1. 对于Customers节点,执行SQL语句”Select CompanyName , ContactName ,
    CustomerID , Phone From Customers”获得客户列表
    ,将查询所得的栏目分别分配到CompanyName,ContactName , 订单列表 ,
    Phone 数据源子节点。
  2. Customers节点遍历所有的查询所得的记录行时,每处理一行,都递归调用子节点处理数据的过程。对于订单列表
    节点,它有子节点,因此其处理数据的过程是,首先执行SQL语句”Select
    Orders.OrderID , Orders.OrderDate , Orders.EmployeeID ,
    Employees.EmployeeID , Employees.FirstName , Employees.LastName From
    Orders , Employees where orders.employeeid = Employees.EmployeeID
    and orders.CustomerID=
    当前处理的CustomerID栏目的值”,例如Customers节点处理CustomerID为“1234”的记录时,订单列
    表执行的SQL语句为”Select … From From Orders , Employees where
    orders.employeeid = Employees.EmployeeID and orders.CustomerID=1234″
    ,
    也就时说订单列表节点出执行的SQL语句是根据当前节点的值而改变的。订单列表节点查询成功后,然后将查询所得的栏目分配到
    OrderID , OrderData 等子节点,其中 栏目 Orders.OrderID分配给了
    订单详细内容 子节点 。
  3. 类似的,对于订单详细内容,执行的SQL语句为”Select
    OrderDetails.ProductID , OrderDetails.UnitPrice ,
    OrderDetails.Discount , Products.ProductName , OrderDetails.Quantity
    ,( OrderDetails.UnitPrice * OrderDetails.Quantity * ( 1 –
    OrderDetails.Discount )) as TotalCount From  OrderDetails , Products
    where OrderDetails.ProductID = Products.ProductID And
    OrderDetails.OrderID =
    当前处理的订单号”,查询所得的栏目分别分配到了它的子节点,其中
    TotalCount 栏目分配到了总金额子节点。

    foreach (Customer custObj in custQuery)
    {
        Console.WriteLine(“Customer ID: {0}”, custObj.CustomerID);
        foreach (Order ord in custObj.Orders)
        {
            Console.WriteLine(“\tOrder ID: {0}”, ord.OrderID);
            foreach (OrderDetail detail in ord.OrderDetails)
            {
                Console.WriteLine(“\t\tProduct ID: {0}”,
detail.ProductID);
            }
        }
    }

parentidbigint标识节点的父节点。此 ID
标识的父节点不一定是父元素。具体情况取决于此 ID
所标识节点的子节点的节点类型。例如,如果节点为文本节点,则其父节点可能是一个属性节点。

执行的SQL语句依次可能为,此处字段列表用 …表示

    Console.Read();
}

如果节点位于 XML 文档的顶层,则其 ParentID 为 NULL。

  1. Select … From Customers
  2. Select … From Orders , Employees where orders.employeeid =
    Employees.EmployeeID and orders.CustomerID=’1234′
  3. Select … From  OrderDetails , Products where
    OrderDetails.ProductID = Products.ProductID And OrderDetails.OrderID
    = 100
  4. Select … From  OrderDetails , Products where
    OrderDetails.ProductID = Products.ProductID And OrderDetails.OrderID
    = 101
  5. Select … From Orders , Employees where orders.employeeid =
    Employees.EmployeeID and orders.CustomerID=’5678′
  6. Select … From  OrderDetails , Products where
    OrderDetails.ProductID = Products.ProductID And OrderDetails.OrderID
    = 201
  7. Select … From  OrderDetails , Products where
    OrderDetails.ProductID = Products.ProductID And OrderDetails.OrderID
    = 202

以上是查询了一个客户列表,但是在读取的时候,我们不光要读客户列表,还要读取它相关的订单及明细。

节点类型int标识节点类型,是对应于 XML 对象模型 (DOM)
节点类型编号的一个整数。

    如此看出,这种多层数据源的使用有利有弊。好处有

需要注意的是,我们没有对db的加载模式做设置,此时将使用延迟加载的模式。

下列值是可以显示在此列中以指明节点类型的值:

  1. 处理过程符合一般的编程逻辑,便于对数据源结构的理解和设计。
  2. 提供了充分的自由度,可以不依赖外部编程来处理大部分复杂的数据库结构。
  3. 此过程中使用的SQL语句简单可靠,很容易理解,而且执行效率高。
  4. 这种多层的数据源结构很大程度上就反映了数据库中的数据结构。数据源树状结构直接映射了数据库中各条记录组成的树状结构,只要了解数据结构就可以很自然的套这这
    种结构来编制报表数据源。某种程度上可以进行数据源结构和数据结构的相互检查。

这个代码没有问题,执行之后的结果如下