ASP.NET LINQ to SQL

LINQ to SQL 查询

LINQ to SQL 将您编写的查询转换成等效的 SQL 查询,然后将它们发送至服务器进行处理。更具体地说,您的应用程序使用 LINQ to SQL API 来请求查询执行。LINQ to SQL 提供程序随后会将查询转换成 SQL 文本,并委托 ADO 提供程序执行。ADO 提供程序将查询结果作为 DataReader 返回。 LINQ to SQL 提供程序将 ADO 结果转换成用户对象的 IQueryable 集合。

下图描绘了此常规流程。

查询执行关系图

LINQ to SQL 查询
说明说明

.NET Framework 内置类型中的大多数方法和运算符都能直接转换成 SQL。LINQ 无法转换的那些方法和运算符会产生运行时异常。有关更多信息,请参见 SQL-CLR 类型映射 (LINQ to SQL)

下表显示了 LINQ 与 LINQ to SQL 查询项之间的相似和不同之处。

LINQ 查询

LINQ to SQL 查询

保存查询的局部变量的返回类型(对于返回序列的查询而言)

泛型 IEnumerable

泛型 IQueryable

指定数据源

使用 From (Visual Basic) 或 from (C#) 子句

相同

筛选

使用 Where/ where 子句

相同

分组

使用 Group…By/ groupby 子句

相同

选择(投影)

使用 Select/ select 子句

相同

延迟执行与立即执行

请参见 LINQ 查询简介 (C#)

相同

实现联接

使用 Join/ join 子句

可以使用 Join/ join 子句,但使用 AssociationAttribute 属性更有效。 有关更多信息,请参见 跨关系查询 (LINQ to SQL)

远程执行与本地执行

 

有关更多信息,请参见 远程查询执行与本地查询执行 (LINQ to SQL)

流式查询与缓存查询

在本地内存情况中不适用

 

跨关系查询 (LINQ to SQL)

您的类定义中对其他对象或其他对象的集合的直接引用相当于数据库中的外键关系。您可以通过使用点表示法在查询时利用这些关系来访问关系属性以及从一个对象定位到另一个对象。这些访问操作会转换成用等效的 SQL 表示的更为复杂的联接或关联子查询。

例如,下面的查询从订单定位到客户,以此方式将结果限制为只包括位于伦敦的客户的订单。

您的类定义中对其他对象或其他对象的集合的直接引用相当于数据库中的外键关系。您可以通过使用点表示法在查询时利用这些关系来访问关系属性以及从一个对象定位到另一个对象。这些访问操作会转换成用等效的 SQL 表示的更为复杂的联接或关联子查询。

例如,下面的查询从订单定位到客户,以此方式将结果限制为只包括位于伦敦的客户的订单。


Dim db As New Northwnd("c:\northwnd.mdf")
      Dim londonOrderQuery = _
  From ord In db.Orders _
  Where ord.Customer.City = "London" _
  Select ord
  
  
    Dim db As New Northwnd("c:\northwnd.mdf")
    Dim londonOrderQuery = _
From ord In db.Orders _
Where ord.Customer.City = "London" _
Select ord

          Northwnd db = new Northwnd(@"northwnd.mdf");
  
          IQueryable<Order> londonOrderQuery =
  from ord in db.Orders
  where ord.Customer.City == "London"
  select ord;
  
  如果关系属性不存在,您必须作为联接手动编写它们,就像您在 SQL 查询中进行编写时一样,如下面的代码所示:
Dim db As New Northwnd("c:\northwnd.mdf")
      Dim londOrderQuery = _
  From cust In db.Customers _
  Join ord In db.Orders On cust.CustomerID Equals ord.CustomerID _
  Select ord
  
  
    Dim db As New Northwnd("c:\northwnd.mdf")
    Dim londOrderQuery = _
From cust In db.Customers _
Join ord In db.Orders On cust.CustomerID Equals ord.CustomerID _
Select ord

          Northwnd db = new Northwnd(@"northwnd.mdf");
          IQueryable<Order> londonOrderQuery =
  from cust in db.Customers
  join ord in db.Orders on cust.CustomerID equals ord.CustomerID
  where cust.City == "London"
  select ord;
  
  
        Northwnd db = new Northwnd(@"northwnd.mdf");
        IQueryable<Order> londonOrderQuery =
from cust in db.Customers
join ord in db.Orders on cust.CustomerID equals ord.CustomerID
where cust.City == "London"
select ord;

您可以使用关系属性来定义一次这种特殊关系。然后您就可以使用更为方便的点语法。但之所以存在关系属性,更重要的原因在于特定于域的对象模型通常定义为层次结构或关系图。您要进行编程的那些对象引用其他对象。如果对象与对象的关系相当于数据库中外键类型的关系,那么这只是一种令人庆幸的巧合而已。在这种情况下,可以很方便地通过访问属性来编写联接。

就这一点而言,关系属性在查询的结果方面要比作为查询本身的一部分重要。在查询检索到关于特定客户的数据后,类定义会指示客户下了订单。换言之,您希望特定客户的 Orders 属性是用该客户下的所有订单填充的集合。 这实际上是您通过以此方式定义类声明的协定。即便查询并未请求订单,您也希望在那里看到这些订单。您希望自己的对象模型保持这样的错觉:它是数据库在内存中的扩展,并且相关对象立即可用。

既然您已经具备了关系,您就可以通过引用您的类中定义的关系属性来编写查询。这些关系引用相当于数据库中的外键关系。使用这些关系的操作会转换成用等效的 SQL 表示的更为复杂的联接。只要您已经定义关系(使用 AssociationAttribute 属性),您就无需在 LINQ to SQL 中编写显式联接的代码。

为帮助保持这种错觉,LINQ to SQL 实现了一种称作延迟加载的技术。有关更多信息,请参见 延迟加载与立即加载 (LINQ to SQL)

可以考虑用下面的 SQL 查询来投影 CustomerID- OrderID 对的列表:

SELECT t0.CustomerID, t1.OrderID
  FROM   Customers AS t0 INNER JOIN
            Orders AS t1 ON t0.CustomerID = t1.CustomerID
  WHERE  (t0.City = @p0)
  
SELECT t0.CustomerID, t1.OrderID
FROM   Customers AS t0 INNER JOIN
          Orders AS t1 ON t0.CustomerID = t1.CustomerID
WHERE  (t0.City = @p0)

下图以图形方式显示了表关系。

跨关系查询

若要通过使用 LINQ to SQL 获得相同的结果,您可以使用 Customer 类中已经存在的 Orders 属性引用。 Orders 引用提供了执行查询和投影 CustomerID- OrderID 对所必需的信息,如下面的代码所示:

Dim db As New Northwnd("c:\northwnd.mdf")
      Dim idQuery = _
  From cust In db.Customers, ord In cust.Orders _
  Where cust.City = "London" _
  Select cust.CustomerID, ord.OrderID
  
  
    Dim db As New Northwnd("c:\northwnd.mdf")
    Dim idQuery = _
From cust In db.Customers, ord In cust.Orders _
Where cust.City = "London" _
Select cust.CustomerID, ord.OrderID

          Northwnd db = new Northwnd(@"northwnd.mdf");
          var idQuery =
  from cust in db.Customers
  from ord in cust.Orders
  where cust.City == "London"
  select new { cust.CustomerID, ord.OrderID };
  
  
        Northwnd db = new Northwnd(@"northwnd.mdf");
        var idQuery =
from cust in db.Customers
from ord in cust.Orders
where cust.City == "London"
select new { cust.CustomerID, ord.OrderID };

您也可以反向操作。也就是说,您可以查询 Orders,然后使用其 Customer 关系引用来访问关于关联的 Customer 对象的信息。 下面的代码投影与前面相同的 CustomerID- OrderID 对,但这一次采用的方法是查询 Orders 而非 Customers

Dim db As New Northwnd("c:\northwnd.mdf")
      Dim idQuery = _
  From ord In db.Orders _
  Where ord.Customer.City = "London" _
  Select ord.CustomerID, ord.OrderID
  
  
    Dim db As New Northwnd("c:\northwnd.mdf")
    Dim idQuery = _
From ord In db.Orders _
Where ord.Customer.City = "London" _
Select ord.CustomerID, ord.OrderID

          Northwnd db = new Northwnd(@"northwnd.mdf");
          var idQuery =
  from ord in db.Orders
  where ord.Customer.City == "London"
  select new { ord.Customer.CustomerID, ord.OrderID };
  
  

远程查询执行与本地查询执行 (LINQ to SQL)


您可以决定以远程方式(即数据库引擎对数据库执行查询)或在本地(LINQ to SQL 对本地缓存执行查询)执行您的查询。

远程执行

请考虑下面的查询:

Dim db As New Northwnd("c:\northwnd.mdf")
      Dim c As Customer = _
  (From cust In db.Customers _
  Where cust.CustomerID = 19283).First()
      Dim orders = From ord In c.Orders _
          Where ord.ShippedDate.Value.Year = 1998
      For Each nextOrder In orders
          ' Do something.
      Next
    Dim db As New Northwnd("c:\northwnd.mdf")
    Dim c As Customer = _
(From cust In db.Customers _
Where cust.CustomerID = 19283).First()
    Dim orders = From ord In c.Orders _
        Where ord.ShippedDate.Value.Year = 1998
    For Each nextOrder In orders
        ' Do something.
    Next

              Northwnd db = new Northwnd(@"northwnd.mdf");
              Customer c = db.Customers.Single(x => x.CustomerID == "19283");
  foreach (Order ord in 
      c.Orders.Where(o => o.ShippedDate.Value.Year == 1998))
  {
      // Do something.
  
  }
  
  如果数据库有数千行订单,则在处理其中很小一部分时您不需要将它们全都检索出来。在 LINQ to SQL 中, EntitySet TEntity 类实现了 IQueryable 接口。 这种方式确保了可以以远程方式执行此类查询。利用此技术有两大优点:
  • 不会检索到不需要的数据。

  • 由于利用了数据库索引,由数据库引擎执行的查询通常更为高效。

本地执行

在其他一些情况下,您可能需要在本地缓存中保留完整的相关实体集。为此, EntitySet TEntity 提供了 Load 方法,用于显式加载 EntitySet TEntity 的所有成员。

如果 EntitySet TEntity 已经加载,则后续查询将在本地执行。 这种方式在两个方面起到帮助作用:

  • 如果此完整集必须在本地使用或使用多次,则您可以避免远程查询和与之相关的延迟。

  • 实体可以序列化为完整的实体。

下面的代码段演示了如何实现本地执行:

Dim db As New Northwnd("c:\northwnd.mdf")
      Dim c As Customer = _
  (From cust In db.Customers _
   Where cust.CustomerID = 19283).First
      c.Orders.Load()
  
      Dim orders = From ord In c.Orders _
          Where ord.ShippedDate.Value.Year = 1998
  
      For Each nextOrder In orders
          ' Do something.
      Next
    Dim db As New Northwnd("c:\northwnd.mdf")
    Dim c As Customer = _
(From cust In db.Customers _
 Where cust.CustomerID = 19283).First
    c.Orders.Load()

    Dim orders = From ord In c.Orders _
        Where ord.ShippedDate.Value.Year = 1998

    For Each nextOrder In orders
        ' Do something.
    Next

              Northwnd db = new Northwnd(@"northwnd.mdf");
              Customer c = db.Customers.Single(x => x.CustomerID == "19283");
  c.Orders.Load();
  
  foreach (Order ord in 
      c.Orders.Where(o => o.ShippedDate.Value.Year == 1998))
  {
      // Do something.
  
  }
  
          }

延迟加载与立即加载 (LINQ to SQL)

查询某对象时,实际上您只检索请求的对象。不会同时自动获取相关对象。(有关更多信息,请参见 跨关系查询 (LINQ to SQL)。)您无法看到相关对象尚未加载这一事实,原因是尝试访问它们时将产生检索它们的请求。

例如,您可能需要查询一组特定的订单,然后偶而向特定客户发送电子邮件通知。您最初不一定需要检索与每个订单有关的所有客户数据。您可以使用延迟加载将额外信息的检索操作延迟到您确实需要检索它们时再进行。请看下面的示例:

Dim db As New Northwnd("c:\northwnd.mdf")
  Dim notificationQuery = _
      From ord In db.Orders _
      Where ord.ShipVia = 3 _
      Select ord
  
  For Each ordObj As Order In notificationQuery
      If ordObj.Freight > 200 Then
          SendCustomerNotification(ordObj.Customer)
          ProcessOrder(ordObj)
      End If
  
  Next
Dim db As New Northwnd("c:\northwnd.mdf")
Dim notificationQuery = _
    From ord In db.Orders _
    Where ord.ShipVia = 3 _
    Select ord

For Each ordObj As Order In notificationQuery
    If ordObj.Freight > 200 Then
        SendCustomerNotification(ordObj.Customer)
        ProcessOrder(ordObj)
    End If

Next

      Northwnd db = new Northwnd(@"northwnd.mdf");
  
      IQueryable<Order> notificationQuery =
      from ord in db.Orders
   where ord.ShipVia == 3
    select ord;
  
      foreach (Order ordObj in notificationQuery)
      {
          if (ordObj.Freight > 200)
              SendCustomerNotification(ordObj.Customer);
          ProcessOrder(ordObj);
      }
  
  }
  
  
    Northwnd db = new Northwnd(@"northwnd.mdf");

    IQueryable<Order> notificationQuery =
    from ord in db.Orders
 where ord.ShipVia == 3
  select ord;

    foreach (Order ordObj in notificationQuery)
    {
        if (ordObj.Freight > 200)
            SendCustomerNotification(ordObj.Customer);
        ProcessOrder(ordObj);
    }

}

