LINQ to SQL






4.66/5 (40投票s)
如何使用LINQ to SQL

目录
引言
本文演示了 LINQ to SQL 是什么以及如何使用其基本功能。我认为这项新功能非常棒,因为它真正简化了开发人员的调试工作,并提供了许多新的应用程序编码方式。
背景
在本文中,我使用了 Microsoft 提供的 Northwind 数据库示例。数据库包含在 ZIP 文件中,也可以在 Microsoft 网站上找到。
什么是 LINQ to SQL
LINQ 项目是 .NET Framework 扩展集的一个代号,它包含语言集成查询、集合和转换操作。它通过原生语言查询语法扩展了 C# 和 Visual Basic。它还提供了利用这些功能的类库。有关更详细的通用信息,请参阅 Microsoft LINQ 项目页面。
LINQ to SQL 是一个新系统,它允许您轻松地映射 SQL 服务器中的表、视图和存储过程。此外,LINQ to SQL 通过其简单且 类似 SQL 的 语言,帮助开发人员映射和请求数据库。它不是 ADO.NET 的替代品,而是提供新功能的扩展。

如何使用 LINQ to SQL
创建新的 C# 项目
在本节中,我将向您展示如何从头开始使用 LINQ to SQL,在项目创建时。
- 使用 Visual C# 2008 创建一个新的 Windows 窗体应用程序。
- 使用 SQL Server 2005 northwind.mdf 文件进行新的“数据连接”。
- 向您的项目添加新项,并选择“LINQ to SQL 类”。将其命名为 _NorthwindDataClasses.dbml_。这个新的 DBML 文件将包含 SQL Server 表到 C# 类的映射。
对象关系设计器是一个设计界面,它将数据库表映射到 C# 类。要做到这一点,只需将表从数据库资源管理器拖放到设计器中。设计器以 UML 方式自动显示表,并表示它们之间的关系。例如,我拖放了 Customers、Order、Order_Detail 和 Product 四个表,如下所示

在 _NorthwindDataClasses.designer.cs_(在项目资源管理器中的 _NorthwindDataClasses.dbml_ 下)中,您会找到对应于表的类的定义,如下所示
SQL | LINQ to SQL O/R 设计器 |
表名 | 类名 |
Columns | 属性 |
关系 | EntitySet 和 EntityRef |
存储过程 | 方法 |
[Table(Name="dbo.Customers")]
public partial class Customer : INotifyPropertyChanging, INotifyPropertyChanged
{
private static PropertyChangingEventArgs emptyChangingEventArgs =
new PropertyChangingEventArgs(String.Empty);
private string _CustomerID;
private string _CompanyName;
private string _ContactName;
private string _ContactTitle;
private string _Address;
private string _City;
private string _Region;
private string _PostalCode;
private string _Country;
private string _Phone;
private string _Fax;
private EntitySet<Order> _Orders;
//..............
}
理解 DataContext
dataContext
是一个类,它提供对 C# 类、数据库连接等的直接访问。这个类在保存设计器时生成。对于名为 _NorthwindDataClasses.dbml_ 的文件,会自动生成 _NorthwindDataClassesDataContext_ 类。它包含表和存储过程的定义。
理解 var 关键字
当您不知道变量的类型时,就会使用此关键字。Visual Studio 2008 会自动选择合适的数据类型,IntelliSense 也会如此!
示例
var anInteger = 5;
var aString = "a string";
var unknown_type = new MyClass();
查询数据库
通过设计器对数据库进行建模后,就可以轻松地使用它来进行查询。
从数据库查询客户
//creating the datacontext instance
NorthwindDataClassesDataContext dc = new NorthwindDataClassesDataContext();
//Sample 1 : query all customers
var customers =
from c in dc.Customers
select c;
//display query result in a dataGridView
dataGridResult.DataSource = customers.ToList();
使用简单语句查询客户
//Sample 2 : query customers which country is UK
//constructing the query
var customers_from_uk =
from c in dc.Customers
where c.Country == "UK"
select c;
仅查询指定列,返回指定类别的集合
使用非常简单的类定义。
public class CMyClassTwoStrings
{
public CMyClassTwoStrings(string name, string country)
{
m_name = name;
m_country = country;
}
public string m_name;
public string m_country;
}
例如
//Sample 4a : query customers(name and country) which contact name starts with a 'A'
//using specific class
var customers_name_starts_a_2col_in_specific_class =
from c in dc.Customers
where c.ContactName.StartsWith("A")
select new CMyClassTwoStrings (c.ContactName, c.Country );
//using the returned collection (using foreach in
//console output to really see the differences with the dataGridView way.)
foreach (CMyClassTwoStrings a in customers_name_starts_a_2col_in_specific_class)
Console.WriteLine(a.m_name + " " + a.m_country);
仅查询指定列,返回未定义类别的集合
//Sample 4b : query customers(name and country) which contact name starts with a 'A'
//using anonymous class
var customers_name_starts_a_2col_in_anonymous_class =
from c in dc.Customers
where c.ContactName.StartsWith("A")
select new {
Name = c.ContactName, //naming the column Name
Country = c.Country //naming the column Country
};
foreach (var a in customers_name_starts_a_2col_in_anonymous_class)
Console.WriteLine(a.Name + " " + a.Country);
此示例演示了如何使用匿名类(在此示例中)由两个字符串组成。此功能旨在创建一个新的类用于临时存储,而开发人员不想(或不需要)声明它。在某些类声明仅用于存储的情况下,它可能很有用。
例如,在示例 4a 中,_CMyClassTwoStrings_ 类仅用于创建查询引擎和控制台输出之间的接口。它在其他地方不被使用,是浪费时间。这种新的编写方式使开发人员能够创建具有任意类型无限数量属性的临时类。每个属性都命名,通过指定名称 Name = c.ContactName
,或不指定名称,即 Name =
。IntelliSense 也适用于匿名类!

查询多个表
//Sample 5 : query customers and products
//(it makes a cross product it do not represent anything else than a query
var customers_and_product =
from c in dc.Customers
from p in dc.Products
where c.ContactName.StartsWith("A") && p.ProductName.StartsWith("P")
select new { Name = c.ContactName, Product = p.ProductName };
结果集合是所有以“A”开头的联系人姓名与所有以“P”开头的产品的笛卡尔积。
查询已联接的表
//Sample 6 : query customers and orders
var customers_and_orders =
from c in dc.Customers
from p in dc.Orders
where c.CustomerID == p.CustomerID
select new { c.ContactName, p.OrderID};
此示例演示了如何指定表之间通过属性联接的关系。
通过 entityref 查询已联接的表
//Sample 7 : query customers and orders with entityref
var customers_and_orders_entityref =
from or in dc.Orders
select new {
Name = or.Customer.ContactName,
OrderId = or.OrderID,
OrderDate = or.OrderDate
};
在此示例中,使用了 entityref
属性。Orders 类有一个名为 Customer
的属性,该属性引用了下订单的客户。它只是一个指向 Customer
类的一个实例的指针。此属性使我们能够直接访问客户属性。此功能的好处在于,开发人员无需确切知道表是如何联接的,并且可以即时访问附加数据。
旧式查询:使用 SQL 字符串
由于您可能希望执行 LINQ to SQL 尚不支持的 SQL,因此提供了一种以旧方式执行 SQL 查询的方法。
//Sample 8 : execute SQL queries
dc.ExecuteCommand("UPDATE Customers SET PostalCode='05024' where CustomerId='ALFKI' ");
插入、更新和删除数据库中的行
LINQ to SQL 提供了一种管理数据库中数据的新方法。INSERT
、DELETE
和 UPDATE
三个 SQL 语句已实现,但使用它们不可见。
更新语句
//Sample 9 : updating data
var customers_in_paris =
from c in dc.Customers
where c.City.StartsWith("Paris")
select c;
foreach (var cust in customers_in_paris)
cust.City = "PARIS";
//modification to database are applied when SubmitChanges is called.
dc.SubmitChanges();
要对数据库进行修改,只需修改任何相关的对象属性并调用 SubmitChanges()
方法。
插入语句
要将新条目插入数据库,只需创建一个 C# 类的实例并将其 Attach
到关联的表。
//Sample 10 : inserting data
Product newProduct = new Product();
newProduct.ProductName = "RC helicopter";
dc.Products.InsertOnSubmit(newProduct);
dc.SubmitChanges();
删除语句
删除数据非常简单。在请求数据库时,提供一个数据集合。然后只需调用 DeleteOnSubmit
(或 DeleteAllOnSubmit
)来删除指定项。
//Sample 11 : deleting data
var products_to_delete =
from p in dc.Products
where p.ProductName.Contains("helicopter")
select p;
dc.Products.DeleteAllOnSubmit(products_to_delete);
dc.SubmitChanges();
IntelliSense
IntelliSense 在查询定义中工作,可以提高开发人员的生产力。这非常有趣,因为它会在 DataContext
、表和属性上弹出。在第一个示例中,IntelliSense 显示了从数据库映射的表列表、连接实例以及许多其他属性。