反过来也可能是可行的。您的应用程序可能必须同时查看客户数据和订单数据。您了解同时需要这两组数据。您了解一旦获得结果,您的应用程序就需要每个客户的订单信息。您不希望一个一个地提交对每个客户的订单的查询。您真正想要的是将订单数据与客户信息一起检索出来。

Dim db As New Northwnd("c:\northwnd.mdf")
  
  db.DeferredLoadingEnabled = False
  
  Dim custQuery = _
      From cust In db.Customers _
      Where cust.City = "London" _
      Select cust
  
  For Each custObj As Customer In custQuery
      For Each ordObj As Order In custObj.Orders
          ProcessCustomerOrder(ordObj)
      Next
  Next
Dim db As New Northwnd("c:\northwnd.mdf")

db.DeferredLoadingEnabled = False

Dim custQuery = _
    From cust In db.Customers _
    Where cust.City = "London" _
    Select cust

For Each custObj As Customer In custQuery
    For Each ordObj As Order In custObj.Orders
        ProcessCustomerOrder(ordObj)
    Next
Next

  Northwnd db = new Northwnd(@"c:\northwnd.mdf");
  
  db.DeferredLoadingEnabled = false;
  
  IQueryable<Customer> custQuery =
      from cust in db.Customers
      where cust.City == "London"
      select cust;
  
  foreach (Customer custObj in custQuery)
  {
      foreach (Order ordObj in custObj.Orders)
      {
          ProcessCustomerOrder(ordObj);
      }
  }
  
发布了23 篇原创文章 · 获赞 26 · 访问量 5万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览