对于表,列表包含其所有列

对于属性,它将显示方法和属性,具体取决于类型(字符串、整数等)。

操作顺序
要使用 LINQ to SQL,开发人员必须确切知道何时执行查询。事实上,LINQ to SQL 非常强大,因为查询在需要时执行,而不是在定义时执行!在第一个示例中,我们有这段代码
///constructing the query
var customers =
from c in dc.Customers
select c;
查询尚未执行;它只是被编译和分析。实际上,当代码访问 customer 变量时,查询就会运行,如下所示
//display query result in a dataGridView
dataGridResult.DataSource = customers.ToList();
增强功能
其他 LINQ to SQL 选项
LINQ to SQL 支持 延迟加载选项。此功能允许用户在检索数据时修改查询引擎的行为。其中之一是延迟加载,它将加载查询的所有数据。例如,对 Order 表的查询可以通过 entityref
访问 Customer 属性。如果 Datacontext.DeferredLoadingEnabled
设置为 true
(默认值),则在访问 Order 条目时会加载 Customer
属性。否则(设置为 false
),则不加载。此选项有助于开发人员优化请求、数据大小和查询时间。这里有一个很好的例子 这里。
管理冲突
当 SubmitChanges()
函数执行时,它首先会验证是否有由外部修改引起的冲突。对于服务器/客户端应用程序,应用程序必须考虑到冲突,以防多个客户端同时访问数据库。要实现冲突解决,SubmitChanges()
会生成 System.Data.LINQ.ChangeConflictException
异常。DataContext
实例提供有关冲突的详细信息,以便确切知道它们为何被抛出。我编写了一个基本的冲突解决程序,但我不会提供所有其他可能性的全部详细信息,因为我认为这应该是一个完整的文章。
try{
//query the database
var customers_in_paris_conflict =
from c in dc.Customers
where c.City.StartsWith("Paris")
select c;
foreach (var cust in customers_in_paris_conflict)
cust.City = "PARIS";
//Make a breakpoint here and modify one customer entry
//(where City is Paris) manually (with VS for example)
//When external update is done, go on and SubmitChanges should throw.
dc.SubmitChanges();
}
catch (System.Data.LINQ.ChangeConflictException)
{
//dc.ChangeConflicts contains the list of all conflicts
foreach (ObjectChangeConflict prob in dc.ChangeConflicts)
{
//there are many ways in resolving conflicts,
//see the RefreshMode enumeration for details
prob.Resolve(RefreshMode.KeepChanges);
}
}
如果您想了解更多关于冲突解决的信息,我建议您参考 此页面(VB 语言)。
关注点
作为本文的结论,我总结了 LINQ to SQL 的要点
- LINQ to SQL 是一种查询语言
- 查询语法在构建时进行验证(不像旧 SQL 查询那样在运行时验证)
- IntelliSense 支持所有 LINQ to SQL 对象
- 查询操作非常简单(选择、插入、更新和删除)
- 管理表的主键/外键关系
- 数据库会自动映射到 C# 类,查询会返回 C# 类实例的集合
- 冲突检测和解决