LINQ to ADO.NET

newsopen

贡献于2011-01-05

字数:0 关键词: .NET开发 LINQ

全部折叠 代码:C# .NET Framework 开发人员指南 LINQ to ADO.NET 请参见 发送反馈意见 Language-Integrated Query (LINQ) 定义了一组可以在 .NET Framework 3.0 编程语言中使用的通用标准查询运算 符。 使用这些标准查询运算符可以投影、筛选和遍历内存中的集合或数据库中的表。 请注意,LINQ 查询使用编程 语言本身进行表示,而不表示为应用程序代码中嵌入的字符串。 这是在 .NET Framework 的早期版本中编写多数应 用程序方式的重大更改。 使用编程语言中编写查询具有多项重要优势。 它可以简化查询,不必使用单独的查询语 言。 并且,如果您使用 Visual Studio 2008 IDE,LINQ 还允许您利用编译时检查、静态类型和 IntelliSense。 在 .NET Framework 中,数据访问的各个方面都集成了 LINQ,包括 DataSet 断开连接式编程模型和现有的 SQL Server 数据库架构。 本节说明 LINQ 的实现 ADO.NETLINQ to ADO.NET。 下面的关系图概述了 LINQ to ADO.NET 如何关联到高级编程语言、其他 LINQ 技术和启用 LINQ 的数据源。 本节内容 参考 请参见 LINQ to ADO.NET 概述 概述 LINQ to ADO.NET。 LINQ to DataSet 提供有关 LINQ to DataSet 的信息,包括编程示例。 LINQ to SQL 提供有关 LINQ to SQL 的信息,包括编程示例。 DataRowComparer DataRowExtensions DataTableExtensions 概念 Language-Integrated Query (LINQ) ADO.NET 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/be3297b9-1b5... 全部折叠 代码:C# .NET Framework 开发人员指南 LINQ to ADO.NET 概述 请参见 发送反馈意见 如今,许多业务开发人员都必须使用两种(或多种)编程语言: 用于业务逻辑和表示层的高级语言(如 Visual C# 或 Visual Basic)和可与数据库交互的查询语言(如 Transact-SQL)。 这要求开发人员精通多种语言才能奏效,同 时也导致在开发环境中语言不匹配。 例如,使用数据访问 API 对数据库执行查询的应用程序会将查询指定为用引号 括起的字符串。 编译器不能读取此查询字符串,因此不会检查是否有错误,如语法无效或引用的列或行是否实际存 在。 不会检查查询参数的类型,也不支持 IntelliSense。 Language-Integrated Query (LINQ) 使开发人员能够在应用程序代码中形成基于集合的查询,而不必使用单独的查询 语言。 您可以编写针对各种可枚举数据源(即实现 IEnumerable 接口的数据源)的 LINQ 查询,可枚举数据源包括 驻留在内存中的数据结构、XML 文档、SQL 数据库和 DataSet 对象等。 虽然这些可枚举数据源以多种方式实现, 但它们都公开相同的语法和语言构造。 由于可以使用编程语言本身形成查询,因此您不必使用编译器无法理解或验 证的以字符串形式嵌入的其他查询语言。 通过提供编译时类型和语法检查以及 IntelliSense,将查询集成到编程语 言也使 Visual Studio 程序员的工作更有效。 这些功能降低了对查询调试和错误修复的需求。 LINQ to ADO.NET 包括两种独立的技术: LINQ to DataSet 和 LINQ to SQL。 使用 LINQ to DataSet 可以对 DataSet 执行丰富而优化的查询,而使用 LINQ to SQL 可以直接查询 SQL Server 数据库架构。 将数据从 SQL 表传输到内存中的对象通常单调乏味并容易出错。 由 LINQ to DataSet 和 LINQ to SQL 实现的 LINQ 提供程序可以将源数据转换为基于 IEnumerable 的对象集合。 在您查询数据和更新数据时,程序员始终会以 IEnumerable 集合的形式查看这些数据。 为编写针对这些集合的查询提供完全的 IntelliSense 支持。 下面各节提供有关 LINQ to DataSet 和 LINQ to SQL 的更多信息。 LINQ to DataSet LINQ to SQL 请参见 DataSet 是赖以生成 ADO.NET 的断开连接式编程模型的关键元素,使用非常广泛。LINQ to DataSet 使开发人 员能够通过使用许多其他数据源可用的同样的查询表述机制在 DataSet 中内置更丰富的查询功能。 有关更多 信息,请参见 LINQ to DataSet。 LINQ to SQL 是适合不需要映射到概念模型的开发人员使用的有用工具。 通过使用 LINQ to SQL,您可以直接 在现有数据库架构上直接使用 LINQ 编程模型。LINQ to SQL 使开发人员能够生成表示数据的 .NET Framework 类。 这些生成的类直接映射到数据库表、视图、存储过程和用户定义的函数,而不映射到概念数 据模型。 使用 LINQ to SQL 时,除了其他数据源(如 XML)外,开发人员还可以使用与内存集合和 DataSet 相同的 LINQ 编程模式直接编写针对存储架构的代码。 有关更多信息,请参见 LINQ to SQL。 概念 LINQ to DataSet LINQ to SQL LINQ 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/bf0c8f93-3ff7-... 全部折叠 代码:C# .NET Framework 开发人员指南 LINQ to DataSet 请参见 发送反馈意见 使用 LINQ to DataSet 可以更快更容易地查询在 DataSet 对象中缓存的数据。 具体而言,通过使开发人员能够使用 编程语言本身而不是通过使用单独的查询语言来编写查询,LINQ to DataSet 可以简化查询。 对于现在可以在其查 询中利用 Visual Studio 所提供的编译时语法检查、静态类型和 IntelliSense 支持的 Visual Studio 开发人员,这特别 有用。 LINQ to DataSet 也可用于查询从一个或多个数据源合并的数据。 这可以使许多需要灵活表示和处理数据的方案 (例如查询本地聚合的数据和 Web 应用程序中的中间层缓存)能够实现。 具体地说,一般报告、分析和业务智能 应用程序将需要这种操作方法。 LINQ to DataSet 功能主要通过 DataRowExtensions 和 DataTableExtensions 类中的扩展方法公开。LINQ to DataSet 基于并使用现有的 ADO.NET 2.0 体系结构生成,在应用程序代码中不能替换 ADO.NET 2.0。 现有的 ADO.NET 2.0 代码将继续在 LINQ to DataSet 应用程序中有效。 下图阐释了 LINQ to DataSet 与 ADO.NET 2.0 和数据存储区的关 系。 本节内容 参考 请参见 入门 (LINQ to DataSet) 编程指南 (LINQ to DataSet) DataTableExtensions DataRowExtensions DataRowComparer 概念 Language-Integrated Query (LINQ) LINQ to ADO.NET 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/743e3755-3ecb... 全部折叠 代码:C# .NET Framework 开发人员指南 入门 (LINQ to DataSet) 请参见 发送反馈意见 本节提供有关使用 LINQ to DataSet 进行编程的介绍性信息。 本节内容 参考 请参见 LINQ to DataSet 概述 提供 LINQ to DataSet 的概念性概述。 向数据集中加载数据 提供填充 DataSet 的示例。 此示例使用 DataAdapter 从数据库中检索数据。 下载示例数据库 (LINQ to DataSet) 提供有关下载 AdventureWorks 示例数据库的信息,LINQ to DataSet 一节中的示例均使用此数据库。 如何: 在 Visual Studio 中创建 LINQ to DataSet 项目 提供有关在 Visual Studio 中创建 LINQ to DataSet 项目的信息。 DataRowComparer DataRowExtensions DataTableExtensions 概念 LINQ to ADO.NET Language-Integrated Query (LINQ) Getting Started with LINQ 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/97522119-f6a3... 全部折叠 代码:C# .NET Framework 开发人员指南 LINQ to DataSet 概述 请参见 发送反馈意见 DataSet 是更为广泛使用的 ADO.NET 组件之一。 它是 ADO.NET 所基于的断开连接式编程模型的关键元素,使用它 可以显式缓存不同数据源中的数据。 在表示层上,DataSet 与 GUI 控件紧密集成,以进行数据绑定。 在中间层 上,它提供保留数据关系形状的缓存并包括快速简单查询和层次结构导航服务。 用于减少对数据库的请求数的常用 技术是使用 DataSet 以便在中间层进行缓存。 例如,考虑数据驱动的 ASP.NET Web 应用程序。 通常,应用程序 的绝大部分数据不会经常更改,属于会话之间或用户之间的公共数据。 此数据可以保存在 Web 服务器的内存中, 这会减少对数据库的请求数并加速用户的交互。 DataSet 的另一个有用特征是允许应用程序将数据子集从一个或多 个数据源导入应用程序空间。 然后,应用程序可以在内存中操作这些数据,同时保留其关系形状。 DataSet 虽然具有突出的优点,但其查询功能也存在限制。 Select 方法可用于筛选和排序,GetChildRows 和 GetParentRow 方法可用于层次结构导航。 但对于更复杂的情况,开发人员必须编写自定义查询。 这会使应用程序 性能低下并且难以维护。 使用 LINQ to DataSet 可以更快更容易地查询在 DataSet 对象中缓存的数据。 这些查询用编程语言本身表示,而不 表示为嵌入在应用程序代码中的字符串。 这意味着开发人员不必学习单独的查询语言。 此外,LINQ to DataSet 可 使 Visual Studio 开发人员的工作效率更高,因为 Visual Studio IDE 提供编译时语法检查、静态类型化和对 LINQ 的 IntelliSense 支持。 LINQ to DataSet 也可用于查询从一个或多个数据源合并的数据。 这可以使许多需要灵活表示和 处理数据的方案能够实现。 具体地说,一般报告、分析和业务智能应用程序将需要这种操作方法。 使用 LINQ to DataSet 查询数据集 N 层应用程序和 LINQ to DataSet 请参见 只有在填充 DataSet 后,您才能开始使用 LINQ to DataSet 来查询 DataSet 对象。 向 DataSet 中加载数据 有多种方法,如使用 DataAdapter 类或 LINQ to SQL。 将数据加载到 DataSet 对象后,可以开始查询数据。 使用 LINQ to DataSet 来表述查询类似于对其他启用 LINQ 的数据源使用Language-Integrated Query (LINQ)。 LINQ 查询可以对 DataSet 中的单个表执行,也可以通过使用 Join 和 GroupJoin 标准查询运算符对多个表执 行。 支持对类型化和非类型化 DataSet 对象执行 LINQ 查询。 如果在应用程序设计时已知 DataSet 的架构,则建 议使用类型化 DataSet。 在类型化 DataSet 中,表和行对每个列都具有类型化成员,从而使查询更简单并且 更具可读性。 除了 System.Core.dll 中实现的标准查询运算符外,LINQ to DataSet 还添加了多种 DataSet 特定扩展,从而 可以更容易地查询一组 DataRow 对象。 这些 DataSet 特定扩展包括用于比较行序列的运算符以及用于访问 DataRow 的列值的方法。 N 层数据应用程序是以数据为中心的应用程序,分为多个逻辑层(或层)。 典型的 N 层应用程序包括一个表 示层、一个中间层和一个数据层。 将应用程序组件分离到不同的层可提高应用程序的可维护性和可伸缩性。 有关 N 层数据应用程序的更多信息,请参见 N-Tier Data Applications。 在 N 层应用程序中,DataSet 通常用于中间层以缓存 Web 应用程序的信息。 LINQ to DataSet 查询功能通过 扩展方法实现,并扩展现有的 ADO.NET 2.0 DataSet。 下面的关系图演示 LINQ to DataSet 如何与 DataSet 相关并适应 N 层应用程序: 概念 查询数据集 (LINQ to DataSet) Language-Integrated Query (LINQ) LINQ to SQL 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/dc20a8fb-03f6... 全部折叠 代码:C# .NET Framework 开发人员指南 向数据集中加载数据 请参见 发送反馈意见 DataSet 对象只有在填充后才能使用 LINQ to DataSet 进行查询。 填充 DataSet 有多种不同的方式。 例如,您可 以使用 LINQ to SQL 来查询数据库并将结果加载到 DataSet 中。 有关更多信息,请参见 LINQ to SQL。 另一种将数据加载到 DataSet 中的常见方式是使用 DataAdapter 类从数据库中检索数据。 下面的示例阐释了这一 过程。 示例 此示例使用 DataAdapter 查询 AdventureWorks 数据库中从 2002 年起的销售信息,并将结果加载到 DataSet 中。 填充 DataSet 以后,可以通过使用 LINQ to DataSet 对其编写查询。 LINQ to DataSet 示例的示例查询 中使用了本示例中的 FillDataSet 方法。 有关更多信息,请参见查询数据集 (LINQ to DataSet)。 C# 复制代码 try { // Create a new adapter and give it a query to fetch sales order, contact, // address, and product information for sales in the year 2002. Point connection // information to the configuration setting "AdventureWorks". string connectionString = "Data Source=localhost;Initial Catalog=AdventureWorks;" + "Integrated Security=true;"; SqlDataAdapter da = new SqlDataAdapter( "SELECT SalesOrderID, ContactID, OrderDate, OnlineOrderFlag, " + "TotalDue, SalesOrderNumber, Status, ShipToAddressID, BillToAddressID " + "FROM Sales.SalesOrderHeader " + "WHERE DATEPART(YEAR, OrderDate) = @year; " + "SELECT d.SalesOrderID, d.SalesOrderDetailID, d.OrderQty, " + "d.ProductID, d.UnitPrice " + "FROM Sales.SalesOrderDetail d " + "INNER JOIN Sales.SalesOrderHeader h " + "ON d.SalesOrderID = h.SalesOrderID " + "WHERE DATEPART(YEAR, OrderDate) = @year; " + "SELECT p.ProductID, p.Name, p.ProductNumber, p.MakeFlag, " + "p.Color, p.ListPrice, p.Size, p.Class, p.Style, p.Weight " + "FROM Production.Product p; " + "SELECT DISTINCT a.AddressID, a.AddressLine1, a.AddressLine2, " + "a.City, a.StateProvinceID, a.PostalCode " + "FROM Person.Address a " + "INNER JOIN Sales.SalesOrderHeader h " + "ON a.AddressID = h.ShipToAddressID OR a.AddressID = h.BillToAddressID " + "WHERE DATEPART(YEAR, OrderDate) = @year; " + "SELECT DISTINCT c.ContactID, c.Title, c.FirstName, " + "c.LastName, c.EmailAddress, c.Phone " + "FROM Person.Contact c " + "INNER JOIN Sales.SalesOrderHeader h " + "ON c.ContactID = h.ContactID " + "WHERE DATEPART(YEAR, OrderDate) = @year;", connectionString); // Add table mappings. da.SelectCommand.Parameters.AddWithValue("@year", 2002); da.TableMappings.Add("Table", "SalesOrderHeader"); da.TableMappings.Add("Table1", "SalesOrderDetail"); da.TableMappings.Add("Table2", "Product"); da.TableMappings.Add("Table3", "Address"); da.TableMappings.Add("Table4", "Contact"); // Fill the DataSet. da.Fill(ds); // Add data relations. DataTable orderHeader = ds.Tables["SalesOrderHeader"]; DataTable orderDetail = ds.Tables["SalesOrderDetail"]; DataRelation order = new DataRelation("SalesOrderHeaderDetail", orderHeader.Columns["SalesOrderID"], orderDetail.Columns["SalesOrderID"], true); ds.Relations.Add(order); 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/a53e5dc1-9669... 请参见 DataTable contact = ds.Tables["Contact"]; DataTable orderHeader2 = ds.Tables["SalesOrderHeader"]; DataRelation orderContact = new DataRelation("SalesOrderContact", contact.Columns["ContactID"], orderHeader2.Columns["ContactID"], true); ds.Relations.Add(orderContact); } catch (SqlException ex) { Console.WriteLine("SQL exception occurred: " + ex.Message); } 概念 LINQ to DataSet 概述 查询数据集 (LINQ to DataSet) LINQ to DataSet 示例 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/a53e5dc1-9669... 全部折叠 代码:C# .NET Framework 开发人员指南 下载示例数据库 (LINQ to DataSet) 请参见 发送反馈意见 LINQ to DataSet 文档中的示例和演练使用 AdventureWorks 示例数据库。 您可以从 Microsoft 下载站点免费下载此产品。 LINQ to DataSet 文档中的示例和演练使用 SQL Server 作为数据存储区。 免费提供的 SQL Server Express Edition 也可代替 SQL Server 用作数据存储区。 下载并安装 AdventureWorks 数据库 下载 SQL Server Express Edition 请参见 下载和安装适用于 SQL Server 的 AdventureWorks 示例数据库 1. 打开 Internet Explorer。 2. 转到 SQL Server 2005 Samples and Sample Databases(SQL Server 2005 示例和示例数据库)网站。 3. 按照说明下载适用于您的处理器类型的 AdventureWorks 示例数据库(如 AdventureWorksDB.msi)并将该 .MSI 文件保存到您的本地计算机。 4. 如果您通过下载或在 SQL Server 安装过程中安装了先前版本的 AdventureWorks,则在运行 AdventureWorks.msi 之前必须删除该版本。 删除先前下载的 AdventureWorks 示例数据库 1. 删除 AdventureWorks 或 AdventureWorksDW 数据库。 2. 从“添加或删除程序”中选择“AdventureWorksDB”或“AdventureWorksBI”,然后单击“删除”。 删除先前使用安装程序安装的 AdventureWorks 示例数据库 1. 删除 AdventureWorks 或 AdventureWorksDW 数据库。 2. 从“添加或删除程序”中选择“Microsoft SQL Server 2005”,然后单击“更改”。 3. 从“选择组件”中选择“工作站组件”,然后单击“下一步”。 4. 从“欢迎使用 SQL Server 安装向导”中单击“下一步”。 5. 从“系统配置检查”中单击“下一步”。 6. 从“更改或删除实例”中单击“更改已安装的组件”。 7. 从“功能选择”中展开“文档、示例和示例数据库”节点。 8. 选择“示例代码和应用程序”。 展开“示例数据库”,选择要删除的示例数据库,然后选择“整个功能将不可用”。 单击“下一步”。 9. 单击“安装”,完成安装向导。 将 AdventureWorks 示例数据库文件附加到 SQL Server 的实例 1. 下载了文件示例数据库安装程序文件后,双击“AdventureWorksDB.msi”文件(或您下载的文件)以安装该数据库。 默认情况下,该数据库安装在 c:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data 位置。 2. 通过执行下面的脚本 SQLCMD 或 SQL Server Management Studio,将 AdventureWorks 数据库文件附加到 SQL Server 的实例: 如果您已将这些文件安装到其他驱动器或目录,则必须在执行 sp_attach_db 存储过程之前适当修改路径。 复制代码 exec sp_attach_db @dbname=N'AdventureWorks', @filename1=N'C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data\AdventureWorks_Data.mdf', @filename2=N'C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data\AdventureWorks_log.ldf' LINQ to DataSet 一节中的示例和演练使用 SQL Server 2005 作为数据存储区,但也可以修改为使用 SQL Server Express Edition。 SQL Server Express Edition 免费提供,并且可以通过应用程序重新分发。 如果您要使用 Visual Studio,则 Pro 和更高的版本将会包括 SQL Server Express Edition。 下载并安装 SQL Server Express Edition 1. 启动 Internet Explorer。 2. 转到 Microsoft SQL 下载页。 3. 按照网站上的安装说明操作。 概念 入门 (LINQ to DataSet) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/eb42a7af-d410... 全部折叠 代码:C# .NET Framework 开发人员指南 如何: 在 Visual Studio 中创建 LINQ to DataSet 项目 请参见 发送反馈意见 不同类型的 LINQ 项目需要某些导入的命名空间 (Visual Basic) 或 using 指令 (C#) 和引用。 最低要求是对 System.Core.dll 的引用和针对 System.Linq 的 using 指令。 默认情况下,如果创建一个新的 Visual C# 2008 项 目,这些都可以自动提供。LINQ to DataSet 还需要对 System.Data.dll 和 System.Data.DataSetExtensions.dll 的引 用以及 Imports (Visual Basic) 或 using (C#) 指令。 如果您要从早期版本的 Visual Studio 升级某个项目,则可能必须手动提供这些与 LINQ 相关的引用。 您可能还必 须将项目手动设置为面向 .NET Framework 3.5 版。 面向 .NET Framework 3.5 1. 在 Visual Studio 2008 中,创建一个新的 Visual Basic 或 C# 项目。 或者,您可以打开一个在 Visual Studio 2005 中创建的 Visual Basic 或 C# 项目,并按照提示将其转换为 Visual Studio 2008 项目。 2. 对于 C# 项目,单击“项目”菜单,然后单击“属性”。 a. 在“应用程序”属性页的“目标 Framework”下拉列表中选择“.NET Framework 3.5”。 3. 对于 Visual Basic 项目,单击“项目”菜单,然后单击“属性”。 a. 在“编译”属性页,单击“高级编译选项”,然后在“目标 Framework(所有配置)”下拉列表中选择 “.NET Framework 3.5”。 4. 在“项目”菜单上,单击“添加引用”,单击“.NET”选项卡,向下滚动到“System.Core”并单击,然后单 击“确定”。 5. 向源代码文件或项目中添加用于 System.Linq 的 using 指令或导入的命名空间。 有关更多信息,请参见using Directive (C# Reference)或How to: Add or Remove Imported Namespaces (Visual Basic)。 启用 LINQ to DataSet 功能 1. 必要时,按照本主题前面的步骤添加对 System.Core.dll 的引用和用于 System.Linq 的 using 指令或导入的 命名空间。 2. 在 C# 或 Visual Basic 中,单击“项目”菜单,然后单击“添加引用”。 3. 在“添加引用”对话框中,单击“.NET”选项卡(如果它不在最前面)。 向下滚动到“System.Data”和 “System.Data.DataSetExtensions”并单击它们。 单击“确定”按钮。 4. 向源代码文件或项目中添加用于 System.Data 的 using 指令或导入的命名空间。 有关更多信息,请参见 using Directive (C# Reference)或How to: Add or Remove Imported Namespaces (Visual Basic)。 5. 添加对 System.Data.DataSetExtensions.dll 的引用以便可以使用 LINQ to Dataset 的功能。 添加对 System.Data.dll 的引用(如果尚不存在)。 6. 或者,添加用于 System.Data.Common 或 System.Data.SqlClient 的 using 指令或导入的命名空间,具 体取决于如何连接到数据库。 请参见 注意: 如果要从命令提示符执行生成,则必须手动引用 drive:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5 中与 LINQ 相关的 DLL。 概念 入门 (LINQ to DataSet) Getting Started with LINQ 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/49ba6cb0-cdd2... 全部折叠 代码:C# .NET Framework 开发人员指南 编程指南 (LINQ to DataSet) 请参见 发送反馈意见 本节提供有关使用 LINQ to DataSet 进行编程的概念性信息和示例。 本节内容 参考 请参见 LINQ to DataSet 中的查询 提供有关如何编写 LINQ to DataSet 查询的信息。 查询数据集 (LINQ to DataSet) 说明如何查询 DataSet 对象。 比较 DataRow (LINQ to DataSet) 说明如何使用 DataRowComparer 对象来比较数据行。 通过查询创建数据表 (LINQ to DataSet) 提供有关通过使用 CopyToDataTable 方法从 LINQ to DataSet 查询创建 DataTable 的信息。 泛型 Field 和 SetField 方法 (LINQ to DataSet) 提供有关泛型 Field 和 SetField 方法的信息。 数据绑定和 LINQ to DataSet 说明使用 DataView 对象的数据绑定。 调试 LINQ to DataSet 查询 提供有关调试和排除 LINQ to DataSet 查询故障的信息。 安全性 (LINQ to DataSet) 说明 LINQ to DataSet 中的安全问题。 LINQ to DataSet 示例 提供使用 LINQ 运算符的查询示例。 DataRowComparer DataRowExtensions DataTableExtensions DataView 概念 LINQ to ADO.NET LINQ General Programming Guide LINQ Framework 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/977aedd7-008... 全部折叠 代码:C# .NET Framework 开发人员指南 LINQ to DataSet 中的查询 请参见 发送反馈意见 查询是一种从数据源检索数据的表达式。 查询通常用专用查询语言表示,如用于关系数据库的 SQL 和用于 XML 的 XQuery。 因此,开发人员一度不得不学习适用于所要查询的每种数据源或数据格式的新查询语言。Language- Integrated Query (LINQ) 提供了一种较为简单的一致模型,适用于各种数据源和格式的数据。 在 LINQ 查询中,您 始终使用编程对象。 一个 LINQ 查询操作包括三个操作: 获取数据源、创建查询和执行查询。 实现 IEnumerable(T) 泛型接口的数据源可以通过 LINQ 进行查询。 对 DataTable 调用 AsEnumerable 将返回实现 泛型 IEnumerable(T) 接口的对象,作为 LINQ to DataSet 查询的数据源。 在查询中,指定您要从数据源中检索的准确信息。 查询也可以指定返回信息之前信息的排序、分组和表现方式。 在 LINQ 中,查询存储在变量中。 如果查询旨在返回一系列值,则查询变量本身也必须为可枚举类型。 此查询变 量不执行任何操作,也不返回任何数据;它只存储查询信息。 创建查询后必须执行该查询以检索任何数据。 在返回一系列值的查询中,查询变量本身从不保存查询结果,它只存储查询命令。 查询的执行将推迟到在 foreach 或 For Each 循环中循环访问查询变量之后进行。 这称为“延迟执行”;也就是说,查询将会在构造之后的某个时 间执行。 这意味着您可以根据需要频繁地执行查询。 例如,当您的数据库由其他应用程序不断更新时,此功能将 会很有用。 在您的应用程序中,您可以创建查询以检索最新信息并重复执行查询,每次返回更新的信息。 与返回一系列值的延迟查询相反,返回单一实例值的查询将立即执行。 Count、Max、Average 和 First 是一些单一 实例查询的示例。 因为需要查询结果来计算单一实例结果,因此这些查询将会立即执行。 例如,若要计算查询结 果的平均值,则必须执行查询,以便求平均值函数具有要使用的输入数据。 您也可以对查询使用 ToList(TSource) 或 ToArray(TSource) 方法以强制立即执行不生成单一实例值的查询。 当想要缓存查询结果时,这些强制立即执行 的技术可能会很有用。 有关延迟和立即执行查询的更多信息,请参见 Getting Started with LINQ。 查询 LINQ to DataSet 查询可以使用两种不同的语法进行表述: 查询表达式语法和基于方法的查询语法。 查询表达式语法 查询表达式是一种声明性查询语法。 此语法使开发人员能够以类似于 SQL 的格式用 C# 或 Visual Basic 编写 查询。 通过使用查询表达式语法,您可以用最少的代码对数据源执行复杂的筛选、排序和分组操作。 有关更 多信息,请参见 LINQ Query Expressions和Basic Query Operators。 查询表达式语法是 C# 3.0 和 Visual Basic 2008 中的新功能。 不过,.NET Framework 公共语言运行库 (CLR) 无法读取查询表达式语法本身。 因此,在编译时,查询表达式将转换为 CLR 能理解的形式,即方法调用。 这些方法称为“标准查询运算符”。 作为开发人员,您可以选择使用方法语法而不使用查询语法直接调用这 些方法。 有关更多信息,请参见Query Syntax versus Method Syntax。 有关如何使用标准查询运算符的更多 信息,请参见 LINQ General Programming Guide。 下面的示例使用 Select 返回 Product 表中的所有行并显示产品名称。 基于方法的查询语法 表述 LINQ to DataSet 查询的另一种方法是使用基于方法的查询。 基于方法的查询语法是对 LINQ 运算符方 法的一系列直接方法调用,将 Lambda 表达式作为参数进行传递。 有关更多信息,请参见 Lambda Expressions。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable products = ds.Tables["Product"]; IEnumerable query = from product in products.AsEnumerable() select product; Console.WriteLine("Product Names:"); foreach (DataRow p in query) { Console.WriteLine(p.Field("Name")); } 页码,1/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/c1a78fa8-9f0c-... 编写查询 此示例使用 Select 返回 Product 中的所有行并显示产品名称。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable products = ds.Tables["Product"]; var query = products.AsEnumerable(). Select(product => new { ProductName = product.Field("Name"), ProductNumber = product.Field("ProductNumber"), Price = product.Field("ListPrice") }); Console.WriteLine("Product Info:"); foreach (var productInfo in query) { Console.WriteLine("Product name: {0} Product number: {1} List price: ${2} ", productInfo.ProductName, productInfo.ProductNumber, productInfo.Price); } 如本主题前面所述,当查询旨在返回一系列值时,查询变量本身只存储查询命令。 如果查询不包含可使查询 立即执行的方法,则查询的实际执行将会推迟,直到在 foreach 或 For Each 循环中循环访问查询变量。 延 迟执行可使多个查询组合在一起或使查询得到扩展。 扩展查询时,将修改查询以包括新操作,最终执行将反 映这些更改。 在下面的示例中,第一个查询返回所有产品。 第二个查询通过使用 Where 扩展第一个查询, 以返回大小为“L”的所有产品: 执行一个查询后,不会再编写其他查询,并且所有后续查询都将使用驻留在内存中的 LINQ 运算符。 当在 foreach 或 For Each 语句中循环访问查询变量或通过调用可导致立即执行的 LINQ 转换运算符之一时,查询 将会开始执行。 这些运算符包括: ToList(TSource)、ToArray(TSource)、ToLookup 和 ToDictionary。 在下面的示例中,第一个查询返回按定价排序的所有产品。 ToArray(TSource) 方法用于强制立即执行查询: C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable products = ds.Tables["Product"]; IEnumerable productsQuery = from product in products.AsEnumerable() select product; IEnumerable largeProducts = productsQuery.Where(p => p.Field("Size") == "L"); Console.WriteLine("Products of size 'L':"); foreach (DataRow product in largeProducts) { Console.WriteLine(product.Field("Name")); } C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable products = ds.Tables["Product"]; IEnumerable query = from product in products.AsEnumerable() orderby product.Field("ListPrice") descending select product; 页码,2/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/c1a78fa8-9f0c-... 请参见 // Force immediate execution of the query. IEnumerable productsArray = query.ToArray(); Console.WriteLine("Every price from highest to lowest:"); foreach (DataRow prod in productsArray) { Console.WriteLine(prod.Field("ListPrice")); } 概念 编程指南 (LINQ to DataSet) 查询数据集 (LINQ to DataSet) Getting Started with LINQ in C# Getting Started with LINQ in Visual Basic 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/c1a78fa8-9f0c-... 全部折叠 代码:C# .NET Framework 开发人员指南 查询数据集 (LINQ to DataSet) 请参见 发送反馈意见 用数据填充 DataSet 对象后,您可以开始查询该对象。 使用 LINQ to DataSet 表述查询类似于对启用 LINQ 的其他 数据源使用 Language-Integrated Query (LINQ)。 但请记住,在对 DataSet 对象使用 LINQ 查询时,所查询的是 DataRow 对象的枚举,而不是自定义类型的枚举。 这意味着可以在 LINQ 查询中使用 DataRow 类的任意成员。 这 允许您创建丰富而复杂的查询。 和 LINQ 的其他实现一样,您可以用两种不同形式创建 LINQ to DataSet 查询: 查询表达式语法和基于方法的查询 语法。 有关这两种形式的更多信息,请参见 Getting Started with LINQ。 您可以使用查询表达式语法或基于方法的 查询语法对 DataSet 中的单个表、对 DataSet 中的多个表或对类型化 DataSet 中的表执行查询。 本节内容 请参见 单表查询 (LINQ to DataSet) 说明如何执行单表查询。 交叉表查询 (LINQ to DataSet) 说明如何执行交叉表查询。 查询类型化数据集 说明如何查询类型化 DataSet 对象。 概念 LINQ to DataSet 示例 向数据集中加载数据 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/bb68d2e4-623... 全部折叠 代码:C# .NET Framework 开发人员指南 单表查询 (LINQ to DataSet) 请参见 发送反馈意见 Language-Integrated Query (LINQ) 查询适用于实现 IEnumerable(T) 接口或 IQueryable 接口的数据源。 DataTable 类不实现任何一个接口,所以如果要使用 DataTable 作为LINQ 查询的 From 子句中的源,则必须调用 AsEnumerable 方法。 下面的示例获取 SalesOrderHeader 表中的所有联机订单并将订单 ID、订单日期和订单编号输出到控制台。 本地变量查询使用查询表达式进行初始化,该表达式通过应用标准查询运算符中的一个或多个查询运算符,或者在 LINQ to DataSet 的情况下应用特定于 DataSet 类的一个或多个运算符对一个或多个信息源进行运算。 前面示例中 的查询表达式使用两个标准查询运算符: Where 和 Select。 Where 子句基于条件筛选序列,在本例中,OnlineOrderFlag 设置为 true。 Select 运算符分配并返回一个可枚举 对象,该对象可捕获传递给运算符的参数。 在上面的示例中,创建了一个具有三个属性的匿名类型: SalesOrderID、OrderDate 和 SalesOrderNumber。 这三个属性的值设置为 SalesOrderHeader 表中的 SalesOrderID、OrderDate 和 SalesOrderNumber 列值。 然后,foreach 循环枚举由 Select 返回的可枚举对象,并生成查询结果。 由于查询是一种可以实现 IEnumerable (T) 的 Enumerable 类型,因此,查询的计算将推迟到使用 foreach 循环来循环访问查询变量之后进行。 推迟查询 计算可使查询保持为可进行多次计算的值,每次计算都可能生成不同的结果。 Field 方法提供对 DataRow 列值的访问,而 SetField(前面的示例未演示)设置 DataRow 中的列值。 Field 方法和 SetField 方法都可以处理可以为 null 的类型,因此不必显式检查 null 值。 这两种方法也都是泛型方法,这意味着 您不必强制转换返回类型。 您可以使用 DataRow 中预先存在的列访问器(例如 o["OrderDate"]),但是这样做 要求您将返回对象强制转换为相应的类型。 如果列可以为 null,则必须使用 IsNull 方法检查值是否为 null。 有关 更多信息,请参见 泛型 Field 和 SetField 方法 (LINQ to DataSet)。 请注意,Field 方法和 SetField 方法的泛型参数 T 中指定的数据类型必须与基础值的类型相匹配,否则将引发 InvalidCastException。 指定的列名称也必须与 DataSet 中的列名称相匹配,否则将引发 ArgumentException。 在 这两种情况下,异常都是在执行查询期间的运行时数据枚举时引发的。 请参见 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderHeader"]; var query = from order in orders.AsEnumerable() where order.Field("OnlineOrderFlag") == true select new { SalesOrderID = order.Field("SalesOrderID"), OrderDate = order.Field("OrderDate"), SalesOrderNumber = order.Field("SalesOrderNumber") }; foreach (var onlineOrder in query) { Console.WriteLine("Order ID: {0} Order date: {1:d} Order number: {2}", onlineOrder.SalesOrderID, onlineOrder.OrderDate, onlineOrder.SalesOrderNumber); } 概念 交叉表查询 (LINQ to DataSet) 查询类型化数据集 泛型 Field 和 SetField 方法 (LINQ to DataSet) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/0b74bcf8-3f87... 全部折叠 代码:C# .NET Framework 开发人员指南 交叉表查询 (LINQ to DataSet) 请参见 发送反馈意见 除了查询单个表外,也可以在 LINQ to DataSet 中执行交叉表查询。 这可以通过使用“联接”来完成。 联接就是将一个数据 源中的对象与另一个数据源中具有相同公共属性的对象(例如产品或联系人 ID)相关联。 在面向对象的编程中,由于每个对 象都有引用另一个对象的成员,所以对象间的关系相对较容易导航。 但在外部数据库表中,导航关系不像这样简单。 数据库 表不包含内置关系。 在这些情况下,可以通过联接操作来匹配每个源中的元素。 例如,假设有两个分别包含产品信息和销售 信息的表,您可以使用联接操作来匹配同一销售订单的销售信息和产品。 Language-Integrated Query (LINQ) 框架提供两个联接运算符,Join 和 GroupJoin。这些运算符执行同等联接, 即仅在键相等 时匹配两个数据源的联接。 (相对而言,Transact-SQL 支持 equals 以外的其他连接运算符,如 less than 运算符)。 对于关系数据库,Join 实现内部联接。 内部联接是仅返回在相对数据集中具有匹配对象的那些对象的一种联接类型。 对于关系数据库,GroupJoin 运算符没有直接等效项,它们实现内部联接和左外部联接的超集。左外部联接是一种即使在第二个 集合中没有关联元素的情况下也会返回第一个(左侧)集合中每个元素的联接。 有关联接的更多信息,请参见Joining。 示例 请参见 下面的示例对 AdventureWorks 示例数据库中的 SalesOrderHeader 和 SalesOrderDetail 表执行传统联接以获取 8 月份以 来的在线订单。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderHeader"]; DataTable details = ds.Tables["SalesOrderDetail"]; var query = from order in orders.AsEnumerable() join detail in details.AsEnumerable() on order.Field("SalesOrderID") equals detail.Field("SalesOrderID") where order.Field("OnlineOrderFlag") == true && order.Field("OrderDate").Month == 8 select new { SalesOrderID = order.Field("SalesOrderID"), SalesOrderDetailID = detail.Field("SalesOrderDetailID"), OrderDate = order.Field("OrderDate"), ProductID = detail.Field("ProductID") }; foreach (var order in query) { Console.WriteLine("{0}\t{1}\t{2:d}\t{3}", order.SalesOrderID, order.SalesOrderDetailID, order.OrderDate, order.ProductID); } 概念 查询数据集 (LINQ to DataSet) 单表查询 (LINQ to DataSet) 查询类型化数据集 Joining LINQ to DataSet 示例 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/6819a16f-8656... 全部折叠 代码:C# .NET Framework 开发人员指南 查询类型化数据集 请参见 发送反馈意见 如果在应用程序设计时已知 DataSet 的架构,则建议在使用 LINQ to DataSet 时使用类型化 DataSet。 类型化 DataSet 是从 DataSet 中派生的类。 因此,它继承 DataSet 的所有方法、事件和属性。 此外,类型化 DataSet 还 提供强类型方法、事件和属性。 这意味着可以按名称而不使用基于集合的方法来访问表和列。 这可使查询更简 单、更具可读性。 有关更多信息,请参见类型化数据集 (ADO.NET)。 LINQ to DataSet 还支持对类型化 DataSet 进行查询。 对于类型化 DataSet,您不必使用泛型 Field 方法或 SetField 方法即可访问列数据。 由于 DataSet 中包括类型信息,因此属性名称在编译时可用。 LINQ to DataSet 提 供对正确类型的列值的访问,以便可以在编译代码时而不是在运行时捕获类型不匹配错误。 在可以开始查询类型化 DataSet 之前,必须先通过使用 Visual Studio 2008 中的数据集设计器生成该类。 有关更多 信息,请参见How to: Create a Typed Dataset。 示例 请参见 下面的示例演示对类型化 DataSet 进行查询: C# 复制代码 var query = from o in orders where o.OnlineOrderFlag == true select new { o.SalesOrderID, o.OrderDate, o.SalesOrderNumber }; foreach(var order in query) { Console.WriteLine("{0}\t{1:d}\t{2}", order.SalesOrderID, order.OrderDate, order.SalesOrderNumber); } 概念 查询数据集 (LINQ to DataSet) 交叉表查询 (LINQ to DataSet) 单表查询 (LINQ to DataSet) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/ad712fa1-2baf-... 全部折叠 代码:C# .NET Framework 开发人员指南 比较 DataRow (LINQ to DataSet) 请参见 发送反馈意见 Language-Integrated Query (LINQ) 定义多种用于比较源元素的集合运算符以查看它们是否相等。LINQ 提供下面的集合运算 符: z Distinct z Union z Intersect z Except 这些运算符通过对每个元素集合调用 GetHashCode 和 Equals 方法来比较源元素。 对于 DataRow,这些运算符执行引用比 较,在对表格格式数据执行集合操作的情况下,这通常不是理想的行为。 对于集合运算,通常需要确定元素值而不是元素 引用是否相等。 因此,向 LINQ to DataSet 中添加了 DataRowComparer 类。 此类可用于比较行值。 DataRowComparer 类包含 DataRow 的值比较实现,所以此类可用于设置集合操作,例如 Distinct。 此类不能直接实例化, 而必须使用 Default 属性返回 DataRowComparer 的实例。 然后调用 Equals(DataRow, DataRow) 方法并作为输入参数传入 要进行比较的两个 DataRow 对象。 如果两个 DataRow 对象中排序的列值集合相等,则 Equals(DataRow, DataRow) 方法 返回 true,否则返回 false。 示例 请参见 此示例使用 Intersect 返回两个表中都存在的联系人。 示例 下面的示例比较两个行并获取它们的哈希代码。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable contactTable = ds.Tables["Contact"]; // Create two tables. IEnumerable query1 = from contact in contactTable.AsEnumerable() where contact.Field("Title") == "Ms." select contact; IEnumerable query2 = from contact in contactTable.AsEnumerable() where contact.Field("FirstName") == "Sandra" select contact; DataTable contacts1 = query1.CopyToDataTable(); DataTable contacts2 = query2.CopyToDataTable(); // Find the intersection of the two tables. var contacts = contacts1.AsEnumerable().Intersect(contacts2.AsEnumerable(), DataRowComparer.Default); Console.WriteLine("Intersection of contacts tables"); foreach (DataRow row in contacts) { Console.WriteLine("Id: {0} {1} {2} {3}", row["ContactID"], row["Title"], row["FirstName"], row["LastName"]); } 概念 向数据集中加载数据 LINQ to DataSet 示例 参考 DataRowComparer 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/8fe0eadf-297b-... 全部折叠 代码:C# .NET Framework 开发人员指南 通过查询创建数据表 (LINQ to DataSet) 请参见 发送反馈意见 数据绑定是 DataTable 对象的一种常用形式。 CopyToDataTable 方法接收查询结果并将数据复制到 DataTable 中, 后者随后会使用该数据进行数据绑定。 在执行数据操作后,新的 DataTable 将合并回源 DataTable。 CopyToDataTable 方法使用下面的过程来通过查询创建 DataTable: 1. CopyToDataTable 方法克隆源表中的 DataTable(实现 IQueryable(T) 接口的 DataTable 对象)。 IEnumerable 源通常来源于 LINQ to DataSet 表达式或方法查询。 2. 克隆的 DataTable 的架构从源表中枚举的第一个 DataRow 对象的列生成,克隆表的名称是源表的名称后面追 加单词“query”。 3. 对于源表中的每一行,会将行内容复制到新 DataRow 对象中,然后将该对象插入到克隆表中。 RowState 和 RowError 属性在整个复制操作过程中保留。 如果源中的 ArgumentException 对象来自不同的表,则会引发 DataRow。 4. 复制完可查询的输入表中的所有 DataRow 对象后,将返回克隆的 DataTable。 如果源序列不包含任何 DataRow 对象,则该方法将返回一个空 DataTable。 请注意,调用 CopyToDataTable 方法将导致执行已绑定到源表的查询。 当 CopyToDataTable 方法在源表的行中遇到空引用或可以为 null 的值类型时,它将用 Value 替换该值。 这样可以 在返回的 DataTable 中正确处理 Null 值。 示例 请参见 下面的示例查询 SalesOrderHeader 表中 2001 年 8 月 8 日以后的订单,并使用 CopyToDataTable 方法通过 查询创建 DataTable。 然后将 DataTable 绑定到作为 DataGridView 的代理的 BindingSource。 C# 复制代码 // Bind the System.Windows.Forms.DataGridView object // to the System.Windows.Forms.BindingSource object. dataGridView.DataSource = bindingSource; // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderHeader"]; // Query the SalesOrderHeader table for orders placed // after August 8, 2001. IEnumerable query = from order in orders.AsEnumerable() where order.Field("OrderDate") > new DateTime(2001, 8, 1) select order; // Create a table from the query. DataTable boundTable = query.CopyToDataTable(); // Bind the table to a System.Windows.Forms.BindingSource object, // which acts as a proxy for a System.Windows.Forms.DataGridView object. bindingSource.DataSource = boundTable; 概念 编程指南 (LINQ to DataSet) 泛型 Field 和 SetField 方法 (LINQ to DataSet) LINQ to DataSet 示例 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/1b97afeb-03f8... 全部折叠 代码:C# .NET Framework 开发人员指南 泛型 Field 和 SetField 方法 (LINQ to DataSet) 请参见 发送反馈意见 LINQ to DataSet 为 DataRow 类提供用于访问列值的扩展方法: Field 方法和 SetField 方法。 这些方法使开发人员 能够更轻松地访问列值,特别是 Null 值。 DataSet 使用 Value 来表示 Null 值,而 LINQ 使用 .NET Framework 2.0 中引入的可以为 null 的类型支持。 使用 DataRow 中预先存在的列访问器需要将返回对象强制转换成相应的类型。 如果 DataRow 中的特定字段可以为 null,则必须显示检查 Null 值,因为返回 Value 并隐式地将其强制转换为另一 种类型会引发 InvalidCastException。 在下面的示例中,如果不使用 IsNull 方法检查 Null 值,则在索引器返回 Value 并试图将其强制转换为 String 的情况下会引发异常。 Field 方法提供对 DataRow 列值的访问,而 SetField 设置 DataRow中的列值。 Field 方法和 SetField 方法都可以处 理可以为 null 的类型,因此不必像前面的示例那样检查 Null 值。 这两种方法也都是泛型方法,因此不必强制转换 返回类型。 下面的示例使用 Field 方法。 请注意,Field 方法和 SetField 方法的泛型参数 T 中指定的数据类型必须与基础值的类型相匹配。 否则,将引发 InvalidCastException 异常。 指定的列名称也必须与 DataSet 中的列名称相匹配,否则将引发 ArgumentException。 在这两种情况下,异常都是在执行查询期间的运行时数据枚举时引发的。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable products = ds.Tables["Product"]; var query = from product in products.AsEnumerable() where !product.IsNull("Color") && (string)product["Color"] == "Red" select new { Name = product["Name"], ProductNumber = product["ProductNumber"], ListPrice = product["ListPrice"] }; foreach (var product in query) { Console.WriteLine("Name: {0}", product.Name); Console.WriteLine("Product number: {0}", product.ProductNumber); Console.WriteLine("List price: ${0}", product.ListPrice); Console.WriteLine(""); } C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable products = ds.Tables["Product"]; var query = from product in products.AsEnumerable() where product.Field("Color") == "Red" select new { Name = product.Field("Name"), ProductNumber = product.Field("ProductNumber"), ListPrice = product.Field("ListPrice") }; foreach (var product in query) { Console.WriteLine("Name: {0}", product.Name); Console.WriteLine("Product number: {0}", product.ProductNumber); Console.WriteLine("List price: ${0}", product.ListPrice); Console.WriteLine(""); } 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/1883365f-9d6c... SetField 方法本身不执行任何类型转换。 但这并不意味着不会发生类型转换。 SetField 方法公开 DataRow 类的 ADO.NET 2.0 行为。 类型转换可以由 DataRow 对象执行,转换的值随后将保存到 DataRow 对象。 请参见 参考 DataRowExtensions 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/1883365f-9d6c... 全部折叠 代码:C# .NET Framework 开发人员指南 数据绑定和 LINQ to DataSet 请参见 发送反馈意见 数据绑定是在应用程序 UI 和业务逻辑之间建立连接的过程。 如果绑定具有正确的设置,并且数据提供适当的通 知,则在数据更改其值时,绑定到该数据的元素会自动反映更改。 DataSet 是数据驻留在内存中的表示形式,不管 包含的数据来自什么数据源,它都可以提供一致的关系编程模型。 使用 ADO.NET 2.0 DataView 可以对存储在 DataTable 中的数据进行排序和筛选。 数据绑定应用程序中经常会使用此功能。 通过使用 DataView,您可以使用 不同的排序顺序公开表中的数据,并且可以按行状态或基于筛选器表达式来筛选数据。 有关 DataView 对象的更多 信息,请参见 DataView (ADO.NET)。 LINQ to DataSet 使开发人员能够通过使用Language-Integrated Query (LINQ) 来创建针对 DataSet 的复杂而功能强 大的查询。 不过,LINQ to DataSet 查询可返回 DataRow 对象的枚举,在绑定方案中不易于使用。 为了简化绑 定,您可以通过 LINQ to DataSet 查询创建一个 DataView。 此 DataView 使用查询中指定的筛选和排序,但更适合 于数据绑定。LINQ to DataSet 通过提供基于表达式的 LINQ 筛选和排序,扩展了 DataView 筛选和排序的功能,它 允许执行比基于字符吕睥筛选和排序更为复杂且功能更为强大的筛选和排序操作。 请注意,DataView 表示查询本身,而不是处于查询前面的视图。 DataView 绑定到 UI 控件(如 DataGrid 或 DataGridView),提供简单的数据绑定模型。 也可以从 DataTable 创建 DataView,从而提供该表的默认视图。 本节内容 请参见 创建 DataView 对象 (LINQ to DataSet) 提供有关创建 DataView 的信息。 使用 DataView 进行筛选 (LINQ to DataSet) 说明如何使用 DataView 进行筛选。 使用 DataView 进行排序 (LINQ to DataSet) 说明如何使用 DataView 进行排序。 DataView 性能 提供有关 DataView 和性能的信息。 如何: 将 DataView 对象绑定到 Windows 窗体 DataGridView 控件 说明如何将 DataView 对象绑定到 DataGridView。 概念 编程指南 (LINQ to DataSet) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/310bff4a-32dd... 全部折叠 代码:C# .NET Framework 开发人员指南 创建 DataView 对象 (LINQ to DataSet) 请参见 发送反馈意见 在 LINQ to DataSet 上下文中创建 DataView 有两种方式。 您可以通过针对 DataTable 的 LINQ to DataSet 查询创建 DataView,也可以从类型化或非类型化 DataTable 创建该对象。 在这两种情况下,您可以通过使用 AsDataView 扩展方法之 一来创建 DataView;DataView 不能直接在 LINQ to DataSet 上下文中构造。 创建 DataView 之后,您可以将其绑定到 Windows 窗体应用程序或 ASP.NET 应用程序中的 UI 控件上,或者更改筛选和排序 设置。 DataView 构造一个索引,该索引可显著提高可使用该索引的操作(如筛选和排序)的性能。 创建 DataView 及修改任何排序 或筛选信息时,均会生成 DataView 的索引。 创建 DataView 然后设置排序或筛选信息会使索引生成至少两次: 一次是在创 建 DataView 时,另一次是在修改任何排序或筛选属性时。 有关使用 DataView 进行筛选和排序的更多信息,请参见使用 DataView 进行筛选 (LINQ to DataSet)和Sorting with DataView。 通过 LINQ to DataSet 查询创建 DataView 从数据表创建 DataView 可以通过 LINQ to DataSet 查询结果创建 DataView 对象,查询结果是 DataRow 对象的投影。 新创建的 DataView 会 从创建它的查询继承筛选和排序信息。 不支持通过返回匿名类型的查询或执行联接操作的查询创建 DataView。 在用于创建 DataView 的查询中仅支持以下查询运算符: z Cast(TResult) z OrderBy z OrderByDescending z Select``2(EnumerableRowCollection(UMP), Expression(Func(UMP, UMP))) z ThenBy z ThenByDescending z Where(TRow) 下面的示例创建按总应付金额排序的联机订单的 DataView: 也可以在从查询创建 DataView 后,使用基于字符串的 RowFilter 和 Sort 属性对其进行筛选和排序。 请注意,这将清 除继承自查询的排序和筛选信息。 下面的示例通过按以“S”开头的姓氏进行筛选的 LINQ to DataSet 查询创建 DataView。 将基于字符串的 Sort 属性设置为先按姓氏升序排序,然后按名字降序排序: 注意: 在大多数情况下,用于筛选和排序的表达式不应有副作用且必须是确定的。 另外,表达式不应包含依赖于固定执 行次数的任何逻辑,因为排序和筛选操作可能会执行任意次。 C# 复制代码 DataTable orders = dataSet.Tables["SalesOrderHeader"]; EnumerableRowCollection query = from order in orders.AsEnumerable() where order.Field("OnlineOrderFlag") == true orderby order.Field("TotalDue") select order; DataView view = query.AsDataView(); bindingSource1.DataSource = view; C# 复制代码 DataTable contacts = dataSet.Tables["Contact"]; EnumerableRowCollection query = from contact in contacts.AsEnumerable() where contact.Field("LastName").StartsWith("S") select contact; DataView view = query.AsDataView(); bindingSource1.DataSource = view; view.Sort = "LastName desc, FirstName asc"; 除了通过 LINQ to DataSet 查询创建 DataView 对象外,还可以通过使用 AsDataView 方法从 DataTable 创建该对象。 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/76057508-e12... 请参见 下面的示例从 SalesOrderDetail 表创建 DataView 并将其设置为 BindingSource 对象的数据源。 此对象充当 DataGridView 控件的代理。 在从 DataView 创建 DataTable 后,可以在其上设置筛选和排序。 下面的示例从 Contact 表创建 DataView 并将 Sort 属性设置为先按姓氏升序排序,然后按名字降序排序: 不过,在通过查询创建 DataView 之后,设置 RowFilter 或 Sort 属性会带来性能降低,因为 DataView 将会构造一个索 引来支持筛选和排序操作。 设置 RowFilter 或 Sort 属性会重新生成数据的索引,从而增加应用程序的系统开销并降低 性能。 在可能的情况下,最好在第一次创建 DataView 时指定筛选和排序信息并避免之后对其进行修改。 C# 复制代码 DataTable orders = dataSet.Tables["SalesOrderDetail"]; DataView view = orders.AsDataView(); bindingSource1.DataSource = view; dataGridView1.AutoResizeColumns(); C# 复制代码 DataTable contacts = dataSet.Tables["Contact"]; DataView view = contacts.AsDataView(); view.Sort = "LastName desc, FirstName asc"; bindingSource1.DataSource = view; dataGridView1.AutoResizeColumns(); 概念 数据绑定和 LINQ to DataSet 使用 DataView 进行筛选 (LINQ to DataSet) 使用 DataView 进行排序 (LINQ to DataSet) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/76057508-e12... 全部折叠 代码:C# .NET Framework 开发人员指南 使用 DataView 进行筛选 (LINQ to DataSet) 请参见 发送反馈意见 使用特定条件筛选数据,然后通过 UI 控件在客户端中表示该数据的能力是数据绑定的一个重要特征。DataView 提供多种方式来筛选数据并返回满足指定筛 选条件的数据行子集。 除了基于字符串的筛选功能以外,DataView 还为筛选条件提供了使用 LINQ 表达式的功能。LINQ 表达式允许执行比基于字符串的筛 选更复杂而功能更强大的筛选操作。 使用 DataView 筛选数据有两种方式: z 通过使用 Where 子句的 LINQ to DataSet 查询创建 DataView。 z 使用 DataView 现有的基于字符串的筛选功能。 通过具有筛选信息的查询创建 DataView 可以通过 LINQ to DataSet 查询创建 DataView 对象。 如果该查询包含一个 Where 子句,则会使用查询中的筛选信息创建 DataView。 Where 子句 中的表达式用于确定哪些数据行将包括在 DataView 中并作为筛选器的基础。 基于表达式的筛选器具有比基于字符串的简单筛选器更强大、更复杂的筛选功能。 基于字符串的筛选器和基于表达式的筛选器是互相排斥的。 如果 在通过查询创建 DataView 后设置基于字符串的 RowFilter,则会清除从查询推断的基于表达式的筛选器。 示例 下面的示例查询 SalesOrderDetail 表中数量大于 2 且小于 6 的订单,通过查询创建 DataView,并将 DataView 绑定到 BindingSource: 示例 下面的示例通过查询 2001 年 6 月 6 日以后达成的订单来创建 DataView: 示例 筛选也可以与排序组合使用。 下面的示例通过查询姓氏以“S”开始并按姓氏排序,然后按名字排序的联系人来创建 DataView: 示例 下面的示例使用 SoundEx 算法查找姓氏与“Zhu”相近的联系人。 SoundEx 算法在 SoundEx 方法中实现。 注意: 在大多数情况下,用于筛选的表达式不应有副作用且必须是确定的。 另外,表达式不应包含依赖于固定执行次数的任何逻辑,因为筛选操作可能 会执行任意次。 C# 复制代码 DataTable orders = dataSet.Tables["SalesOrderDetail"]; EnumerableRowCollection query = from order in orders.AsEnumerable() where order.Field("OrderQty") > 2 && order.Field("OrderQty") < 6 select order; DataView view = query.AsDataView(); bindingSource1.DataSource = view; C# 复制代码 DataTable orders = dataSet.Tables["SalesOrderHeader"]; EnumerableRowCollection query = from order in orders.AsEnumerable() where order.Field("OrderDate") > new DateTime(2002, 6, 1) select order; DataView view = query.AsDataView(); bindingSource1.DataSource = view; C# 复制代码 DataTable contacts = dataSet.Tables["Contact"]; EnumerableRowCollection query = from contact in contacts.AsEnumerable() where contact.Field("LastName").StartsWith("S") orderby contact.Field("LastName"), contact.Field("FirstName") select contact; DataView view = query.AsDataView(); bindingSource1.DataSource = view; dataGridView1.AutoResizeColumns(); C# 复制代码 DataTable contacts = dataSet.Tables["Contact"]; string soundExCode = SoundEx("Zhu"); EnumerableRowCollection query = from contact in contacts.AsEnumerable() where SoundEx(contact.Field("LastName")) == soundExCode select contact; DataView view = query.AsDataView(); bindingSource1.DataSource = view; dataGridView1.AutoResizeColumns(); 页码,1/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/5632d74a-ff53... SoundEx 是一种拼音算法,用于按英语发音来索引姓名,它最初由美国人口调查局开发。 SoundEx 方法返回一个表示姓名的四字符代码,由一个英文 字母后跟三个数字构成。 字母是姓名的首字母,数字对姓名中剩余的辅音字母编码。 发音相近的姓名具有相同的 SoundEx 代码。 上一示例的 SoundEx 方法中使用的 SoundEx 实现如下所示: C# 复制代码 static private string SoundEx(string word) { // The length of the returned code. int length = 4; // Value to return. string value = ""; // The size of the word to process. int size = word.Length; // The word must be at least two characters in length. if (size > 1) { // Convert the word to uppercase characters. word = word.ToUpper(System.Globalization.CultureInfo.InvariantCulture); // Convert the word to a character array. char[] chars = word.ToCharArray(); // Buffer to hold the character codes. StringBuilder buffer = new StringBuilder(); buffer.Length = 0; // The current and previous character codes. int prevCode = 0; int currCode = 0; // Add the first character to the buffer. buffer.Append(chars[0]); // Loop through all the characters and convert them to the proper character code. for (int i = 1; i < size; i++) { switch (chars[i]) { case 'A': case 'E': case 'I': case 'O': case 'U': case 'H': case 'W': case 'Y': currCode = 0; break; case 'B': case 'F': case 'P': case 'V': currCode = 1; break; case 'C': case 'G': case 'J': case 'K': case 'Q': case 'S': case 'X': case 'Z': currCode = 2; break; case 'D': case 'T': currCode = 3; break; case 'L': currCode = 4; break; case 'M': case 'N': currCode = 5; break; case 'R': currCode = 6; break; } // Check if the current code is the same as the previous code. if (currCode != prevCode) { // Check to see if the current code is 0 (a vowel); do not process vowels. if (currCode != 0) buffer.Append(currCode); } // Set the previous character code. prevCode = currCode; // If the buffer size meets the length limit, exit the loop. if (buffer.Length == length) break; } // Pad the buffer, if required. size = buffer.Length; if (size < length) buffer.Append('0', (length - size)); // Set the value to return. value = buffer.ToString(); } // Return the value. return value; 页码,2/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/5632d74a-ff53... 使用 RowFilter 属性 清除筛选器 请参见 } DataView 现有的基于字符串的筛选功能仍可在 LINQ to DataSet 上下文中执行。 有关基于字符串的 RowFilter 筛选的更多信息,请参见排序和筛选数 据 (ADO.NET)。 下面的示例从 Contact 表创建 DataView,然后设置 RowFilter 属性以返回联系人的姓氏为“Zhu”的行: 从 DataTable 或 LINQ to DataSet 查询创建 DataView 后,可以使用 RowFilter 属性基于行的列值指定行的子集。 基于字符串的筛选器和基于表达式 的筛选器是互相排斥的。 设置 RowFilter 属性将清除从 LINQ to DataSet 查询推断的筛选表达式,并且该筛选表达式无法重置。 如果要返回特定数据查询的结果而不是提供数据子集的动态视图,则可以使用 DataView 的 Find 或 FindRows 方法,而不设置 RowFilter 属性。 RowFilter 属性最适合用于用绑定控件显示筛选结果的数据绑定应用程序。 设置 RowFilter 属性会重新生成数据的索引,从而增加应用程序的系统开销 并降低性能。 Find 和 FindRows 方法使用当前索引,而不要求重新生成索引。 如果只想调用 Find 或 FindRows 一次,则应使用现有的 DataView。 如果想要调用 Find 或 FindRows 多次,则应该创建一个新的 DataView 以便对想要搜索的列重新生成索引,然后调用 Find 或 FindRows 方法。 有关 Find 和 FindRows 方法的更多信息,请参见查找行 (ADO.NET)和 DataView 性能。 C# 复制代码 DataTable contacts = dataSet.Tables["Contact"]; DataView view = contacts.AsDataView(); view.RowFilter = "LastName='Zhu'"; bindingSource1.DataSource = view; dataGridView1.AutoResizeColumns(); C# 复制代码 DataTable contacts = dataSet.Tables["Contact"]; EnumerableRowCollection query = from contact in contacts.AsEnumerable() where contact.Field("LastName") == "Hernandez" select contact; DataView view = query.AsDataView(); bindingSource1.DataSource = view; dataGridView1.AutoResizeColumns(); view.RowFilter = "LastName='Zhu'"; 使用 RowFilter 属性设置筛选之后,可以清除 DataView 上的筛选器。 DataView 上的筛选器可以采用两种不同的方式清除: z 将 RowFilter 属性设置为 null。 z 将 RowFilter 属性设置为一个空字符串。 示例 下面的示例通过查询创建 DataView,然后通过将 RowFilter 属性设置为 null 来清除该筛选器: 示例 下面的示例从表创建 DataView,设置 RowFilter 属性,然后通过将 RowFilter 属性设置为一个空的字符串来清除该筛选器: C# 复制代码 DataTable orders = dataSet.Tables["SalesOrderHeader"]; EnumerableRowCollection query = from order in orders.AsEnumerable() where order.Field("OrderDate") > new DateTime(2002, 11, 20) && order.Field("TotalDue") < new Decimal(60.00) select order; DataView view = query.AsDataView(); bindingSource1.DataSource = view; view.RowFilter = null; C# 复制代码 DataTable contacts = dataSet.Tables["Contact"]; DataView view = contacts.AsDataView(); view.RowFilter = "LastName='Zhu'"; bindingSource1.DataSource = view; dataGridView1.AutoResizeColumns(); // Clear the row filter. view.RowFilter = ""; 概念 数据绑定和 LINQ to DataSet 使用 DataView 进行排序 (LINQ to DataSet) 页码,3/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/5632d74a-ff53... 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,4/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/5632d74a-ff53... 全部折叠 代码:C# .NET Framework 开发人员指南 使用 DataView 进行排序 (LINQ to DataSet) 请参见 发送反馈意见 基于特定条件对数据进行排序,然后通过 UI 控件向客户端表示该数据的能力是数据绑定的一个重要特征。DataView 提供多种方式对数据进行排 序并返回按指定排序条件排序的数据行。 除了基于字符串的排序功能以外,DataView 还使您能够对排序条件使用 Language-Integrated Query (LINQ) 表达式。LINQ 表达式允许执行比基于字符串的排序更复杂而功能更强大的排序操作。 本主题介绍这两种使用 DataView 的排序方法。 通过具有排序信息的查询创建 DataView 使用基于字符串的排序属性 可以通过 LINQ to DataSet 查询创建 DataView 对象。 如果查询包含 OrderBy、OrderByDescending、ThenBy 或 ThenByDescending 子 句,则这些子句中的表达式将用作对 DataView 中的数据进行排序的基础。 例如,如果查询包含 Order By… 和 Then By… 子句,则生成 的 DataView 将按指定的两个列对数据进行排序。 基于表达式的排序具有比基于字符串的简单排序更强大、更复杂的排序功能。 请注意,基于字符串和基于表达式的排序是互相排斥的。 如 果在通过查询创建 DataView 后设置基于字符串的 Sort,则会清除从查询推断的基于表达式的筛选,并且无法重置。 创建 DataView 及修改任何排序或筛选信息时,均会生成 DataView 的索引。 通过在用于创建 DataView 的 LINQ to DataSet 查询中提供排 序条件且在以后不修改排序信息,您可以获得最佳性能。 有关更多信息,请参见 DataView 性能。 示例 下面的示例查询 SalesOrderHeader 表并按订单日期对返回的行排序,通过查询创建 DataView,并将 DataView 绑定到 BindingSource。 示例 下面的示例查询 SalesOrderHeader 表并按总应付金额对返回的行排序,通过查询创建 DataView,并将 DataView 绑定到 BindingSource。 示例 下面的示例查询 SalesOrderDetail 表并先按订单数量,然后按销售订单 ID 对返回的行排序,通过查询创建 DataView,并将 DataView 绑 定到 BindingSource。 注意: 在大多数情况下,用于排序的表达式不应有副作用且必须是确定的。 另外,表达式不应包含依赖于固定执行次数的任何逻辑,因为排序 操作可能会执行任意次。 C# 复制代码 DataTable orders = dataSet.Tables["SalesOrderHeader"]; EnumerableRowCollection query = from order in orders.AsEnumerable() orderby order.Field("OrderDate") select order; DataView view = query.AsDataView(); bindingSource1.DataSource = view; C# 复制代码 DataTable orders = dataSet.Tables["SalesOrderHeader"]; EnumerableRowCollection query = from order in orders.AsEnumerable() orderby order.Field("TotalDue") select order; DataView view = query.AsDataView(); bindingSource1.DataSource = view; C# 复制代码 DataTable orders = dataSet.Tables["SalesOrderDetail"]; EnumerableRowCollection query = from order in orders.AsEnumerable() orderby order.Field("OrderQty"), order.Field("SalesOrderID") select order; DataView view = query.AsDataView(); bindingSource1.DataSource = view; DataView 的基于字符串的排序功能对于 LINQ to DataSet 仍有效。 通过 LINQ to DataSet 查询创建 DataView 后,可以使用 Sort 属性对 DataView 设置排序。 基于字符串和基于表达式的排序功能是互相排斥的。 设置 Sort 属性将清除从创建 DataView 的查询中继承的基于表达式的排序。 有关基于字符串的 Sort 筛选的更多信息,请参见排序和筛选数据 (ADO.NET)。 示例 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/885b3b7b-51c... 清除排序 请参见 下面的示例从 Contact 表创建 DataView 并先按姓氏以降序,然后按名字以升序对行进行排序: 示例 下面的示例查询 Contact 表中以字母“S”开头的姓氏。 通过该查询创建 DataView 并将其绑定到 BindingSource 对象。 C# 复制代码 DataTable contacts = dataSet.Tables["Contact"]; DataView view = contacts.AsDataView(); view.Sort = "LastName desc, FirstName asc"; bindingSource1.DataSource = view; dataGridView1.AutoResizeColumns(); C# 复制代码 DataTable contacts = dataSet.Tables["Contact"]; EnumerableRowCollection query = from contact in contacts.AsEnumerable() where contact.Field("LastName").StartsWith("S") select contact; DataView view = query.AsDataView(); bindingSource1.DataSource = view; view.Sort = "LastName desc, FirstName asc"; 有关 DataView 的排序信息可以在使用 Sort 属性设置后清除。 清除 DataView 中的排序信息有两种方式: z 将 Sort 属性设置为 null。 z 将 Sort 属性设置为一个空字符串。 示例 下面的示例通过查询创建一个 DataView 并通过将 Sort 属性设置为空字符串清除排序。 示例 下面的示例从 Contact 表创建 DataView 并将 Sort 属性设置为按姓氏以降序排序。 然后通过将 Sort 属性设置为 null 来清除排序信息: C# 复制代码 DataTable orders = dataSet.Tables["SalesOrderHeader"]; EnumerableRowCollection query = from order in orders.AsEnumerable() orderby order.Field("TotalDue") select order; DataView view = query.AsDataView(); bindingSource1.DataSource = view; view.Sort = ""; C# 复制代码 DataTable contacts = dataSet.Tables["Contact"]; DataView view = contacts.AsDataView(); view.Sort = "LastName desc"; bindingSource1.DataSource = view; dataGridView1.AutoResizeColumns(); // Clear the sort. view.Sort = null; 概念 数据绑定和 LINQ to DataSet 使用 DataView 进行筛选 (LINQ to DataSet) Sorting Data 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/885b3b7b-51c... 全部折叠 代码:C# .NET Framework 开发人员指南 DataView 性能 请参见 发送反馈意见 本主题讨论使用 DataView 类的 Find 和 FindRows 方法并在 Web 应用程序中缓存 DataView 时所具有的性能优势。 Find 和 FindRows ASP.NET 请参见 DataView 构造一个索引。 索引包含从表或视图中的一个或多个列生成的键。 这些键存储在结构中,这种结构可使 DataView 能够快速有效地查 找行或与键值关联的行。 使用索引的操作(如筛选和排序)可显著提高性能。 创建 DataView 及修改任何排序或筛选信息时,均会生成 DataView 的索引。 创建 DataView 然后设置排序或筛选信息会使索引生成至少两次: 一次是在创建 DataView 时,另一次是在修改任何排序或 筛选属性时。 有关使用 DataView 进行筛选和排序的更多信息,请参见使用 DataView 进行筛选 (LINQ to DataSet)和使用 DataView 进行排序 (LINQ to DataSet)。 如果要返回特定数据查询的结果而不是提供数据子集的动态视图,则可以使用 DataView 的 Find 或 FindRows 方法,而不设置 RowFilter 属性。 RowFilter 属性最适合用于用绑定控件显示筛选结果的数据绑定应用程序。 设置 RowFilter 属性会重新生成数据的索引,从而增加应用程序的系统 开销并降低性能。 Find 和 FindRows 方法使用当前索引,而不要求重新生成索引。 如果只想调用 Find 或 FindRows 一次,则应使用现有的 DataView。 如果想要调用 Find 或 FindRows 多次,则应该创建一个新的 DataView 以便对想要搜索的列重新生成索引,然后调用 Find 或 FindRows 方法。 有关 Find 和 FindRows 方法的更多信息,请参见查找行 (ADO.NET)。 下面的示例使用 Find 方法来查找姓氏为“Zhu”的联系人。 下面的示例使用 FindRows 方法来查找所有红颜色的产品。 C# 复制代码 DataTable contacts = dataSet.Tables["Contact"]; EnumerableRowCollection query = from contact in contacts.AsEnumerable() orderby contact.Field("LastName") select contact; DataView view = query.AsDataView(); // Find a contact with the last name of Zhu. int found = view.Find("Zhu"); C# 复制代码 DataTable products = dataSet.Tables["Product"]; EnumerableRowCollection query = from product in products.AsEnumerable() orderby product.Field("ListPrice"), product.Field("Color") select product; DataView view = query.AsDataView(); view.Sort = "Color"; object[] criteria = new object[] { "Red"}; DataRowView[] foundRowsView = view.FindRows(criteria); ASP.NET 具有一种缓存机制,允许您在内存中存储需要创建大量服务器资源的对象。 缓存这些类型的资源可以显著提高应用程序的性能。 缓存 由 Cache 类实现,缓存实例专用于每个应用程序。 由于创建新的 DataView 对象需要大量资源,因此您可能希望在 Web 应用程序中使用此缓存 功能,使得每次刷新网页时,不必重新生成 DataView。 在下面的示例中,对 DataView 进行缓存以便在刷新该页时不必对数据重新排序。 C# 复制代码 if (Cache["ordersView"] == null) { // Fill the DataSet. DataSet dataSet = FillDataSet(); DataTable orders = dataSet.Tables["SalesOrderHeader"]; EnumerableRowCollection query = from order in orders.AsEnumerable() where order.Field("OnlineOrderFlag") == true orderby order.Field("TotalDue") select order; DataView view = query.AsDataView(); Cache.Insert("ordersView", view); } DataView ordersView = (DataView)Cache["ordersView"]; GridView1.DataSource = ordersView; GridView1.DataBind(); 概念 数据绑定和 LINQ to DataSet 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/90820e49-9d4... 全部折叠 代码:C# .NET Framework 开发人员指南 如何: 将 DataView 对象绑定到 Windows 窗体 DataGridView 控件 请参见 发送反馈意见 DataGridView 控件提供一种以表格格式显示数据的功能强大且灵活的方法。 DataGridView 控件支持标准 Windows 窗体数据绑定模型,因此它可以绑定到 DataView 和各种其他数据源。 但在多数情况下,该控件将会绑定到用于管理数据源交互详细信息的 BindingSource 组件。 有关 DataGridView 控件的更多信息,请参见 DataGridView Control Overview (Windows Forms)。 将 DataGridView 控件连接到 DataView 1. 实现方法以处理有关从数据库检索数据的详细信息。 下面的代码示例实现 GetData 方法,该方法初始化 SqlDataAdapter 组件并使用该组件来填充 DataSet。 请确保将 connectionString 变量设置为适合数据库的值。 您需要访问安装有 AdventureWorks SQL Server 示例数据库的服务器。 2. 在窗体的 Load 事件处理程序中,将 DataGridView 控件绑定到 BindingSource 组件并调用 GetData 方法,以从数据库检索数据。 DataView 通过对 Contact DataTable 的 LINQ to DataSet 查询创建,然后绑定到 BindingSource 组件。 请参见 C# 复制代码 private void GetData() { try { // Initialize the DataSet. dataSet = new DataSet(); dataSet.Locale = CultureInfo.InvariantCulture; // Create the connection string for the AdventureWorks sample database. string connectionString = "Data Source=localhost;Initial Catalog=AdventureWorks;" + "Integrated Security=true;"; // Create the command strings for querying the Contact table. string contactSelectCommand = "SELECT ContactID, Title, FirstName, LastName, EmailAddress, Phone FROM Person.Contact"; // Create the contacts data adapter. contactsDataAdapter = new SqlDataAdapter( contactSelectCommand, connectionString); // Create a command builder to generate SQL update, insert, and // delete commands based on the contacts select command. These are used to // update the database. SqlCommandBuilder contactsCommandBuilder = new SqlCommandBuilder(contactsDataAdapter); // Fill the data set with the contact information. contactsDataAdapter.Fill(dataSet, "Contact"); } catch (SqlException ex) { MessageBox.Show(ex.Message); } } C# 复制代码 private void Form1_Load(object sender, EventArgs e) { // Connect to the database and fill the DataSet. GetData(); contactDataGridView.DataSource = contactBindingSource; // Create a LinqDataView from a LINQ to DataSet query and bind it // to the Windows forms control. EnumerableRowCollection contactQuery = from row in dataSet.Tables["Contact"].AsEnumerable() where row.Field("EmailAddress") != null orderby row.Field("LastName") select row; contactView = contactQuery.AsDataView(); // Bind the DataGridView to the BindingSource. contactBindingSource.DataSource = contactView; contactDataGridView.AutoResizeColumns(); } 概念 数据绑定和 LINQ to DataSet 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/2b73d60a-604... 全部折叠 代码:C# .NET Framework 开发人员指南 调试 LINQ to DataSet 查询 请参见 发送反馈意见 Visual Studio 2008 支持 LINQ to DataSet 代码的调试。 但是,在调试 LINQ to DataSet 代码和非 LINQ to DataSet 托管代码之间存在一些差异。大多数调试功能使用 LINQ to DataSet 语句,包括单步执行、设置断点以及查看调试 程序窗口中显示的结果。 而且,延迟查询执行有些在调试 LINQ to DataSet 代码时应考虑的副作用,并且在使用 “编辑并继续”方面有些限制。 本主题讨论调试方面的问题,该调试对于与非 LINQ to DataSet 托管代码对比的 LINQ to DataSet 来说是唯一的。 查看结果 编辑并继续 请参见 您可以使用数据提示、“监视”窗口和“快速监视”对话框来查看 LINQ to DataSet 语句的结果。 使用源窗 口,可以将指针暂时停留在源窗口中的查询上,这样数据提示就会出现。 可以复制 LINQ to DataSet 变量, 然后将其粘贴到“监视”窗口或“快速监视”对话框。 在 LINQ to DataSet 中,创建或声明查询时并不计算 查询,而只在执行查询时才计算。 这称为延迟执行。 因此,查询变量直到计算时才有值。 有关更多信息, 请参见LINQ to DataSet 中的查询。 调试程序必须计算查询才能显示查询结果。 当您在调试程序中查看 LINQ to DataSet 查询结果时,将进行此 隐式计算,应考虑到这存在一些副作用。 每个查询计算都会占用时间。 展开结果节点也会占用时间。 对于 某些查询,重复计算可能引起明显的性能损失。 计算查询也会有副作用,这些副作用会更改数据值或程序的 状态。 并非所有查询都有副作用。 要确定查询能否安全计算而没有副作用,必须了解实现查询的代码。 有 关更多信息,请参见Side Effects and Expressions。 “编辑并继续”不支持 LINQ to DataSet 查询的更改。 如果在调试会话过程中,添加、移除或更改 LINQ to DataSet 语句,则会显示一个对话框,告诉您“编辑并继续”不支持该更改。 此时,可以取消更改,或停止 调试会话然后使用编辑过的代码重新启动新会话。 此外,“编辑并继续”不支持更改 LINQ to DataSet 语句中使用的变量的类型或值。 同样,您可取消更改, 或停止调试会话然后重新启动调试会话。 在 Visual C# 2008 中,无法在包含 LINQ to DataSet 查询的方法中的任何代码上使用“编辑并继续”。 在 Visual Basic 2008 中,可以对非 LINQ to DataSet 代码使用“编辑并继续”(即使在包含 LINQ to DataSet 查询的方法中也同样可以)。 可以在 LINQ to DataSet 语句之前添加或移除代码,即使这些更改会影响 LINQ to DataSet 查询的行号。 非 LINQ to DataSet 代码的 Visual Basic 调试过程仍与引入 LINQ to DataSet 之前的 过程相同。 但是,您无法更改、添加或移除 LINQ to DataSet 查询,除非停止调试才能应用这些更改。 概念 Debugging Managed Code 编程指南 (LINQ to DataSet) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/f4c54015-8ce2... 全部折叠 代码:C# .NET Framework 开发人员指南 安全性 (LINQ to DataSet) 请参见 发送反馈意见 本主题讨论 LINQ to DataSet 中的安全性问题。 将查询传递到不受信任的组件 外部输入 请参见 LINQ to DataSet 查询可在程序中的一点表述,而在另一点执行。 在表述查询的点,查询可引用在该点可见的 任何元素,例如调用方法所属的类的私有成员,或者表示局部变量/参数的符号。 在执行时,查询能有效访问 那些查询在表述中引用的成员,即使其中调用代码没有可见性。 执行查询的代码没有任意添加的可见性,因 为它无法选择访问内容。 它能严格访问查询所访问的内容,并且仅通过查询本身访问。 这意味着将查询的引用传递到其他代码段即表示信任接收该查询的组件,组件可访问查询引用的所有公共成员 和私有成员。 通常情况下,不应将 LINQ to DataSet 查询传递到不受信任的组件,除非查询已经过仔细构 造,使它不能公开应保留为私有的信息。 应用程序常常采用外部输入(来自用户或其他外部代理),并根据该输入执行操作。 在使用 LINQ to DataSet 的情况下,应用程序可能根据外部输入或使用查询中的外部输入以特定方式构造查询。LINQ to DataSet 查询 在接受文本的任何位置都接受参数。 应用程序开发人员应使用参数化查询,而不是将来自外部代理的文本直 接注入查询。 任何直接或间接从用户或外部代理派生的输入都可能包含利用目标语言的语法来执行未授权操作的内容。 这 称为 SQL 注入式攻击,是以目标语言为 Transact-SQL 的攻击模式命名的。 恶意用户利用这种直接注入到查 询的用户输入删除数据库表、产生拒绝服务或者更改所执行操作的性质。 尽管在 LINQ to DataSet 可以撰写 查询,但是要通过对象模型 API 执行。LINQ to DataSet 查询不像 Transact-SQL 那样使用字符串操作或串联 来撰写,所以从传统意义上讲不易受 SQL 注入式攻击影响。 概念 编程指南 (LINQ to DataSet) 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/6116b2b8-75f4... 全部折叠 代码:C# .NET Framework 开发人员指南 LINQ to DataSet 示例 请参见 发送反馈意见 本节提供使用标准查询运算符的 LINQ to DataSet 编程示例。 这些示例中所使用的 DataSet 已通过使用 向数据集 中加载数据 中指定的 FillDataSet 方法进行填充。 有关更多信息,请参见Standard Query Operators Overview。 本节内容 请参见 查询表达式语法示例 (LINQ to DataSet) 包含下面的示例: z 查询表达式语法示例: 投影 (LINQ to DataSet) z 查询表达式语法示例: 限制 (LINQ to DataSet) z 查询表达式语法示例: 分区 (LINQ to DataSet) z 查询表达式语法示例: 排序 (LINQ to DataSet) z 查询表达式语法示例: 元素运算符 (LINQ to DataSet) z 查询表达式语法示例: 聚合运算符 (LINQ to DataSet) z 查询表达式语法示例: 联接运算符 (LINQ to DataSet) 基于方法的查询语法示例 (LINQ to DataSet) 包含下面的示例: z 基于方法的查询语法示例: 投影 (LINQ to DataSet) z 基于方法的查询语法示例: 分区 (LINQ to DataSet) z 基于方法的查询语法示例: 排序 (LINQ to DataSet) z 基于方法的查询语法示例: 集合运算符 (LINQ to DataSet) z 基于方法的查询语法示例: 转换 (LINQ to DataSet) z 基于方法的查询语法示例: 元素运算符 (LINQ to DataSet) z 基于方法的查询语法示例: 聚合运算符 (LINQ to DataSet) z 基于方法的查询语法示例: 联接 (LINQ to DataSet) 数据集特定的运算符示例 (LINQ to DataSet) 包含演示如何使用 CopyToDataTable 方法和 DataRowComparer 类的示例。 概念 编程指南 (LINQ to DataSet) 向数据集中加载数据 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/dfd91658-8d8a... 全部折叠 代码:C# .NET Framework 开发人员指南 查询表达式语法示例 (LINQ to DataSet) 请参见 发送反馈意见 本节提供使用标准查询运算符的查询表达式语法的 LINQ to DataSet 编程示例。 这些示例中所使用的 DataSet 已通 过使用 向数据集中加载数据 中指定的 FillDataSet 方法进行填充。 有关更多信息,请参见Standard Query Operators Overview。 本节内容 请参见 查询表达式语法示例: 投影 (LINQ to DataSet) 本主题中的示例演示如何使用 Select 和 SelectMany 方法来查询 DataSet。 查询表达式语法示例: 限制 (LINQ to DataSet) 本主题中的示例演示如何使用 Where 方法来查询 DataSet。 查询表达式语法示例: 分区 (LINQ to DataSet) 本主题中的示例演示如何使用 Skip(TSource) 和 Take(TSource) 方法来查询 DataSet 并分隔结果。 查询表达式语法示例: 排序 (LINQ to DataSet) 本主题中的示例演示如何使用 OrderBy、OrderByDescending、Reverse(TSource) 和 ThenByDescending 方法 来查询 DataSet 并排序结果。 查询表达式语法示例: 元素运算符 (LINQ to DataSet) 本主题中的示例演示如何使用 First 和 ElementAt(TSource) 方法来获取 DataSet 中的 DataRow 元素。 查询表达式语法示例: 聚合运算符 (LINQ to DataSet) 本主题中的示例演示如何使用 Average、Count、Max、Min 和 Sum 方法来查询 DataSet 并聚合数据。 查询表达式语法示例: 联接运算符 (LINQ to DataSet) 本主题中的示例演示如何使用 GroupJoin 和 Join 方法来查询 DataSet。 概念 基于方法的查询语法示例 (LINQ to DataSet) 数据集特定的运算符示例 (LINQ to DataSet) LINQ to DataSet 示例 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/f743fbc7-faff-... 全部折叠 代码:C# .NET Framework 开发人员指南 查询表达式语法示例: 投影 (LINQ to DataSet) 请参见 发送反馈意见 本主题中的示例演示如何使用 Select 和 SelectMany 方法以便使用查询表达式语法来查询 DataSet。 这些示例中所使用的 FillDataSet 方法已在 向数据集中加载数据 中指定。 本主题中的示例使用 AdventureWorks 示例数据库中的 Contact、Address、Product、SalesOrderHeader 和 SalesOrderDetail 表。 本主题中的示例使用下面的 using/Imports 语句: 有关更多信息,请参见如何: 在 Visual Studio 中创建 LINQ to DataSet 项目。 Select C# 复制代码 using System; using System.Linq; using System.Linq.Expressions; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Data.Common; using System.Globalization; 示例 此示例使用 Select 方法返回 Product 表中的所有行并显示产品名称。 示例 此示例使用 Select 仅返回一系列产品名称。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable products = ds.Tables["Product"]; IEnumerable query = from product in products.AsEnumerable() select product; Console.WriteLine("Product Names:"); foreach (DataRow p in query) { Console.WriteLine(p.Field("Name")); } C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable products = ds.Tables["Product"]; IEnumerable query = from product in products.AsEnumerable() select product.Field("Name"); Console.WriteLine("Product Names:"); foreach (string productName in query) { Console.WriteLine(productName); } 页码,1/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/48c9f5ed-76bf... SelectMany 示例 此示例使用 From …, …(等效于 SelectMany 方法)来选择 TotalDue 小于 500.00 的所有订单。 示例 此示例使用 From …, …(等效于 SelectMany 方法)来选择于 2002 年 10 月 1 日或之后达成的所有订单。 示例 此示例使用 From …, …(等效于 SelectMany 方法)来选择订单总额大于 10000.00 的所有订单,并使用 From 赋值以避免请求二次总额。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable contacts = ds.Tables["Contact"]; DataTable orders = ds.Tables["SalesOrderHeader"]; var query = from contact in contacts.AsEnumerable() from order in orders.AsEnumerable() where contact.Field("ContactID") == order.Field("ContactID") && order.Field("TotalDue") < 500.00M select new { ContactID = contact.Field("ContactID"), LastName = contact.Field("LastName"), FirstName = contact.Field("FirstName"), OrderID = order.Field("SalesOrderID"), Total = order.Field("TotalDue") }; foreach (var smallOrder in query) { Console.WriteLine("Contact ID: {0} Name: {1}, {2} Order ID: {3} Total Due: ${4} ", smallOrder.ContactID, smallOrder.LastName, smallOrder.FirstName, smallOrder.OrderID, smallOrder.Total); } C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable contacts = ds.Tables["Contact"]; DataTable orders = ds.Tables["SalesOrderHeader"]; var query = from contact in contacts.AsEnumerable() from order in orders.AsEnumerable() where contact.Field("ContactID") == order.Field("ContactID") && order.Field("OrderDate") >= new DateTime(2002, 10, 1) select new { ContactID = contact.Field("ContactID"), LastName = contact.Field("LastName"), FirstName = contact.Field("FirstName"), OrderID = order.Field("SalesOrderID"), OrderDate = order.Field("OrderDate") }; foreach (var order in query) { Console.WriteLine("Contact ID: {0} Name: {1}, {2} Order ID: {3} Order date: {4:d} ", order.ContactID, order.LastName, order.FirstName, order.OrderID, order.OrderDate); } 页码,2/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/48c9f5ed-76bf... 请参见 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable contacts = ds.Tables["Contact"]; DataTable orders = ds.Tables["SalesOrderHeader"]; var query = from contact in contacts.AsEnumerable() from order in orders.AsEnumerable() let total = order.Field("TotalDue") where contact.Field("ContactID") == order.Field("ContactID") && total >= 10000.0M select new { ContactID = contact.Field("ContactID"), LastName = contact.Field("LastName"), OrderID = order.Field("SalesOrderID"), total }; foreach (var order in query) { Console.WriteLine("Contact ID: {0} Last name: {1} Order ID: {2} Total: {3}", order.ContactID, order.LastName, order.OrderID, order.total); } 概念 向数据集中加载数据 LINQ to DataSet 示例 Standard Query Operators Overview 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/48c9f5ed-76bf... 全部折叠 代码:C# .NET Framework 开发人员指南 查询表达式语法示例: 限制 (LINQ to DataSet) 请参见 发送反馈意见 本主题中的示例演示如何使用 Where 方法以便使用查询表达式语法来查询 DataSet。 这些示例中所使用的 FillDataSet 方法已在 向数据集中加载数据 中指定。 本主题中的示例使用 AdventureWorks 示例数据库中的 Contact、Address、Product、SalesOrderHeader 和 SalesOrderDetail 表。 本主题中的示例使用下面的 using/Imports 语句: 有关更多信息,请参见如何: 在 Visual Studio 中创建 LINQ to DataSet 项目。 Where C# 复制代码 using System; using System.Linq; using System.Linq.Expressions; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Data.Common; using System.Globalization; 示例 此示例返回所有联机订单。 示例 此示例返回订单数量大于 2 且小于 6 的订单。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderHeader"]; var query = from order in orders.AsEnumerable() where order.Field("OnlineOrderFlag") == true select new { SalesOrderID = order.Field("SalesOrderID"), OrderDate = order.Field("OrderDate"), SalesOrderNumber = order.Field("SalesOrderNumber") }; foreach (var onlineOrder in query) { Console.WriteLine("Order ID: {0} Order date: {1:d} Order number: {2}", onlineOrder.SalesOrderID, onlineOrder.OrderDate, onlineOrder.SalesOrderNumber); } C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderDetail"]; var query = from order in orders.AsEnumerable() where order.Field("OrderQty") > 2 && 页码,1/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/1daf42c2-c9f4-... 示例 此示例返回所有颜色为红色的产品。 示例 此示例使用 Where 方法查找 2002 年 12 月 1 日之后达成的订单,然后使用 GetChildRows 方法获取每个订 单的详细信息。 order.Field("OrderQty") < 6 select new { SalesOrderID = (int)order.Field("SalesOrderID"), OrderQty = order.Field("OrderQty") }; foreach (var order in query) { Console.WriteLine("Order ID: {0} Order quantity: {1}", order.SalesOrderID, order.OrderQty); } C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable products = ds.Tables["Product"]; var query = from product in products.AsEnumerable() where product.Field("Color") == "Red" select new { Name = product.Field("Name"), ProductNumber = product.Field("ProductNumber"), ListPrice = product.Field("ListPrice") }; foreach (var product in query) { Console.WriteLine("Name: {0}", product.Name); Console.WriteLine("Product number: {0}", product.ProductNumber); Console.WriteLine("List price: ${0}", product.ListPrice); Console.WriteLine(""); } C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderHeader"]; IEnumerable query = from order in orders.AsEnumerable() where order.Field("OrderDate") >= new DateTime(2002, 12, 1) select order; Console.WriteLine("Orders that were made after 12/1/2002:"); foreach (DataRow order in query) { Console.WriteLine("OrderID {0} Order date: {1:d} ", order.Field("SalesOrderID"), order.Field("OrderDate")); foreach (DataRow orderDetail in order.GetChildRows("SalesOrderHeaderDetail")) { Console.WriteLine(" Product ID: {0} Unit Price {1}", orderDetail["ProductID"], orderDetail["UnitPrice"]); } } 页码,2/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/1daf42c2-c9f4-... 请参见 概念 向数据集中加载数据 LINQ to DataSet 示例 Standard Query Operators Overview 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/1daf42c2-c9f4-... 全部折叠 代码:C# .NET Framework 开发人员指南 查询表达式语法示例: 分区 (LINQ to DataSet) 请参见 发送反馈意见 本主题中的示例演示如何使用 Skip(TSource) 和 Take(TSource) 方法以便使用查询表达式语法来查询 DataSet。 这些示例中所使用的 FillDataSet 方法已在 向数据集中加载数据 中指定。 本主题中的示例使用 AdventureWorks 示例数据库中的 Contact、Address、Product、SalesOrderHeader 和 SalesOrderDetail 表。 本主题中的示例使用下面的 using/Imports 语句: 有关更多信息,请参见如何: 在 Visual Studio 中创建 LINQ to DataSet 项目。 Skip Take C# 复制代码 using System; using System.Linq; using System.Linq.Expressions; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Data.Common; using System.Globalization; 示例 此示例使用 Skip(TSource) 方法获取 Seattle 中除前两个地址以外的所有地址。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable addresses = ds.Tables["Address"]; DataTable orders = ds.Tables["SalesOrderHeader"]; var query = ( from address in addresses.AsEnumerable() from order in orders.AsEnumerable() where address.Field("AddressID") == order.Field("BillToAddressID") && address.Field("City") == "Seattle" select new { City = address.Field("City"), OrderID = order.Field("SalesOrderID"), OrderDate = order.Field("OrderDate") }).Skip(2); Console.WriteLine("All but first 2 orders in Seattle:"); foreach (var order in query) { Console.WriteLine("City: {0} Order ID: {1} Total Due: {2:d}", order.City, order.OrderID, order.OrderDate); } 示例 此示例使用 Take(TSource) 方法获取 Seattle 中的前三个地址。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable addresses = ds.Tables["Address"]; 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/beb5f361-1ac8... 请参见 DataTable orders = ds.Tables["SalesOrderHeader"]; var query = ( from address in addresses.AsEnumerable() from order in orders.AsEnumerable() where address.Field("AddressID") == order.Field("BillToAddressID") && address.Field("City") == "Seattle" select new { City = address.Field("City"), OrderID = order.Field("SalesOrderID"), OrderDate = order.Field("OrderDate") }).Take(3); Console.WriteLine("First 3 orders in Seattle:"); foreach (var order in query) { Console.WriteLine("City: {0} Order ID: {1} Total Due: {2:d}", order.City, order.OrderID, order.OrderDate); } 概念 向数据集中加载数据 LINQ to DataSet 示例 Standard Query Operators Overview 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/beb5f361-1ac8... 全部折叠 代码:C# .NET Framework 开发人员指南 查询表达式语法示例: 排序 (LINQ to DataSet) 请参见 发送反馈意见 本主题中的示例演示如何使用 OrderBy、OrderByDescending、Reverse(TSource) 和 ThenByDescending 方法来查询 DataSet 并使用查询表达式语法对结果排序。 这些示例中所使用的 FillDataSet 方法已在 向数据集中加载数据 中指定。 本主题中的示例使用 AdventureWorks 示例数据库中的 Contact、Address、Product、SalesOrderHeader 和 SalesOrderDetail 表。 本主题中的示例使用下面的 using/Imports 语句: 有关更多信息,请参见如何: 在 Visual Studio 中创建 LINQ to DataSet 项目。 OrderBy C# 复制代码 using System; using System.Linq; using System.Linq.Expressions; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Data.Common; using System.Globalization; 示例 此示例使用 OrderBy 返回按姓氏排序的联系人列表。 示例 此示例使用 OrderBy 按姓氏的长度对联系人列表排序。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable contacts = ds.Tables["Contact"]; IEnumerable query = from contact in contacts.AsEnumerable() orderby contact.Field("LastName") select contact; Console.WriteLine("The sorted list of last names:"); foreach (DataRow contact in query) { Console.WriteLine(contact.Field("LastName")); } C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable contacts = ds.Tables["Contact"]; IEnumerable query = from contact in contacts.AsEnumerable() orderby contact.Field("LastName").Length select contact; Console.WriteLine("The sorted list of last names (by length):"); foreach (DataRow contact in query) { Console.WriteLine(contact.Field("LastName")); 页码,1/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/653a4a97-1e4a... OrderByDescending Reverse ThenByDescending } 示例 此示例使用等效于 OrderByDescending 方法的 orderby… descending (Order By … Descending) 从高到低 排序价格列表。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable products = ds.Tables["Product"]; IEnumerable query = from product in products.AsEnumerable() orderby product.Field("ListPrice") descending select product.Field("ListPrice"); Console.WriteLine("The list price from highest to lowest:"); foreach (Decimal product in query) { Console.WriteLine(product); } 示例 此示例使用 Reverse(TSource) 创建 OrderDate 早于 2002 年 2 月 20 日的订单的列表。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderHeader"]; IEnumerable query = ( from order in orders.AsEnumerable() where order.Field("OrderDate") < new DateTime(2002, 02, 20) select order).Reverse(); Console.WriteLine("A backwards list of orders where OrderDate < Feb 20, 2002"); foreach (DataRow order in query) { Console.WriteLine(order.Field("OrderDate")); } 示例 此示例使用等效于 ThenByDescending 方法的 OrderBy… Descending 先按名称然后按定价从高到低对产品 列表排序。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable products = ds.Tables["Product"]; IEnumerable query = from product in products.AsEnumerable() orderby product.Field("Name"), product.Field("ListPrice") descending select product; 页码,2/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/653a4a97-1e4a... 请参见 foreach (DataRow product in query) { Console.WriteLine("Product ID: {0} Product Name: {1} List Price {2}", product.Field("ProductID"), product.Field("Name"), product.Field("ListPrice")); } 概念 向数据集中加载数据 LINQ to DataSet 示例 Standard Query Operators Overview 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/653a4a97-1e4a... 全部折叠 代码:C# .NET Framework 开发人员指南 查询表达式语法示例: 元素运算符 (LINQ to DataSet) 请参见 发送反馈意见 本主题中的示例演示如何使用 First 和 ElementAt(TSource) 方法以便使用查询表达式语法从 DataSet 中获取 DataRow 元素。 这些示例中所使用的 FillDataSet 方法已在 向数据集中加载数据 中指定。 本主题中的示例使用 AdventureWorks 示例数据库中的 Contact、Address、Product、SalesOrderHeader 和 SalesOrderDetail 表。 本主题中的示例使用下面的 using/Imports 语句: 有关更多信息,请参见如何: 在 Visual Studio 中创建 LINQ to DataSet 项目。 ElementAt First C# 复制代码 using System; using System.Linq; using System.Linq.Expressions; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Data.Common; using System.Globalization; 示例 此示例使用 ElementAt(TSource) 方法检索 PostalCode == "M4B 1V7" 的第五个地址。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable addresses = ds.Tables["Address"]; var fifthAddress = ( from address in addresses.AsEnumerable() where address.Field("PostalCode") == "M4B 1V7" select address.Field("AddressLine1")) .ElementAt(5); Console.WriteLine("Fifth address where PostalCode = 'M4B 1V7': {0}", fifthAddress); 示例 此示例使用 First 方法返回名字为“Brooke”的第一个联系人。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable contacts = ds.Tables["Contact"]; DataRow query = ( from contact in contacts.AsEnumerable() where (string)contact["FirstName"] == "Brooke" select contact) .First(); Console.WriteLine("ContactID: " + query.Field("ContactID")); Console.WriteLine("FirstName: " + query.Field("FirstName")); Console.WriteLine("LastName: " + query.Field("LastName")); 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/ca392dda-16e3... 请参见 概念 向数据集中加载数据 LINQ to DataSet 示例 Standard Query Operators Overview 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/ca392dda-16e3... 全部折叠 代码:C# .NET Framework 开发人员指南 查询表达式语法示例: 聚合运算符 (LINQ to DataSet) 请参见 发送反馈意见 本主题中的示例演示如何使用 Average、Count、Max、Min 和 Sum 方法来查询 DataSet 并使用查询表达式语法聚 合数据。 这些示例中所使用的 FillDataSet 方法已在 向数据集中加载数据 中指定。 本主题中的示例使用 AdventureWorks 示例数据库中的 Contact、Address、Product、SalesOrderHeader 和 SalesOrderDetail 表。 本主题中的示例使用下面的 using/Imports 语句: 有关更多信息,请参见如何: 在 Visual Studio 中创建 LINQ to DataSet 项目。 Average C# 复制代码 using System; using System.Linq; using System.Linq.Expressions; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Data.Common; using System.Globalization; 示例 此示例使用 Average 方法来计算每种款式的产品的平均定价。 示例 此示例使用 Average 获取每个联系人 ID 的总平均应付金额。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); var products = ds.Tables["Product"].AsEnumerable(); var query = from product in products group product by product.Field("Style") into g select new { Style = g.Key, AverageListPrice = g.Average(product => product.Field("ListPrice")) }; foreach (var product in query) { Console.WriteLine("Product style: {0} Average list price: {1}", product.Style, product.AverageListPrice); } C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderHeader"]; var query = from order in orders.AsEnumerable() group order by order.Field("ContactID") into g select new { 页码,1/5(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/85dafa07-e102... Count 示例 此示例使用 Average 获取每个联系人的具有平均 TotalDue 的订单。 Category = g.Key, averageTotalDue = g.Average(order => order.Field("TotalDue")) }; foreach (var order in query) { Console.WriteLine("ContactID = {0} \t Average TotalDue = {1}", order.Category, order.averageTotalDue); } C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderHeader"]; var query = from order in orders.AsEnumerable() group order by order.Field("ContactID") into g let averageTotalDue = g.Average(order => order.Field("TotalDue")) select new { Category = g.Key, CheapestProducts = g.Where(order => order.Field("TotalDue") == averageTotalDue) }; foreach (var orderGroup in query) { Console.WriteLine("ContactID: {0}", orderGroup.Category); foreach (var order in orderGroup.CheapestProducts) { Console.WriteLine("Average total due for SalesOrderID {1} is: {0}", order.Field("TotalDue"), order.Field("SalesOrderID")); } Console.WriteLine(""); } 示例 此示例使用 Count 返回联系人 ID 的列表及每个联系人 ID 的订单数。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable contacts = ds.Tables["Contact"]; var query = from contact in contacts.AsEnumerable() select new { CustomerID = contact.Field("ContactID"), OrderCount = contact.GetChildRows("SalesOrderContact").Count() }; foreach (var contact in query) { Console.WriteLine("CustomerID = {0} \t OrderCount = {1}", contact.CustomerID, contact.OrderCount); 页码,2/5(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/85dafa07-e102... Max 示例 此示例按颜色对产品分组并使用 Count 返回每个颜色组中的产品数目。 } C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable products = ds.Tables["Product"]; var query = from product in products.AsEnumerable() group product by product.Field("Color") into g select new { Color = g.Key, ProductCount = g.Count() }; foreach (var product in query) { Console.WriteLine("Color = {0} \t ProductCount = {1}", product.Color, product.ProductCount); } 示例 此示例使用 Max 方法获取每个联系人 ID 的最大总应付金额。 示例 此示例使用 Max 方法获取每个联系人 ID 的具有最大 TotalDue 的订单。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderHeader"]; var query = from order in orders.AsEnumerable() group order by order.Field("ContactID") into g select new { Category = g.Key, maxTotalDue = g.Max(order => order.Field("TotalDue")) }; foreach (var order in query) { Console.WriteLine("ContactID = {0} \t Maximum TotalDue = {1}", order.Category, order.maxTotalDue); } C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderHeader"]; var query = from order in orders.AsEnumerable() group order by order.Field("ContactID") into g let maxTotalDue = g.Max(order => order.Field("TotalDue")) select new 页码,3/5(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/85dafa07-e102... Min { Category = g.Key, CheapestProducts = g.Where(order => order.Field("TotalDue") == maxTotalDue) }; foreach (var orderGroup in query) { Console.WriteLine("ContactID: {0}", orderGroup.Category); foreach (var order in orderGroup.CheapestProducts) { Console.WriteLine("MaxTotalDue {0} for SalesOrderID {1}: ", order.Field("TotalDue"), order.Field("SalesOrderID")); } } 示例 此示例使用 Min 方法获取每个联系人 ID 的最小总应付金额。 示例 此示例使用 Min 方法获取每个联系人的具有最小总应付金额的订单。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderHeader"]; var query = from order in orders.AsEnumerable() group order by order.Field("ContactID") into g select new { Category = g.Key, smallestTotalDue = g.Min(order => order.Field("TotalDue")) }; foreach (var order in query) { Console.WriteLine("ContactID = {0} \t Minimum TotalDue = {1}", order.Category, order.smallestTotalDue); } C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderHeader"]; var query = from order in orders.AsEnumerable() group order by order.Field("ContactID") into g let minTotalDue = g.Min(order => order.Field("TotalDue")) select new { Category = g.Key, smallestTotalDue = g.Where(order => order.Field("TotalDue") == minTotalDue) }; foreach (var orderGroup in query) { 页码,4/5(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/85dafa07-e102... Sum 请参见 Console.WriteLine("ContactID: {0}", orderGroup.Category); foreach (var order in orderGroup.smallestTotalDue) { Console.WriteLine("Mininum TotalDue {0} for SalesOrderID {1}: ", order.Field("TotalDue"), order.Field("SalesOrderID")); } Console.WriteLine(""); } 示例 此示例使用 Sum 方法获取每个联系人 ID 的总应付金额。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderHeader"]; var query = from order in orders.AsEnumerable() group order by order.Field("ContactID") into g select new { Category = g.Key, TotalDue = g.Sum(order => order.Field("TotalDue")), }; foreach (var order in query) { Console.WriteLine("ContactID = {0} \t TotalDue sum = {1}", order.Category, order.TotalDue); } 概念 向数据集中加载数据 LINQ to DataSet 示例 Standard Query Operators Overview 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,5/5(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/85dafa07-e102... 全部折叠 代码:C# .NET Framework 开发人员指南 查询表达式语法示例: 联接运算符 (LINQ to DataSet) 请参见 发送反馈意见 联接是面向相互之间具有可导航关系的数据源(如关系数据库表)的查询中的一项重要操作。 联接两个数据源就是 将一个数据源中的对象与另一个数据源中具有相同公共属性的对象相关联。 有关更多信息,请参见Standard Query Operators Overview。 本主题中的示例演示如何使用 GroupJoin 和 Join 方法以便使用查询表达式语法来查询 DataSet。 这些示例中所使用的 FillDataSet 方法已在 向数据集中加载数据 中指定。 本主题中的示例使用 AdventureWorks 示例数据库中的 Contact、Address、Product、SalesOrderHeader 和 SalesOrderDetail 表。 本主题中的示例使用下面的 using/Imports 语句: 有关更多信息,请参见如何: 在 Visual Studio 中创建 LINQ to DataSet 项目。 GroupJoin C# 复制代码 using System; using System.Linq; using System.Linq.Expressions; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Data.Common; using System.Globalization; 示例 此示例对 SalesOrderHeader 和 GroupJoin 表执行 SalesOrderDetail 以得出每个客户的订单数。 组联接等效 于左外部联接,它返回第一个(左侧)数据源的每个元素(即使其他数据源中没有关联元素)。 示例 此示例对 Contact 和 SalesOrderHeader 表执行 GroupJoin。 组联接等效于左外部联接,它返回第一个(左 侧)数据源的每个元素(即使其他数据源中没有关联元素)。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); var orders = ds.Tables["SalesOrderHeader"].AsEnumerable(); var details = ds.Tables["SalesOrderDetail"].AsEnumerable(); var query = from order in orders join detail in details on order.Field("SalesOrderID") equals detail.Field("SalesOrderID") into ords select new { CustomerID = order.Field("SalesOrderID"), ords = ords.Count() }; foreach (var order in query) { Console.WriteLine("CustomerID: {0} Orders Count: {1}", order.CustomerID, order.ords); } C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); 页码,1/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/f4d86667-3392... Join ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable contacts = ds.Tables["Contact"]; DataTable orders = ds.Tables["SalesOrderHeader"]; var query = from contact in contacts.AsEnumerable() join order in orders.AsEnumerable() on contact.Field("ContactID") equals order.Field("ContactID") select new { ContactID = contact.Field("ContactID"), SalesOrderID = order.Field("SalesOrderID"), FirstName = contact.Field("FirstName"), Lastname = contact.Field("Lastname"), TotalDue = order.Field("TotalDue") }; foreach (var contact_order in query) { Console.WriteLine("ContactID: {0} " + "SalesOrderID: {1} " + "FirstName: {2} " + "Lastname: {3} " + "TotalDue: {4}", contact_order.ContactID, contact_order.SalesOrderID, contact_order.FirstName, contact_order.Lastname, contact_order.TotalDue); } 示例 此示例对 SalesOrderHeader 和 SalesOrderDetail 表执行联接,以获取八月份的联机订单。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderHeader"]; DataTable details = ds.Tables["SalesOrderDetail"]; var query = from order in orders.AsEnumerable() join detail in details.AsEnumerable() on order.Field("SalesOrderID") equals detail.Field("SalesOrderID") where order.Field("OnlineOrderFlag") == true && order.Field("OrderDate").Month == 8 select new { SalesOrderID = order.Field("SalesOrderID"), SalesOrderDetailID = detail.Field("SalesOrderDetailID"), OrderDate = order.Field("OrderDate"), ProductID = detail.Field("ProductID") }; foreach (var order in query) { Console.WriteLine("{0}\t{1}\t{2:d}\t{3}", order.SalesOrderID, order.SalesOrderDetailID, order.OrderDate, order.ProductID); } 页码,2/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/f4d86667-3392... 请参见 概念 向数据集中加载数据 LINQ to DataSet 示例 Standard Query Operators Overview 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/f4d86667-3392... 全部折叠 代码:C# .NET Framework 开发人员指南 基于方法的查询语法示例 (LINQ to DataSet) 请参见 发送反馈意见 本节提供使用标准查询运算符的基于方法的查询语法的 LINQ to DataSet 编程示例。 这些示例中所使用的 DataSet 已通过使用 向数据集中加载数据 中指定的 FillDataSet 方法进行填充。 有关更多信息,请参见Standard Query Operators Overview。 本节内容 请参见 基于方法的查询语法示例: 投影 (LINQ to DataSet) 本主题中的示例演示如何使用 Select 和 SelectMany 方法来查询 DataSet。 基于方法的查询语法示例: 分区 (LINQ to DataSet) 本主题中的示例演示如何使用 Skip(TSource) 和 Take(TSource) 方法来查询 DataSet 并分隔结果。 基于方法的查询语法示例: 排序 (LINQ to DataSet) 本主题中的示例演示如何使用 OrderBy、OrderByDescending、Reverse(TSource) 和 ThenByDescending 方法 来查询 DataSet 并排序结果。 基于方法的查询语法示例: 集合运算符 (LINQ to DataSet) 本主题中的示例演示如何使用 Distinct、Except、Intersect 和 Union 运算符对多组数据行执行基于值的比较 运算。 基于方法的查询语法示例: 转换 (LINQ to DataSet) 本主题中的示例演示如何使用 ToArray(TSource)、ToDictionary 和 ToList(TSource) 方法立即执行查询表达 式。 基于方法的查询语法示例: 元素运算符 (LINQ to DataSet) 本主题中的示例演示如何使用 First 和 ElementAt(TSource) 方法来获取 DataSet 中的 DataRow 元素。 基于方法的查询语法示例: 聚合运算符 (LINQ to DataSet) 本主题中的示例演示如何使用 Average、Count、Max、Min 和 Sum 方法来查询 DataSet 并聚合数据。 基于方法的查询语法示例: 联接 (LINQ to DataSet) 本主题中的示例演示如何使用 GroupJoin 和 Join 方法来查询 DataSet。 概念 查询表达式语法示例 (LINQ to DataSet) 数据集特定的运算符示例 (LINQ to DataSet) LINQ to DataSet 示例 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/d340775c-7f39... 全部折叠 代码:C# .NET Framework 开发人员指南 基于方法的查询语法示例: 投影 (LINQ to DataSet) 请参见 发送反馈意见 本主题中的示例演示如何使用 Select 和 SelectMany 方法以便使用基于方法的查询语法来查询 DataSet。 这些示例中所使用的 FillDataSet 方法已在 向数据集中加载数据 中指定。 本主题中的示例使用 AdventureWorks 示例数据库中的 Contact、Address、Product、SalesOrderHeader 和 SalesOrderDetail 表。 本主题中的示例使用下面的 using/Imports 语句: 有关更多信息,请参见如何: 在 Visual Studio 中创建 LINQ to DataSet 项目。 Select SelectMany C# 复制代码 using System; using System.Linq; using System.Linq.Expressions; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Data.Common; using System.Globalization; 示例 此示例使用 Select 方法将 Name、ProductNumber 和 ListPrice 属性投影到一系列匿名类型。 在生成的类型 中,还将 ListPrice 属性重命名为 Price。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable products = ds.Tables["Product"]; var query = products.AsEnumerable(). Select(product => new { ProductName = product.Field("Name"), ProductNumber = product.Field("ProductNumber"), Price = product.Field("ListPrice") }); Console.WriteLine("Product Info:"); foreach (var productInfo in query) { Console.WriteLine("Product name: {0} Product number: {1} List price: ${2} ", productInfo.ProductName, productInfo.ProductNumber, productInfo.Price); } 示例 此示例使用 SelectMany 方法来选择 TotalDue 小于 500.00 的所有订单。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); var contacts = ds.Tables["Contact"].AsEnumerable(); var orders = ds.Tables["SalesOrderHeader"].AsEnumerable(); var query = contacts.SelectMany( 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/0fc2c8f0-1967... 请参见 示例 此示例使用 SelectMany 方法来选择于 2002 年 10 月 1 日或之后达成的所有订单。 contact => orders.Where(order => (contact.Field("ContactID") == order.Field("ContactID")) && order.Field("TotalDue") < 500.00M) .Select(order => new { ContactID = contact.Field("ContactID"), LastName = contact.Field("LastName"), FirstName = contact.Field("FirstName"), OrderID = order.Field("SalesOrderID"), Total = order.Field("TotalDue") })); foreach (var smallOrder in query) { Console.WriteLine("Contact ID: {0} Name: {1}, {2} Order ID: {3} Total Due: ${4} ", smallOrder.ContactID, smallOrder.LastName, smallOrder.FirstName, smallOrder.OrderID, smallOrder.Total); } C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); var contacts = ds.Tables["Contact"].AsEnumerable(); var orders = ds.Tables["SalesOrderHeader"].AsEnumerable(); var query = contacts.SelectMany( contact => orders.Where(order => (contact.Field("ContactID") == order.Field("ContactID")) && order.Field("OrderDate") >= new DateTime(2002, 10, 1)) .Select(order => new { ContactID = contact.Field("ContactID"), LastName = contact.Field("LastName"), FirstName = contact.Field("FirstName"), OrderID = order.Field("SalesOrderID"), OrderDate = order.Field("OrderDate") })); foreach (var order in query) { Console.WriteLine("Contact ID: {0} Name: {1}, {2} Order ID: {3} Order date: {4:d} ", order.ContactID, order.LastName, order.FirstName, order.OrderID, order.OrderDate); } 概念 向数据集中加载数据 LINQ to DataSet 示例 Standard Query Operators Overview 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/0fc2c8f0-1967... 全部折叠 代码:C# .NET Framework 开发人员指南 基于方法的查询语法示例: 分区 (LINQ to DataSet) 请参见 发送反馈意见 本主题中的示例演示如何使用 Skip(TSource)、SkipWhile、Take(TSource) 和 TakeWhile 方法以便使用查询表达式语 法来查询 DataSet。 这些示例中所使用的 FillDataSet 方法已在 向数据集中加载数据 中指定。 本主题中的示例使用 AdventureWorks 示例数据库中的 Contact、Address、Product、SalesOrderHeader 和 SalesOrderDetail 表。 本主题中的示例使用下面的 using/Imports 语句: 有关更多信息,请参见如何: 在 Visual Studio 中创建 LINQ to DataSet 项目。 Skip C# 复制代码 using System; using System.Linq; using System.Linq.Expressions; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Data.Common; using System.Globalization; 示例 此示例使用 Skip(TSource) 方法获取 Contact 表中除前五个联系人以外的所有联系人。 示例 此示例使用 Skip(TSource) 方法获取 Seattle 中除前两个地址以外的所有地址。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable contacts = ds.Tables["Contact"]; IEnumerable allButFirst5Contacts = contacts.AsEnumerable().Skip(5); Console.WriteLine("All but first 5 contacts:"); foreach (DataRow contact in allButFirst5Contacts) { Console.WriteLine("FirstName = {0} \tLastname = {1}", contact.Field("FirstName"), contact.Field("Lastname")); } C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable addresses = ds.Tables["Address"]; DataTable orders = ds.Tables["SalesOrderHeader"]; var query = ( from address in addresses.AsEnumerable() from order in orders.AsEnumerable() where address.Field("AddressID") == order.Field("BillToAddressID") && address.Field("City") == "Seattle" select new { City = address.Field("City"), 页码,1/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/a582c53f-f203... SkipWhile Take OrderID = order.Field("SalesOrderID"), OrderDate = order.Field("OrderDate") }).Skip(2); Console.WriteLine("All but first 2 orders in Seattle:"); foreach (var order in query) { Console.WriteLine("City: {0} Order ID: {1} Total Due: {2:d}", order.City, order.OrderID, order.OrderDate); } 示例 此示例使用 OrderBy 和 SkipWhile 方法返回 Product 表中定价大于 300.00 的产品。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable products = ds.Tables["Product"]; IEnumerable skipWhilePriceLessThan300 = products.AsEnumerable() .OrderBy(listprice => listprice.Field("ListPrice")) .SkipWhile(product => product.Field("ListPrice") < 300.00M); Console.WriteLine("Skip while ListPrice is less than 300.00:"); foreach (DataRow product in skipWhilePriceLessThan300) { Console.WriteLine(product.Field("ListPrice")); } 示例 此示例使用 Take(TSource) 方法只获取 Contact 表中的前五个联系人。 示例 此示例使用 Take(TSource) 方法获取 Seattle 中的前三个地址。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable contacts = ds.Tables["Contact"]; IEnumerable first5Contacts = contacts.AsEnumerable().Take(5); Console.WriteLine("First 5 contacts:"); foreach (DataRow contact in first5Contacts) { Console.WriteLine("Title = {0} \t FirstName = {1} \t Lastname = {2}", contact.Field("Title"), contact.Field("FirstName"), contact.Field("Lastname")); } C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable addresses = ds.Tables["Address"]; DataTable orders = ds.Tables["SalesOrderHeader"]; 页码,2/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/a582c53f-f203... TakeWhile 请参见 var query = ( from address in addresses.AsEnumerable() from order in orders.AsEnumerable() where address.Field("AddressID") == order.Field("BillToAddressID") && address.Field("City") == "Seattle" select new { City = address.Field("City"), OrderID = order.Field("SalesOrderID"), OrderDate = order.Field("OrderDate") }).Take(3); Console.WriteLine("First 3 orders in Seattle:"); foreach (var order in query) { Console.WriteLine("City: {0} Order ID: {1} Total Due: {2:d}", order.City, order.OrderID, order.OrderDate); } 示例 此示例使用 OrderBy 和 TakeWhile 返回 Product 表中定价小于 300.00 的产品。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable products = ds.Tables["Product"]; IEnumerable takeWhileListPriceLessThan300 = products.AsEnumerable() .OrderBy(listprice => listprice.Field("ListPrice")) .TakeWhile(product => product.Field("ListPrice") < 300.00M); Console.WriteLine("First ListPrice less than 300:"); foreach (DataRow product in takeWhileListPriceLessThan300) { Console.WriteLine(product.Field("ListPrice")); } 概念 向数据集中加载数据 LINQ to DataSet 示例 Standard Query Operators Overview 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/a582c53f-f203... 全部折叠 代码:C# .NET Framework 开发人员指南 基于方法的查询语法示例: 排序 (LINQ to DataSet) 请参见 发送反馈意见 本主题中的示例演示如何使用 OrderBy、Reverse(TSource) 和 ThenBy 方法来查询 DataSet 并使用方法查询语法对 结果排序。 这些示例中所使用的 FillDataSet 方法已在 向数据集中加载数据 中指定。 本主题中的示例使用 AdventureWorks 示例数据库中的 Contact、Address、Product、SalesOrderHeader 和 SalesOrderDetail 表。 本主题中的示例使用下面的 using/Imports 语句: 有关更多信息,请参见如何: 在 Visual Studio 中创建 LINQ to DataSet 项目。 OrderBy Reverse C# 复制代码 using System; using System.Linq; using System.Linq.Expressions; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Data.Common; using System.Globalization; 示例 此示例使用具有自定义比较器的 OrderBy 方法对姓氏执行不区分大小写的排序。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable contacts = ds.Tables["Contact"]; IEnumerable query = contacts.AsEnumerable().OrderBy(contact => contact.Field("LastName"), new CaseInsensitiveComparer()); foreach (DataRow contact in query) { Console.WriteLine(contact.Field("LastName")); } 示例 此示例使用 Reverse(TSource) 方法创建 OrderDate 早于 2002 年 2 月 20 日的订单的列表。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderHeader"]; IEnumerable query = ( from order in orders.AsEnumerable() where order.Field("OrderDate") < new DateTime(2002, 02, 20) select order).Reverse(); Console.WriteLine("A backwards list of orders where OrderDate < Feb 20, 2002"); foreach (DataRow order in query) { Console.WriteLine(order.Field("OrderDate")); 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/8f9ce4fd-e84f-... ThenBy 请参见 } 示例 此示例使用具有自定义比较器的 OrderBy 和 ThenBy 方法对产品名称先按定价排序,然后执行不区分大小写 的降序排序。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable products = ds.Tables["Product"]; IEnumerable query = products.AsEnumerable().OrderBy(product => product.Field("ListPrice")) .ThenBy(product => product.Field("Name"), new CaseInsensitiveComparer()); foreach (DataRow product in query) { Console.WriteLine("Product ID: {0} Product Name: {1} List Price {2}", product.Field("ProductID"), product.Field("Name"), product.Field("ListPrice")); } 概念 向数据集中加载数据 LINQ to DataSet 示例 Standard Query Operators Overview 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/8f9ce4fd-e84f-... 全部折叠 代码:C# .NET Framework 开发人员指南 基于方法的查询语法示例: 集合运算符 (LINQ to DataSet) 请参见 发送反馈意见 本主题中的示例演示如何使用 Distinct、Except、Intersect 和 Union 运算符对多组数据行执行基于值的比较运算。 向数据集中加载数据有关 DataRowComparer 的更多信息,请参见比较 DataRow (LINQ to DataSet)。 这些示例中所使用的 FillDataSet 方法已在 向数据集中加载数据 中指定。 本主题中的示例使用 AdventureWorks 示例数据库中的 Contact、Address、Product、SalesOrderHeader 和 SalesOrderDetail 表。 本主题中的示例使用下面的 using/Imports 语句: 有关更多信息,请参见如何: 在 Visual Studio 中创建 LINQ to DataSet 项目。 Distinct Except C# 复制代码 using System; using System.Linq; using System.Linq.Expressions; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Data.Common; using System.Globalization; 示例 此示例使用 Distinct 方法移除序列中的重复元素。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); List rows = new List(); DataTable contact = ds.Tables["Contact"]; // Get 100 rows from the Contact table. IEnumerable query = (from c in contact.AsEnumerable() select c).Take(100); DataTable contactsTableWith100Rows = query.CopyToDataTable(); // Add 100 rows to the list. foreach (DataRow row in contactsTableWith100Rows.Rows) rows.Add(row); // Create duplicate rows by adding the same 100 rows to the list. foreach (DataRow row in contactsTableWith100Rows.Rows) rows.Add(row); DataTable table = System.Data.DataTableExtensions.CopyToDataTable(rows); // Find the unique contacts in the table. IEnumerable uniqueContacts = table.AsEnumerable().Distinct(DataRowComparer.Default); Console.WriteLine("Unique contacts:"); foreach (DataRow uniqueContact in uniqueContacts) { Console.WriteLine(uniqueContact.Field("ContactID")); } 示例 页码,1/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/fa93af15-28af-... Intersect Union 此示例使用 Except 方法返回出现在第一个表中但不出现在第二个表中的联系人。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable contactTable = ds.Tables["Contact"]; // Create two tables. IEnumerable query1 = from contact in contactTable.AsEnumerable() where contact.Field("Title") == "Ms." select contact; IEnumerable query2 = from contact in contactTable.AsEnumerable() where contact.Field("FirstName") == "Sandra" select contact; DataTable contacts1 = query1.CopyToDataTable(); DataTable contacts2 = query2.CopyToDataTable(); // Find the contacts that are in the first // table but not the second. var contacts = contacts1.AsEnumerable().Except(contacts2.AsEnumerable(), DataRowComparer.Default); Console.WriteLine("Except of employees tables"); foreach (DataRow row in contacts) { Console.WriteLine("Id: {0} {1} {2} {3}", row["ContactID"], row["Title"], row["FirstName"], row["LastName"]); } 示例 此示例使用 Intersect 方法返回同时出现在两个表中的联系人。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable contactTable = ds.Tables["Contact"]; // Create two tables. IEnumerable query1 = from contact in contactTable.AsEnumerable() where contact.Field("Title") == "Ms." select contact; IEnumerable query2 = from contact in contactTable.AsEnumerable() where contact.Field("FirstName") == "Sandra" select contact; DataTable contacts1 = query1.CopyToDataTable(); DataTable contacts2 = query2.CopyToDataTable(); // Find the intersection of the two tables. var contacts = contacts1.AsEnumerable().Intersect(contacts2.AsEnumerable(), DataRowComparer.Default); Console.WriteLine("Intersection of contacts tables"); foreach (DataRow row in contacts) { Console.WriteLine("Id: {0} {1} {2} {3}", row["ContactID"], row["Title"], row["FirstName"], row["LastName"]); } 示例 页码,2/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/fa93af15-28af-... 请参见 此示例使用 Union 方法返回这两个表的任一表中特有的联系人。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); // Create two tables. DataTable contactTable = ds.Tables["Contact"]; IEnumerable query1 = from contact in contactTable.AsEnumerable() where contact.Field("Title") == "Ms." select contact; IEnumerable query2 = from contact in contactTable.AsEnumerable() where contact.Field("FirstName") == "Sandra" select contact; DataTable contacts1 = query1.CopyToDataTable(); DataTable contacts2 = query2.CopyToDataTable(); // Find the union of the two tables. var contacts = contacts1.AsEnumerable().Union(contacts2.AsEnumerable(), DataRowComparer.Default); Console.WriteLine("Union of contacts tables:"); foreach (DataRow row in contacts) { Console.WriteLine("Id: {0} {1} {2} {3}", row["ContactID"], row["Title"], row["FirstName"], row["LastName"]); } 概念 向数据集中加载数据 LINQ to DataSet 示例 Standard Query Operators Overview 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/fa93af15-28af-... 全部折叠 代码:C# .NET Framework 开发人员指南 基于方法的查询语法示例: 转换 (LINQ to DataSet) 请参见 发送反馈意见 本主题中的示例演示如何使用 ToArray(TSource)、ToDictionary 和 ToList(TSource) 方法立即执行查询表达式。 这些示例中所使用的 FillDataSet 方法已在 向数据集中加载数据 中指定。 本主题中的示例使用 AdventureWorks 示例数据库中的 Contact、Address、Product、SalesOrderHeader 和 SalesOrderDetail 表。 本主题中的示例使用下面的 using/Imports 语句: 有关更多信息,请参见如何: 在 Visual Studio 中创建 LINQ to DataSet 项目。 ToArray ToDictionary ToList C# 复制代码 using System; using System.Linq; using System.Linq.Expressions; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Data.Common; using System.Globalization; 示例 此示例使用 ToArray(TSource) 方法立即将序列计算为数组。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable products = ds.Tables["Product"]; IEnumerable productsArray = products.AsEnumerable().ToArray(); IEnumerable query = from product in productsArray orderby product.Field("ListPrice") descending select product; Console.WriteLine("Every price from highest to lowest:"); foreach (DataRow product in query) { Console.WriteLine(product.Field("ListPrice")); } 示例 此示例使用 ToDictionary 方法立即将序列和相关键表达式计算为字典。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable products = ds.Tables["Product"]; var scoreRecordsDict = products.AsEnumerable(). ToDictionary(record => record.Field("Name")); Console.WriteLine("Top Tube's ProductID: {0}", scoreRecordsDict["Top Tube"]["ProductID"]); 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/a084c16b-9b5... 请参见 示例 此示例使用 ToList(TSource) 方法立即将序列计算为 List(T),其中 T 的类型为 DataRow。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable products = ds.Tables["Product"]; IEnumerable productList = products.AsEnumerable().ToList(); IEnumerable query = from product in productList orderby product.Field("Name") select product; Console.WriteLine("The product list, ordered by product name:"); foreach (DataRow product in query) { Console.WriteLine(product.Field("Name").ToLower(CultureInfo.InvariantCulture)); } 概念 向数据集中加载数据 LINQ to DataSet 示例 Standard Query Operators Overview 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/a084c16b-9b5... 全部折叠 代码:C# .NET Framework 开发人员指南 基于方法的查询语法示例: 元素运算符 (LINQ to DataSet) 请参见 发送反馈意见 本主题中的示例演示如何使用 First 方法以便使用基于方法的查询语法来获取 DataSet 中的 DataRow 元素。 这些示例中所使用的 FillDataSet 方法已在 向数据集中加载数据 中指定。 本主题中的示例使用 AdventureWorks 示例数据库中的 Contact、Address、Product、SalesOrderHeader 和 SalesOrderDetail 表。 本主题中的示例使用下面的 using/Imports 语句: 有关更多信息,请参见如何: 在 Visual Studio 中创建 LINQ to DataSet 项目。 First 请参见 C# 复制代码 using System; using System.Linq; using System.Linq.Expressions; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Data.Common; using System.Globalization; 示例 此示例使用 First 方法来查找以“caroline”开头的第一个电子邮件地址。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable contacts = ds.Tables["Contact"]; DataRow startsWith = contacts.AsEnumerable(). First(contact => contact.Field("EmailAddress").StartsWith("caroline")); Console.WriteLine("An email address starting with 'caroline': {0}", startsWith.Field("EmailAddress")); 概念 向数据集中加载数据 LINQ to DataSet 示例 Standard Query Operators Overview 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/eedf2fbd-f407-... 全部折叠 代码:C# .NET Framework 开发人员指南 基于方法的查询语法示例: 聚合运算符 (LINQ to DataSet) 请参见 发送反馈意见 本主题中的示例演示如何使用 Aggregate、Average、Count、LongCount、Max、Min 和 Sum 运算符来查询 DataSet 并使用方法查询语法聚合数据。 这些示例中所使用的 FillDataSet 方法已在 向数据集中加载数据 中指定。 本主题中的示例使用 AdventureWorks 示例数据库中的 Contact、Address、Product、SalesOrderHeader 和 SalesOrderDetail 表。 本主题中的示例使用下面的 using/Imports 语句: 有关更多信息,请参见如何: 在 Visual Studio 中创建 LINQ to DataSet 项目。 Aggregate Average C# 复制代码 using System; using System.Linq; using System.Linq.Expressions; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Data.Common; using System.Globalization; 示例 此示例使用 Aggregate 方法获取 Contact 表中前 5 个联系人并生成逗号分隔的姓氏列表。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); IEnumerable contacts = ds.Tables["Contact"].AsEnumerable(); string nameList = contacts.Take(5).Select(contact => contact.Field("LastName")).Aggregate((workingList, next) => workingList + "," + next); Console.WriteLine(nameList); 示例 此示例使用 Average 方法来计算产品的平均定价。 示例 此示例使用 Average 方法来计算每种款式的产品的平均定价。 示例 此示例使用 Average 方法来计算总平均应付金额。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); var products = ds.Tables["Product"].AsEnumerable(); Decimal averageListPrice = products.Average(product => product.Field("ListPrice")); Console.WriteLine("The average list price of all the products is ${0}", averageListPrice); C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); var products = ds.Tables["Product"].AsEnumerable(); var query = from product in products group product by product.Field("Style") into g select new { Style = g.Key, AverageListPrice = g.Average(product => product.Field("ListPrice")) }; foreach (var product in query) { Console.WriteLine("Product style: {0} Average list price: {1}", product.Style, product.AverageListPrice); } C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderHeader"]; 页码,1/5(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/5ed5f01d-acb2... Count 示例 此示例使用 Average 方法获取每个联系人 ID 的总平均应付金额。 示例 此示例使用 Average 方法获取每个联系人的具有平均 TotalDue 的订单。 Decimal averageTotalDue = orders.AsEnumerable(). Average(order => order.Field("TotalDue")); Console.WriteLine("The average TotalDue is {0}.", averageTotalDue); C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderHeader"]; var query = from order in orders.AsEnumerable() group order by order.Field("ContactID") into g select new { Category = g.Key, averageTotalDue = g.Average(order => order.Field("TotalDue")) }; foreach (var order in query) { Console.WriteLine("ContactID = {0} \t Average TotalDue = {1}", order.Category, order.averageTotalDue); } C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderHeader"]; var query = from order in orders.AsEnumerable() group order by order.Field("ContactID") into g let averageTotalDue = g.Average(order => order.Field("TotalDue")) select new { Category = g.Key, CheapestProducts = g.Where(order => order.Field("TotalDue") == averageTotalDue) }; foreach (var orderGroup in query) { Console.WriteLine("ContactID: {0}", orderGroup.Category); foreach (var order in orderGroup.CheapestProducts) { Console.WriteLine("Average total due for SalesOrderID {1} is: {0}", order.Field("TotalDue"), order.Field("SalesOrderID")); } Console.WriteLine(""); } 示例 此示例使用 Count 方法返回 Product 表中的产品数目。 示例 此示例使用 Count 方法返回联系人 ID 的列表及每个联系人 ID 的订单数。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); var products = ds.Tables["Product"].AsEnumerable(); int numProducts = products.Count(); Console.WriteLine("There are {0} products.", numProducts); C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable contacts = ds.Tables["Contact"]; var query = from contact in contacts.AsEnumerable() select new { CustomerID = contact.Field("ContactID"), 页码,2/5(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/5ed5f01d-acb2... LongCount Max 示例 此示例按颜色对产品分组并使用 Count 方法返回每个颜色组中的产品数目。 OrderCount = contact.GetChildRows("SalesOrderContact").Count() }; foreach (var contact in query) { Console.WriteLine("CustomerID = {0} \t OrderCount = {1}", contact.CustomerID, contact.OrderCount); } C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable products = ds.Tables["Product"]; var query = from product in products.AsEnumerable() group product by product.Field("Color") into g select new { Color = g.Key, ProductCount = g.Count() }; foreach (var product in query) { Console.WriteLine("Color = {0} \t ProductCount = {1}", product.Color, product.ProductCount); } 示例 此示例获取长整型的联系人计数。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable contacts = ds.Tables["Contact"]; long numberOfContacts = contacts.AsEnumerable().LongCount(); Console.WriteLine("There are {0} Contacts", numberOfContacts); 示例 此示例使用 Max 方法来获取最大总应付金额。 示例 此示例使用 Max 方法获取每个联系人 ID 的最大总应付金额。 示例 此示例使用 Max 方法获取每个联系人 ID 的具有最大 TotalDue 的订单。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderHeader"]; Decimal maxTotalDue = orders.AsEnumerable(). Max(w => w.Field("TotalDue")); Console.WriteLine("The maximum TotalDue is {0}.", maxTotalDue); C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderHeader"]; var query = from order in orders.AsEnumerable() group order by order.Field("ContactID") into g select new { Category = g.Key, maxTotalDue = g.Max(order => order.Field("TotalDue")) }; foreach (var order in query) { Console.WriteLine("ContactID = {0} \t Maximum TotalDue = {1}", order.Category, order.maxTotalDue); } 页码,3/5(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/5ed5f01d-acb2... Min C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderHeader"]; var query = from order in orders.AsEnumerable() group order by order.Field("ContactID") into g let maxTotalDue = g.Max(order => order.Field("TotalDue")) select new { Category = g.Key, CheapestProducts = g.Where(order => order.Field("TotalDue") == maxTotalDue) }; foreach (var orderGroup in query) { Console.WriteLine("ContactID: {0}", orderGroup.Category); foreach (var order in orderGroup.CheapestProducts) { Console.WriteLine("MaxTotalDue {0} for SalesOrderID {1}: ", order.Field("TotalDue"), order.Field("SalesOrderID")); } } 示例 此示例使用 Min 方法来获取最小总应付金额。 示例 此示例使用 Min 方法获取每个联系人 ID 的最小总应付金额。 示例 此示例使用 Min 方法获取每个联系人的具有最小总应付金额的订单。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderHeader"]; Decimal smallestTotalDue = orders.AsEnumerable(). Min(totalDue => totalDue.Field("TotalDue")); Console.WriteLine("The smallest TotalDue is {0}.", smallestTotalDue); C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderHeader"]; var query = from order in orders.AsEnumerable() group order by order.Field("ContactID") into g select new { Category = g.Key, smallestTotalDue = g.Min(order => order.Field("TotalDue")) }; foreach (var order in query) { Console.WriteLine("ContactID = {0} \t Minimum TotalDue = {1}", order.Category, order.smallestTotalDue); } C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderHeader"]; var query = from order in orders.AsEnumerable() group order by order.Field("ContactID") into g let minTotalDue = g.Min(order => order.Field("TotalDue")) select new { Category = g.Key, smallestTotalDue = g.Where(order => order.Field("TotalDue") == minTotalDue) }; foreach (var orderGroup in query) { Console.WriteLine("ContactID: {0}", orderGroup.Category); foreach (var order in orderGroup.smallestTotalDue) 页码,4/5(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/5ed5f01d-acb2... Sum 请参见 { Console.WriteLine("Mininum TotalDue {0} for SalesOrderID {1}: ", order.Field("TotalDue"), order.Field("SalesOrderID")); } Console.WriteLine(""); } 示例 此示例使用 Sum 方法获取 SalesOrderDetail 表中订单数量的总数目。 示例 此示例使用 Sum 方法获取每个联系人 ID 的总应付金额。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderDetail"]; double totalOrderQty = orders.AsEnumerable(). Sum(o => o.Field("OrderQty")); Console.WriteLine("There are a total of {0} OrderQty.", totalOrderQty); C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable orders = ds.Tables["SalesOrderHeader"]; var query = from order in orders.AsEnumerable() group order by order.Field("ContactID") into g select new { Category = g.Key, TotalDue = g.Sum(order => order.Field("TotalDue")), }; foreach (var order in query) { Console.WriteLine("ContactID = {0} \t TotalDue sum = {1}", order.Category, order.TotalDue); } 概念 向数据集中加载数据 LINQ to DataSet 示例 Standard Query Operators Overview 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,5/5(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/5ed5f01d-acb2... 全部折叠 代码:C# .NET Framework 开发人员指南 基于方法的查询语法示例: 联接 (LINQ to DataSet) 请参见 发送反馈意见 联接是面向相互之间具有可导航关系的数据源(如关系数据库表)的查询中的一项重要操作。 联接两个数据源就是 将一个数据源中的对象与另一个数据源中具有相同公共属性的对象相关联。 有关更多信息,请参见Standard Query Operators Overview。 本主题中的示例演示如何使用 Join 方法以便使用该方法的查询语法来查询 DataSet。 这些示例中所使用的 FillDataSet 方法已在 向数据集中加载数据 中指定。 本主题中的示例使用 AdventureWorks 示例数据库中的 Contact、Address、Product、SalesOrderHeader 和 SalesOrderDetail 表。 本主题中的示例使用下面的 using/Imports 语句: 有关更多信息,请参见如何: 在 Visual Studio 中创建 LINQ to DataSet 项目。 Join C# 复制代码 using System; using System.Linq; using System.Linq.Expressions; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Data.Common; using System.Globalization; 示例 此示例对 Contact 和 SalesOrderHeader 表执行联接。 示例 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable contacts = ds.Tables["Contact"]; DataTable orders = ds.Tables["SalesOrderHeader"]; var query = contacts.AsEnumerable().Join(orders.AsEnumerable(), order => order.Field("ContactID"), contact => contact.Field("ContactID"), (contact, order) => new { ContactID = contact.Field("ContactID"), SalesOrderID = order.Field("SalesOrderID"), FirstName = contact.Field("FirstName"), Lastname = contact.Field("Lastname"), TotalDue = order.Field("TotalDue") }); foreach (var contact_order in query) { Console.WriteLine("ContactID: {0} " + "SalesOrderID: {1} " + "FirstName: {2} " + "Lastname: {3} " + "TotalDue: {4}", contact_order.ContactID, contact_order.SalesOrderID, contact_order.FirstName, contact_order.Lastname, contact_order.TotalDue); } 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/4fd5ed2c-b03a... 请参见 此示例对 Contact 和 SalesOrderHeader 表执行联接,并对结果按联系人 ID 进行分组。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable contacts = ds.Tables["Contact"]; DataTable orders = ds.Tables["SalesOrderHeader"]; var query = contacts.AsEnumerable().Join(orders.AsEnumerable(), order => order.Field("ContactID"), contact => contact.Field("ContactID"), (contact, order) => new { ContactID = contact.Field("ContactID"), SalesOrderID = order.Field("SalesOrderID"), FirstName = contact.Field("FirstName"), Lastname = contact.Field("Lastname"), TotalDue = order.Field("TotalDue") }) .GroupBy(record => record.ContactID); foreach (var group in query) { foreach (var contact_order in group) { Console.WriteLine("ContactID: {0} " + "SalesOrderID: {1} " + "FirstName: {2} " + "Lastname: {3} " + "TotalDue: {4}", contact_order.ContactID, contact_order.SalesOrderID, contact_order.FirstName, contact_order.Lastname, contact_order.TotalDue); } } 概念 向数据集中加载数据 LINQ to DataSet 示例 Standard Query Operators Overview 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/4fd5ed2c-b03a... 全部折叠 代码:C# .NET Framework 开发人员指南 数据集特定的运算符示例 (LINQ to DataSet) 请参见 发送反馈意见 本主题中的示例演示如何使用 CopyToDataTable 方法和 DataRowComparer 类。 这些示例中所使用的 FillDataSet 方法已在 向数据集中加载数据 中指定。 本主题中的示例使用 AdventureWorks 示例数据库中的 Contact、Address、Product、SalesOrderHeader 和 SalesOrderDetail 表。 本主题中的示例使用下面的 using/Imports 语句: 有关更多信息,请参见如何: 在 Visual Studio 中创建 LINQ to DataSet 项目。 CopyToDataTable DataRowComparer C# 复制代码 using System; using System.Linq; using System.Linq.Expressions; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Data.Common; using System.Globalization; 示例 此示例通过使用 DataTable 方法加载包含查询结果的 CopyToDataTable。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); DataTable contacts1 = ds.Tables["Contact"]; IEnumerable query = from contact in contacts1.AsEnumerable() where contact.Field("Title") == "Ms." && contact.Field("FirstName") == "Carla" select contact; DataTable contacts2 = query.CopyToDataTable(); foreach (DataRow contact in contacts2.AsEnumerable()) { Console.WriteLine("ID:{0} Name: {1}, {2}", contact.Field("ContactID"), contact.Field("LastName"), contact.Field("FirstName")); } 复制代码 示例 此示例通过使用 DataRowComparer 比较两个不同的数据行。 C# 复制代码 // Fill the DataSet. DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; FillDataSet(ds); // Get two rows from the SalesOrderHeader table. DataTable table = ds.Tables["SalesOrderHeader"]; DataRow left = (DataRow)table.Rows[0]; 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/8fdd64af-6ad0... 请参见 DataRow right = (DataRow)table.Rows[1]; // Compare the two different rows. IEqualityComparer comparer = DataRowComparer.Default; bool bEqual = comparer.Equals(left, right); if (bEqual) Console.WriteLine("The two rows are equal"); else Console.WriteLine("The two rows are not equal"); // Get the hash codes of the two rows. Console.WriteLine("The hashcodes for the two rows are {0}, {1}", comparer.GetHashCode(left), comparer.GetHashCode(right)); 概念 向数据集中加载数据 LINQ to DataSet 示例 发送 反馈意见 ,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/wd_linqadonet/html/8fdd64af-6ad0... 全部折叠 代码:C# LINQ to SQL LINQ to SQL 发送反馈意见 LINQ to SQL 是 .NET Framework 3.5 版的一个组件,提供了用于将关系数据作为对象管理的运行时基础结构。 在 LINQ to SQL 中,关系数据库的数据模型映射到用开发人员所用的编程语言表示的对象模型。当应用程序运行 时,LINQ to SQL 会将对象模型中的语言集成查询转换为 SQL,然后将它们发送到数据库进行执行。当数据库返回 结果时,LINQ to SQL 会将它们转换回您可以用您自己的编程语言处理的对象。 使用 Visual Studio 的开发人员通常使用对象关系设计器,它提供了用于实现许多 LINQ to SQL 功能的用户界面。有 关更多信息,请参见Object Relational Designer (O/R Designer) 和对象关系设计器(O/R 设计器). 此版本的 LINQ to SQL 附带的文档介绍了生成 LINQ to SQL 应用程序所需的基本构造块、流程和技术。您还可以在 MSDN Library 中搜索特定问题,并且可以参与 LINQ Forum(LINQ 论坛),在这里您可以与专家们详细讨论更复杂 的主题。最后,LINQ to SQL: .NET Language-Integrated Query for Relational Data(LINQ to SQL:关系数据的 .NET 语言集成查询)白皮书详细介绍了 LINQ to SQL 技术并包含了 Visual Basic 和 C# 代码示例。 本节内容 相关章节 注意: 关系数据显示为由二维表(关系或平面文件)组成的集合,其中公共列将表互相关联起来。若要有效地使用 LINQ to SQL,您必须略为熟悉关系数据库的基本原理。 入门 (LINQ to SQL) 提供对 LINQ to SQL 的简要概述以及有关如何开始使用 LINQ to SQL 的信息。 编程指南 (LINQ to SQL) 提供映射、查询、更新、调试及类似任务的步骤。 参考 (LINQ to SQL) 提供有关 LINQ to SQL 的多个方面的参考信息。相关主题包括“SQL-CLR 类型映射”、“标准查询运算符转 换”等。 示例 (LINQ to SQL) 提供指向 Visual Basic 和 C# 示例的链接。 语言集成查询 (LINQ) 提供对 LINQ 技术的概述。 Visual Basic 中的 LINQ 介绍针对 Visual Basic 用户的 LINQ 技术。 LINQ to ADO.NET 链接到 ADO.NET 门户。 LINQ to SQL 演练 列出可用于 LINQ to SQL 的演练。 下载示例数据库 (LINQ to SQL) 介绍如何下载文档中使用的示例数据库。 LinqDataSource 技术概述 介绍 LinqDataSource 控件如何通过 ASP.NET 数据源控件结构向 Web 开发人员公开 语言集成查询 (LINQ)。 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/73d13345-eece-47... 全部折叠 代码:C# LINQ to SQL 入门 (LINQ to SQL) 请参见 发送反馈意见 通过使用 LINQ to SQL,您可以使用 LINQ 技术访问 SQL 数据库,就像您访问内存中的集合一样。 例如,在下面的代码中,创建了 nw 对象来表示 Northwind 数据库,将 Customers 表作为目标,筛选出了来自 London 的 Customers 行,并选择了一个表示 CompanyName 的字符串以进行检索。 执行循环时,将检索到 CompanyName 值的集合。 后续步骤 请参见 C# 复制代码 // Northwnd inherits from System.Data.Linq.DataContext. Northwnd nw = new Northwnd(@"northwnd.mdf"); var companyNameQuery = from cust in nw.Customers where cust.City == "London" select cust.CompanyName; foreach (var customer in companyNameQuery) { Console.WriteLine(customer); } 有关包括插入和更新在内的其他一些示例,请参见使用 LINQ to SQL 可以执行的操作。 接下来,请试着按一些演练和教程中的说明动手操作,实际体验一下 LINQ to SQL 的使用。请参见通过演练 学习 (LINQ to SQL)。 最后,请阅读使用 LINQ to SQL 的典型步骤,学习如何开始您自己的 LINQ to SQL 项目。 概念 LINQ to SQL LINQ 简介 LINQ to SQL 对象模型 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/db8a557a-fef8-4f4f... 全部折叠 代码:C# LINQ to SQL 使用 LINQ to SQL 可以执行的操作 请参见 发送反馈意见 LINQ to SQL 支持您作为 SQL 开发人员所期望的所有关键功能。您可以查询表中的信息、在表中插入信息以及更新 和删除表中的信息。 选择 插入 更新 只需用您自己的编程语言编写一个 LINQ 查询,然后执行该查询以检索结果,即可实现选择(“投影”)。 LINQ to SQL 本身会将所有必要操作转换为您熟悉的必要 SQL 操作。有关更多信息,请参见 LINQ to SQL。 在下面的示例中,检索来自伦敦的客户的公司名称并将其显示在控制台窗口中。 C# 复制代码 // Northwnd inherits from System.Data.Linq.DataContext. Northwnd nw = new Northwnd(@"northwnd.mdf"); var companyNameQuery = from cust in nw.Customers where cust.City == "London" select cust.CompanyName; foreach (var customer in companyNameQuery) { Console.WriteLine(customer); } 若要执行 SQL Insert,只需向您已创建的对象模型添加对象,然后对 DataContext 调用 SubmitChanges 即 可。 在下面的示例中,通过使用 InsertOnSubmit 向 Customers 表添加了一位新客户以及有关该客户的信息。 C# 复制代码 // Northwnd inherits from System.Data.Linq.DataContext. Northwnd nw = new Northwnd(@"northwnd.mdf"); Customer cust = new Customer(); cust.CompanyName = "SomeCompany"; cust.City = "London"; cust.CustomerID = "98128"; cust.PostalCode = "55555"; cust.Phone = "555-555-5555"; nw.Customers.InsertOnSubmit(cust); // At this point, the new Customer object is added in the object model. // In LINQ to SQL, the change is not sent to the database until // SubmitChanges is called. nw.SubmitChanges(); 若要 Update 某一数据库项,首先要检索该项,然后直接在对象模型中编辑它。在修改了该对象之后,请对 DataContext 调用 SubmitChanges 以更新数据库。 在下面的示例中,检索来自伦敦的所有客户。然后将其所在城市的名称从“London”更改为“London - Metro”。最后,调用 SubmitChanges 以将所做的更改发送至数据库。 C# 复制代码 Northwnd nw = new Northwnd(@"northwnd.mdf"); var cityNameQuery = from cust in nw.Customers where cust.City.Contains("London") select cust; foreach (var customer in cityNameQuery) 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/061d98b2-baa7-43... 删除 请参见 { if (customer.City == "London") { customer.City = "London - Metro"; } } nw.SubmitChanges(); 若要 Delete 某一项,请从其所属集合中移除该项,然后对 DataContext 调用 SubmitChanges 以提交所做的 更改。 在下面的示例中,从数据库中检索 CustomerID 为 98128 的客户。然后,在确认检索到客户行之后,调用 DeleteOnSubmit 以将该对象从集合中移除。最后,调用 SubmitChanges 以将删除内容转发至数据库。 注意: LINQ to SQL 无法识别级联删除操作。如果要在对行有约束的表中删除行,请参见如何:从数据库中删除行 (LINQ to SQL)。 C# 复制代码 Northwnd nw = new Northwnd(@"northwnd.mdf"); var deleteIndivCust = from cust in nw.Customers where cust.CustomerID == "98128" select cust; if (deleteIndivCust.Count() > 0) { nw.Customers.DeleteOnSubmit(deleteIndivCust.First()); nw.SubmitChanges(); } 概念 编程指南 (LINQ to SQL) LINQ to SQL 对象模型 入门 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/061d98b2-baa7-43... 全部折叠 代码:C# LINQ to SQL 使用 LINQ to SQL 的典型步骤 请参见 发送反馈意见 若要实现 LINQ to SQL 应用程序,请按照本主题后面部分说明的步骤操作。请注意,很多步骤是可选的。您可以以 对象模型的默认状态使用它,这种可能性很高。 为了很快入手,请使用对象关系设计器来创建对象模型并开始编写查询代码。有关更多信息,请参见Object Relational Designer (O/R Designer) 和对象关系设计器(O/R 设计器). 创建对象模型 使用对象模型 第一步是用现有关系数据库的元数据创建对象模型。对象模型按照开发人员所用的编程语言来表示数据库。有 关更多信息,请参见 LINQ to SQL 对象模型。 1. 选择用于创建模型的工具。 有三种工具可用于创建模型。 z 对象关系设计器。 此设计器提供了用于从现有数据库创建对象模型的丰富用户界面。此工具是 Visual Studio IDE 的一部 分,最适合小型或中型数据库。有关更多信息,请参见Object Relational Designer (O/R Designer) 和对象 关系设计器(O/R 设计器). z SQLMetal 代码生成工具 此命令行实用工具提供了与 O/R 设计器略微不同的一组选项。最好使用此工具对大型数据库进行建模。 有关更多信息,请参见代码生成工具 (SqlMetal.exe)。 z 代码编辑器 您可以通过使用 Visual Studio代码编辑器或其他编辑器编写自己的代码。我们建议,在您具有现有数据库 且可以使用 O/R 设计器或 SQLMetal 工具时不要使用这种方法,因为这种方法容易出错。但是,代码编 辑器在改进或修改您已通过使用其他工具生成的代码方面非常有用。有关更多信息,请参见如何:使用代 码编辑器自定义实体类 (LINQ to SQL)。 2. 选择您要生成的代码类型。 z 用于基于属性的映射的 C# 或 Visual Basic 源代码文件。 然后将此代码文件加入您的 Visual Studio项目中。有关更多信息,请参见基于属性的映射 (LINQ to SQL)。 z 用于外部映射的 XML 文件。 通过使用此方法,您可以将映射元数据放在应用程序代码外部。有关更多信息,请参见外部映射引用 (LINQ to SQL)。 z DBML 文件,您可以在生成最终代码文件之前修改此文件。 这是一项高级功能。 3. 改进代码文件以反映您的应用程序的需要。 为此,您可以使用 O/R 设计器或代码编辑器。 注意: O/R 设计器不支持生成外部映射文件。您必须使用 SQLMetal 工具来实现此功能。 下图显示了在两层方案中开发人员与数据之间的关系。对于其他情况,请参见使用 LINQ to SQL 的 N 层应用 程序和远程应用程序。 既然您已经有了对象模型,您就可以在该模型中描述信息请求和操作数据。您应从对象模型中的对象和属性的 角度来考虑,而不是从数据库的行和列的角度来考虑。您不是直接对数据库进行操作。 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/9a88bd51-bd74-48... 请参见 当您指示 LINQ to SQL 执行您已描述的查询或对您已操作的数据调用 SubmitChanges() 时,LINQ to SQL 会 用数据库的语言与数据库通信。 以下内容代表使用您已创建的对象模型的典型步骤。 1. 创建查询以从数据库中检索信息。 有关更多信息,请参见 LINQ to SQL 中的查询概念和查询示例 (LINQ to SQL)。 2. 重写 Insert、Update 和 Delete 的默认行为。 这一步是可选的。有关更多信息,请参见自定义插入、更新和删除操作 (LINQ to SQL)。 3. 设置适当的选项以检测和报告并发冲突。 您可以保留模型用于处理并发冲突的默认值,也可以根据您的需要对其进行更改。有关更多信息,请参见如 何:指定测试哪些成员是否发生并发冲突 (LINQ to SQL) 和如何:指定并发异常的引发时间 (LINQ to SQL)。 4. 建立继承层次结构。 这一步是可选的。有关更多信息,请参见继承支持 (LINQ to SQL)。 5. 提供合适的用户界面。 这一步是可选的,取决于您的应用程序的使用方式。有关更多信息,请参见Object Relational Designer (O/R Designer) 和对象关系设计器(O/R 设计器). 6. 调试并测试应用程序。 有关更多信息,请参见调试支持 (LINQ to SQL)。 概念 入门 (LINQ to SQL) 创建对象模型 (LINQ to SQL) 存储过程 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/9a88bd51-bd74-48... 全部折叠 代码:C# LINQ to SQL 下载示例数据库 (LINQ to SQL) 请参见 发送反馈意见 LINQ to SQL 中的许多示例和演练都使用 Northwind 示例数据库和 SQL Server Express Edition。您可以从 Microsoft 下载网站免费下载这些产品。 下载 Northwind 数据库 下载 SQL Server Express Edition 下载 Management Studio Express 请参见 下载和安装用于 SQL Server 的 Northwind 示例数据库 1. 启动 Internet Explorer。 2. 转到 Northwind and Pubs Sample Databases(Northwind 和 Pubs 示例数据库)网站。 3. 单击“下载”。 4. 在“文件下载”对话框中选择“保存”。 5. 文件下载完成之后,双击 Nwind.exe 文件安装数据库。 默认情况下,此数据库安装在 驱动器:\SQL Server 2000 Sample Databases 中。 SQL Server Express Edition 免费提供,您可以利用应用程序重新发布它。如果您使用的是 Visual Studio,则 专业版和更高版本中包含了 SQL Server Express Edition。 下载和安装 SQL Server Express Edition 1. 启动 Internet Explorer。 2. 转到 Microsoft 下载中心网站。 3. 在“关键字”框中,键入 SQL Server Express。 4. 单击“转到”。 5. 在结果页中,单击指向“Microsoft SQL Server 2005 Express Edition”下载页的链接。 6. 按照网站上的安装说明进行操作。 如果要修改您已下载的数据库,您可以在 Visual Studio 集成开发环境 (IDE) 中从“服务器资源管理器”访问 此数据库,或使用 Microsoft SQL Server Management Studio Express (SSMSE)。 下载 Management Studio Express z 请按照 SSMSE site(SSMSE 站点)上的说明进行操作。 概念 入门 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/ef9d69a1-9461-43f... 全部折叠 代码:C# LINQ to SQL 通过演练学习 (LINQ to SQL) 请参见 发送反馈意见 LINQ to SQL 文档提供了一些演练。本主题介绍一些一般性的演练问题(包括疑难解答),并提供指向用于了解 LINQ to SQL 的一些入门级演练的链接。 入门演练 常规 疑难解答 注意: 此“入门”部分中的演练向您展示了支持 LINQ to SQL 技术的基本代码。在实际操作中,您通常会使用对象关 系设计器和 Windows 窗体项目来实现您的 LINQ to SQL 应用程序。O/R 设计器文档提供了用于此目的的示例和 演练。有关更多信息,请参见Object Relational Designer (O/R Designer) 和对象关系设计器(O/R 设计器). 本节中提供了若干演练。这些演练基于 Northwind 示例数据库,以最不复杂的方式循序渐进地展示了 LINQ to SQL 的功能。 应遵循的典型进展方案如下: 目标 Visual Basic C# 创建一个实体类并执行一个 简单查询。 演练:简单的对象模型和查询 (Visual Basic) (LINQ to SQL) 演练:简单对象模型和查询 (C#) (LINQ to SQL) 添加第二个类并执行一个更 为复杂的查询。 (需要完成前一个演练。) 演练:跨关系进行查询 (Visual Basic) (LINQ to SQL) 演练:跨关系查询 (C#) (LINQ to SQL) 向数据库中添加项、更改并 删除其中的项。 演练:操作数据 (Visual Basic) (LINQ to SQL) 演练:操作数据 (C#) (LINQ to SQL) 使用存储过程。 演练:仅使用存储过程 (Visual Basic) (LINQ to SQL) 演练:仅使用存储过程 (C#) (LINQ to SQL) 以下信息大致介绍了这些演练: z 环境:每个 LINQ to SQL 演练使用 Visual Studio 作为其集成开发环境 (IDE)。 z SQL 引擎:所编写的这些演练需使用 SQL Server Express 来执行。如果您没有 SQL Server Express,可 以免费下载。有关更多信息,请参见下载示例数据库 (LINQ to SQL)。 z LINQ to SQL 演练通常需要 Northwind 示例数据库。有关更多信息,请参见下载示例数据库 (LINQ to SQL)。 z 您在演练中看到的对话框和菜单命令可能会与“帮助”中描述的有所不同,具体取决于您现用的设置或 Visual Studio 版本。若要更改设置,请单击“工具”菜单上的“导入和导出设置”。有关更多信息,请参 见 Visual Studio 设置。 z 对于介绍多层方案的演练,服务器必须位于与开发计算机不同的计算机上,并且您必须具有访问此服务器 的相应权限。 z 通常表示 Northwind 示例数据库中 Orders 表的类名为 [Order]。这种转义是必需的,因为 Order 是 Visual Basic 中的一个关键字。 注意: LINQ to SQL 演练使用文件名作为连接字符串。只需指定文件名即可,这是 LINQ to SQL 为 SQL Server Express 用户提供的便捷之处。始终要注意安全问题。有关更多信息,请参见 LINQ to SQL 中 的安全性。 发生运行时错误的原因可能是您没有足够的权限来访问这些演练中使用的数据库。请参见以下步骤以帮助解决 最常见的此类问题。 登录问题 您的应用程序可能尝试使用数据库不接受的数据库登录名来访问数据库。 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/a8ae2965-6a49-41... 请参见 验证或更改数据库登录名 1. 在 Windows 的“开始”菜单上,依次指向“所有程序”、“Microsoft SQL Server 2005”、“配置工 具”,然后单击“SQL Server 配置管理器”。 2. 在“SQL Server 配置管理器”的左窗格中,单击“SQL Server 2005 服务”。 3. 在右窗格中,右击“SQL Server (SQLEXPRESS)”,然后单击“属性”。 4. 单击“登录”选项卡,验证您尝试登录到服务器的方式。 在大多数情况下,使用“Local System”即可登录。 如果您做了更改,请单击“重新启动”以重新启动此服务。 协议 有时,您的应用程序用来访问数据库的协议可能未设置正确。例如,LINQ to SQL 中的演练需要的“Named Pipes”协议默认是未启用的。 启用 Named Pipes 协议 1. 在“SQL Server 配置管理器”的左窗格中,展开“SQL Server 2005 网络配置”,然后单击 “SQLEXPRESS 的协议”。 2. 在右窗格中,确保“Named Pipes”协议已启用。如果它未启用,请右击“Name Pipes”,然后单击“启 用”。 您将需要停止此服务,再重新启动它。请按下一部分中的步骤操作。 停止并重新启动服务 您必须先停止并重新启动服务,所做的更改才能生效。 停止并重新启动服务 1. 在“SQL Server 配置管理器”的左窗格中,单击“SQL Server 2005 服务”。 2. 在右窗格中,右击“SQL Server (SQLEXPRESS)”,然后单击“停止”。 3. 右击“SQL Server (SQLEXPRESS)”,然后单击“重新启动”。 概念 入门 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/a8ae2965-6a49-41... 全部折叠 代码:C# LINQ to SQL 演练:简单对象模型和查询 (C#) (LINQ to SQL) 请参见 发送反馈意见 本演练提供了复杂性最小的基本端对端 LINQ to SQL 方案。您将创建一个可为示例 Northwind 数据库中的 Customers 表建模的实体类。然后您将创建一个简单查询,用于列出位于伦敦的客户。 本演练在设计上是面向代码的,以帮助说明 vbtecdlinq 概念。一般来说,您会使用对象关系设计 器来创建对象模型。有关更多信息,请参见Object Relational Designer (O/R Designer) 和对象关系设计器(O/R 设计 器). 本演练是使用 Visual C# 开发设置编写的。 先决条件 概述 创建 LINQ to SQL 解决方案 添加 LINQ 引用和指令 注意: 对于在以下说明中使用的某些 Visual Studio 用户界面元素,您的计算机可能会显示不同的名称或位置。这些元 素取决于您所使用的 Visual Studio 版本和您所使用的设置。有关更多信息,请参见 Visual Studio 设置。 z 本演练使用专用文件夹(“c:\linqtest5”)来保存文件。请在开始本演练前创建此文件夹。 z 此演练需要 Northwind 示例数据库。如果您的开发计算机上没有此数据库,您可以从 Microsoft 下载网站 下载它。有关说明,请参见下载示例数据库 (LINQ to SQL)。下载此数据库后,请将文件复制到 c:\linqtest5 文件夹。 本演练由六项主要任务组成: z 在 Visual Studio 中创建 LINQ to SQL 解决方案。 z 将类映射到数据库表。 z 在类上指定属性,用于表示数据库列。 z 指定与 Northwind 数据库的连接。 z 创建针对该数据库运行的简单查询。 z 执行查询并观察结果。 此任务为第一项任务,在此任务中,您要创建一个 Visual Studio 解决方案,此解决方案包含生成和运行 LINQ to SQL 项目所必需的引用。 创建 LINQ to SQL 解决方案 1. 在 Visual Studio 的“文件”菜单上指向“新建”,然后单击“项目”。 2. 在“新建项目”对话框的“项目类型”窗格中,单击“Visual C#”。 3. 在“模板”窗格中,单击“控制台应用程序”。 4. 在“名称”框中键入“LinqConsoleApp”。 5. 在“位置”框中,确认要用于存储项目文件的位置。 6. 单击“确定”。 本演练用到默认情况下您的项目中可能未安装的程序集。如果在您的项目中未将 System.Data.Linq 作为引用 列出(在“解决方案资源管理器”中展开“引用”节点),请按照以下步骤中的说明添加它。 添加 System.Data.Linq 1. 在“解决方案资源管理器”中,右击“引用”,然后单击“添加引用”。 2. 在“添加引用”对话框中,依次单击“.NET”、System.Data.Linq 程序集和“确定”。 此程序集即被添加到项目中。 3. 在“Program.cs”的顶部添加以下指令: C# 复制代码 页码,1/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/419961cc-92d6-45f... 将类映射到数据库表 在类中指定用于表示数据库列的属性 指定与 Northwind 数据库的连接 using System.Data.Linq; using System.Data.Linq.Mapping; 在此步骤中,您将创建一个类,并将其映射到数据库表。这样的类称为“实体类”。请注意,只需添加 TableAttribute 属性即可实现映射。Name 属性指定数据库中的表的名称。 创建一个实体类并将其映射到数据库表 z 将下面的代码键入或粘贴到 Program.cs 中紧靠在 Program 类声明上方的位置: C# 复制代码 [Table(Name = "Customers")] public class Customer { } 您将在此步骤中完成多项任务。 z 使用 ColumnAttribute 属性在实体类上指定 CustomerID 和 City 属性,用于表示数据库表中的列。 z 指定 CustomerID 属性,用于表示数据库中的主键列。 z 指定 _CustomerID 和 _City 字段作为私有存储区。然后 LINQ to SQL 可以直接存储和检索值,而不是 使用可能包含业务逻辑的公共访问器。 表示两个数据库列的特性 z 将下面的代码键入或粘贴到 Program.cs 中 Customer 类的大括号内。 C# 复制代码 private string _CustomerID; [Column(IsPrimaryKey=true, Storage="_CustomerID")] public string CustomerID { get { return this._CustomerID; } set { this._CustomerID = value; } } private string _City; [Column(Storage="_City")] public string City { get { return this._City; } set { this._City=value; } } } } 在此步骤中,使用 DataContext 对象在基于代码的数据结构与数据库本身之间建立连接。DataContext 是用于 从数据库检索对象和提交更改的主要通道。 您还可以针对数据库中的 Customers 表声明 Table 作为查询的类型化逻辑表。您将在后续步骤 页码,2/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/419961cc-92d6-45f... 创建简单查询 执行查询 中创建和执行这些查询。 指定数据库连接 z 将下面的代码键入或粘贴到 Main 方法中。 请注意,假定 northwnd.mdf 文件位于 linqtest5 文件夹中。有关更多信息,请参见本演练前面介绍的 “先决条件”一节。 C# 复制代码 // Use a connection string. DataContext db = new DataContext (@"c:\linqtest5\northwnd.mdf"); // Get a typed table to run queries. Table Customers = db.GetTable(); 在此步骤中,您将创建一个查询,用于确定数据库的 Customers 表中有哪些客户位于伦敦。此步骤中的查询 代码只描述查询,而不执行查询。这种方法称为“延迟执行”。有关更多信息,请参见 LINQ 查询简介。 您还将生成一个日志输出,用于显示 LINQ to SQL 生成的 SQL 命令。此日志记录功能(使用 Log)在调试过 程中可以提供帮助,并有助于确定发送到数据库的命令是否准确地表示您的查询。 创建简单查询 z 将下面的代码键入或粘贴到 Table 声明后面的 Main 方法中。 C# 复制代码 // Attach the log to show generated SQL. db.Log = Console.Out; // Query for customers in London. IQueryable custQuery = from cust in Customers where cust.City == "London" select cust; 在此步骤中,您将实际执行查询。您在前面步骤中创建的查询表达式只有在需要结果时才会进行计算。当您开 始 foreach 迭代时,将会针对数据库执行 SQL 命令,并将对象具体化。 执行查询 1. 将下面的代码键入或粘贴到 Main 方法的末尾(在查询说明之后)。 2. 按 F5 调试该应用程序。 控制台窗口中的查询结果应显示如下: ID=AROUT, City=London ID=BSBEV, City=London ID=CONSH, City=London ID=EASTC, City=London ID=NORTS, City=London ID=SEVES, City=London 3. 在控制台窗口中按 Enter 以关闭应用程序。 C# 复制代码 foreach (Customer cust in custQuery) { Console.WriteLine("ID={0}, City={1}", cust.CustomerID, cust.City); } // Prevent console window from closing. Console.ReadLine(); 注意: 如果您的应用程序产生运行时错误,请参见通过演练学习 (LINQ to SQL) 中的“疑难解答”一节。 页码,3/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/419961cc-92d6-45f... 后续步骤 请参见 演练:跨关系查询 (C#) (LINQ to SQL) 主题会接着此演练进行介绍。“跨关系查询”演练演示 LINQ to SQL 如何跨表查询,如同关系数据库中的联接。 如果您希望进行“跨关系查询”演练,请务必保存您刚完成演练的解决方案,这是“跨关系查询”演练的前提 条件。 概念 通过演练学习 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,4/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/419961cc-92d6-45f... 全部折叠 代码:C# LINQ to SQL 演练:跨关系查询 (C#) (LINQ to SQL) 请参见 发送反馈意见 本演练演示如何使用 LINQ to SQL 关联来表示数据库中的外键关系。 本演练是使用 Visual C# 开发设置编写的。 先决条件 概述 跨表映射关系 对 Customer 类进行批注 注意: 对于在以下说明中使用的某些 Visual Studio 用户界面元素,您的计算机可能会显示不同的名称或位置。这些元 素取决于您所使用的 Visual Studio 版本和您所使用的设置。有关更多信息,请参见 Visual Studio 设置。 您必须已完成演练:简单对象模型和查询 (C#) (LINQ to SQL)。本演练建立在该演练基础之上,包括在 c:\linqtest5 中须存在 northwnd.mdf 文件。 本演练由三项主要任务组成: z 添加一个实体类以表示 Northwind 示例数据库中的 Orders 表。 z 向 Customer 类补充一些批注,以增强 Customer 和 Order 类之间的关系。 z 创建并运行查询以测试能否通过使用 Customer 类获取 Order 信息。 在 Customer 类定义的后面,创建包含如下代码的 Order 实体类定义,这些代码表示 Order.Customer 作为 外键与 Customer.CustomerID 相关。 添加 Order 实体类 z 在 Customer 类后面键入或粘贴如下代码: C# 复制代码 [Table(Name = "Orders")] public class Order { private int _OrderID = 0; private string _CustomerID; private EntityRef _Customer; public Order() { this._Customer = new EntityRef(); } [Column(Storage = "_OrderID", DbType = "Int NOT NULL IDENTITY", IsPrimaryKey = true, IsDbGenerated = true)] public int OrderID { get { return this._OrderID; } // No need to specify a setter because IsDBGenerated is // true. } [Column(Storage = "_CustomerID", DbType = "NChar(5)")] public string CustomerID { get { return this._CustomerID; } set { this._CustomerID = value; } } [Association(Storage = "_Customer", ThisKey = "CustomerID")] public Customer Customer { get { return this._Customer.Entity; } set { this._Customer.Entity = value; } } } 页码,1/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/552abeb1-18f2-4e9... 跨 Customer-Order 关系创建并运行查询 创建数据库的强类型化视图 在此步骤中,您要对 Customer 类进行批注,以指示它与 Order 类的关系。(这种添加批注的操作并非绝对 必需的,因为定义任一方向上的关系都足以满足创建链接的需要。但添加此批注确实便于您在任一方向上定位 对象。) 对 Customer 类进行批注 z 将下面的代码键入或粘贴到 Customer 类中: C# 复制代码 private EntitySet _Orders; public Customer() { this._Orders = new EntitySet(); } [Association(Storage = "_Orders", OtherKey = "CustomerID")] public EntitySet Orders { get { return this._Orders; } set { this._Orders.Assign(value); } } 现在您可以直接从 Customer 对象访问 Order 对象,或反过来进行访问。您不需要在客户和订单之间具有显 式联接。 使用 Customer 对象访问 Order 对象 1. 通过将下面的代码键入或粘贴到 Main 方法中修改此方法: 2. 按 F5 调试应用程序。 3. 在控制台窗口中按 Enter,以停止调试。 C# 复制代码 // Query for customers who have placed orders. var custQuery = from cust in Customers where cust.Orders.Any() select cust; foreach (var custObj in custQuery) { Console.WriteLine("ID={0}, Qty={1}", custObj.CustomerID, custObj.Orders.Count); } 注意: 您可以通过注释掉 db.Log = Console.Out; 来消除控制台窗口中的 SQL 代码。 从数据库的强类型化视图着手要容易得多。通过将 DataContext 对象强类型化,您无需调用 GetTable。当您 使用强类型化的 DataContext 对象时,您可以在所有查询中使用强类型化表。 在以下步骤中,您将创建 Customers 作为映射到数据库中的 Customers 表的强类型化表。 对 DataContext 对象进行强类型化 1. 将下面的代码添加到 Customer 类声明的上方。 C# 复制代码 public class Northwind : DataContext { // Table abstracts database details per table/data type. public Table Customers; public Table Orders; public Northwind(string connection) : base(connection) { } } 页码,2/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/552abeb1-18f2-4e9... 后续步骤 请参见 2. 将 Main 方法修改为使用强类型化的 DataContext,如下所示: 3. 按 F5 调试应用程序。 控制台窗口输出如下: ID=WHITC 4. 在控制台窗口中按 Enter,以停止调试。 C# 复制代码 // Use a connection string. Northwind db = new Northwind(@"C:\linqtest5\northwnd.mdf"); // Query for customers from Seattle. var custQuery = from cust in db.Customers where cust.City == "Seattle" select cust; foreach (var custObj in custQuery) { Console.WriteLine("ID={0}", custObj.CustomerID); } // Freeze the console window. Console.ReadLine(); 下一演练(演练:操作数据 (C#) (LINQ to SQL))演示如何操作数据。该演练不要求您保存本系列中已经完成 的两个演练的结果。 概念 通过演练学习 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/552abeb1-18f2-4e9... 全部折叠 代码:C# LINQ to SQL 演练:操作数据 (C#) (LINQ to SQL) 请参见 发送反馈意见 本演练提供了用于在数据库中添加、修改和删除数据的基本端对端 LINQ to SQL 方案。您将使用 Northwind 示例数 据库的一个副本来添加一位客户,更改该客户的姓名,然后删除一个订单。 本演练是使用 Visual C# 开发设置编写的。 先决条件 概述 创建 LINQ to SQL 解决方案 添加 LINQ 引用和指令 注意: 对于在以下说明中使用的某些 Visual Studio 用户界面元素,您的计算机可能会显示不同的名称或位置。这些元 素取决于您所使用的 Visual Studio 版本和您所使用的设置。有关更多信息,请参见 Visual Studio 设置。 本演练需要如下内容: z 本演练使用专用文件夹(“c:\linqtest6”)来保存文件。请在开始本演练前创建此文件夹。 z Northwind 示例数据库。 如果您的开发计算机上没有此数据库,您可以从 Microsoft 下载网站下载它。有关说明,请参见下载示例 数据库 (LINQ to SQL)。下载此数据库后,请将 northwnd.mdf 文件复制到 c:\linqtest6 文件夹。 z 从 Northwind 数据库生成的 C# 代码文件。 可以使用对象关系设计器或 SQLMetal 工具生成此文件。本演练是通过使用 SQLMetal 工具以及如下命令 行编写的: sqlmetal /code:"c:\linqtest6\northwind.cs" /language:csharp "C:\linqtest6 \northwnd.mdf" /pluralize 有关更多信息,请参见代码生成工具 (SqlMetal.exe)。 本演练由六项主要任务组成: z 在 Visual Studio 中创建 LINQ to SQL 解决方案。 z 向项目添加数据库代码文件。 z 创建新的客户对象。 z 修改客户的联系人姓名。 z 删除订单。 z 将这些更改提交至 Northwind 数据库。 此任务为第一项任务,在此任务中,您要创建一个 Visual Studio 解决方案,此解决方案包含生成和运行 LINQ to SQL 项目所必需的引用。 创建 LINQ to SQL 解决方案 1. 在 Visual Studio 的“文件”菜单上指向“新建”,然后单击“项目”。 2. 在“新建项目”对话框中的“项目类型”窗格中,单击“Visual C#”。 3. 在“模板”窗格中,单击“控制台应用程序”。 4. 在“名称”框中键入“LinqDataManipulationApp”。 5. 在“位置”框中,确认要用于存储项目文件的位置。 6. 单击“确定”。 本演练用到默认情况下您的项目中可能未安装的程序集。如果在您的项目中未将 System.Data.Linq 作为引用 列出,请按照以下步骤中的说明添加它。 添加 System.Data.Linq 1. 在“解决方案资源管理器”中,右击“引用”,然后单击“添加引用”。 2. 在“添加引用”对话框中,依次单击“.NET”、System.Data.Linq 程序集,再单击“确定”。 页码,1/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/24adfbe0-0ad6-449... 将 Northwind 代码文件添加到项目 设置数据库连接 创建新实体 此程序集即被添加到项目中。 3. 在 Program.cs 的顶部添加以下指令: C# 复制代码 using System.Data.Linq; using System.Data.Linq.Mapping; 以下这些步骤假定您已使用 SQLMetal 工具从 Northwind 示例数据库生成代码文件。有关更多信息,请参见本 演练前面部分的“先决条件”一节。 将 northwind 代码文件添加到项目 1. 在“项目”菜单上单击“添加现有项”。 2. 在“添加现有项”对话框中,导航到 c:\linqtest6\northwind.cs,然后单击“添加”。 northwind.cs 文件即被添加到项目中。 首先,测试与数据库的连接。特别要注意,数据库 Northwnd 不含 i 字符。如果在后面的步骤中产生错误,请 检查 northwind.cs 文件以确定 Northwind 分部类的拼法。 设置并测试数据库连接 1. 将下面的代码键入或粘贴到 Program 类的 Main 方法中: 2. 按 F5 在此点测试应用程序。 “控制台”窗口即会打开。 您可以通过如下方法关闭应用程序:在“控制台”窗口中按 Enter,或在 Visual Studio 的“调试”菜单 上单击“停止调试”。 C# 复制代码 // Use the following connection string. Northwnd db = new Northwnd(@"c:\linqtest6\northwnd.mdf"); // Keep the console window open after activity stops. Console.ReadLine(); 创建新实体很简单。可以使用 new 关键字创建对象(如 Customer)。 在本节及后续各节中,您将只对本地缓存进行更改。如果您不调用 SubmitChanges,则不会向数据库发送任 何更改,一直到本演练结束都是如此。 添加新的 Customer 实体对象 1. 通过在 Main 方法中的 Console.ReadLine(); 前添加如下代码,创建一个新的 Customer: 2. 按 F5 调试解决方案。 3. 在“控制台”窗口中按 Enter 可停止调试并继续本演练。 C# 复制代码 // Create the new Customer object. Customer newCust = new Customer(); newCust.CompanyName = "AdventureWorks Cafe"; newCust.CustomerID = "ADVCA"; // Add the customer to the Customers table. db.Customers.InsertOnSubmit(newCust); Console.WriteLine("\nCustomers matching CA before insert"); foreach (var c in db.Customers.Where(cust => cust.CustomerID.Contains("CA"))) { Console.WriteLine("{0}, {1}, {2}", c.CustomerID, c.CompanyName, c.Orders.Count); } 页码,2/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/24adfbe0-0ad6-449... 更新实体 删除实体 将更改提交到数据库 在以下步骤中,您将检索一个 Customer 对象并修改其属性之一。 更改客户的姓名 z 将下面的代码添加到 Console.ReadLine(); 上方: C# 复制代码 // Query for specific customer. // First() returns one object rather than a collection. var existingCust = (from c in db.Customers where c.CustomerID == "ALFKI" select c) .First(); // Change the contact name of the customer. existingCust.ContactName = "New Contact"; 使用同一客户对象,您可以删除第一个订单。 下面的代码演示如何切断行与行之间的关系,以及如何从数据库中删除行。请将下面的代码添加到 Console.ReadLine 前面,以了解如何可以删除对象: 删除行 z 将下面的代码添加到 Console.ReadLine(); 上方紧靠它的位置: C# 复制代码 // Access the first element in the Orders collection. Order ord0 = existingCust.Orders[0]; // Access the first element in the OrderDetails collection. OrderDetail detail0 = ord0.OrderDetails[0]; // Display the order to be deleted. Console.WriteLine ("The Order Detail to be deleted is: OrderID = {0}, ProductID = {1}", detail0.OrderID, detail0.ProductID); // Mark the Order Detail row for deletion from the database. db.OrderDetails.DeleteOnSubmit(detail0); 创建、更新和删除对象所需执行的最后一步是将更改实际提交到数据库。如不执行这一步,您所做的更改将仅 限本地,不会出现在查询结果中。 将更改提交到数据库 1. 将下面的代码插入到 Console.ReadLine 上方紧靠它的位置: 2. 将下面的代码插入到 SubmitChanges 后面,以显示提交更改前后的效果: C# 复制代码 db.SubmitChanges(); C# 复制代码 Console.WriteLine("\nCustomers matching CA after update"); foreach (var c in db.Customers.Where(cust => cust.CustomerID.Contains("CA"))) { Console.WriteLine("{0}, {1}, {2}", c.CustomerID, c.CompanyName, c.Orders.Count); } 页码,3/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/24adfbe0-0ad6-449... 请参见 3. 按 F5 调试解决方案。 4. 在“控制台”窗口中按 Enter,以关闭应用程序。 注意: 通过提交更改添加了新的客户后,您无法再次按原样执行此解决方案。若要再次执行此解决方案,请更改 要添加的客户姓名和客户 ID。 概念 通过演练学习 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,4/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/24adfbe0-0ad6-449... 全部折叠 代码:C# LINQ to SQL 演练:仅使用存储过程 (C#) (LINQ to SQL) 请参见 发送反馈意见 本演练提供了通过仅执行存储过程来访问数据的 LINQ to SQL 基本端对端方案。数据库管理员经常使用此方法来限 制数据存储的访问方式。 出于本演练的需要,您将用到已映射到 Northwind 示例数据库中存储过程的两个方法:CustOrdersDetail 和 CustOrderHist。此映射发生在运行 SqlMetal 命令行工具来生成 C# 文件时。有关更多信息,请参见本演练后面的 “先决条件”一节。 本演练不依赖于对象关系设计器。使用 Visual Studio 的开发人员还可以使用 O/R 设计器来实现存储过程功能。有 关更多信息,请参见Object Relational Designer (O/R Designer) 和对象关系设计器(O/R 设计器). 本演练是使用 Visual C# 开发设置编写的。 先决条件 概述 创建 LINQ to SQL 解决方案 注意: 您还可以在 LINQ to SQL 应用程序中使用存储过程来重写默认行为,尤其是 Create、Update 和 Delete 进程 的默认行为。有关更多信息,请参见自定义插入、更新和删除操作 (LINQ to SQL)。 注意: 对于在以下说明中使用的某些 Visual Studio 用户界面元素,您的计算机可能会显示不同的名称或位置。这些元 素取决于您所使用的 Visual Studio 版本和您所使用的设置。有关更多信息,请参见 Visual Studio 设置。 本演练需要如下内容: z 本演练使用专用文件夹(“c:\linqtest7”)来保存文件。请在开始本演练前创建此文件夹。 z Northwind 示例数据库。 如果您的开发计算机上没有此数据库,您可以从 Microsoft 下载网站下载它。有关说明,请参见下载示例 数据库 (LINQ to SQL)。下载此数据库后,请将 northwnd.mdf 文件复制到 c:\linqtest7 文件夹。 z 从 Northwind 数据库生成的 C# 代码文件。 本演练是通过使用 SqlMetal 工具以及如下命令行编写的: sqlmetal /code:"c:\linqtest7\northwind.cs" /language:csharp "c:\linqtest7 \northwnd.mdf" /sprocs /functions /pluralize 有关更多信息,请参见代码生成工具 (SqlMetal.exe)。 本演练由六项主要任务组成: z 在 Visual Studio 中设置 LINQ to SQL 解决方案。 z 将 System.Data.Linq 程序集添加到项目中。 z 向项目添加数据库代码文件。 z 创建与数据库的连接。 z 设置用户界面。 z 运行和测试应用程序。 此任务为第一项任务,在此任务中,您要创建一个 Visual Studio 解决方案,此解决方案包含生成和运行 LINQ to SQL 项目所必需的引用。 创建 LINQ to SQL 解决方案 1. 在 Visual Studio 的“文件”菜单上指向“新建”,然后单击“项目”。 2. 在“新建项目”对话框中的“项目类型”窗格中,单击“Visual C#”。 3. 在“模板”窗格中,单击“Windows 窗体应用程序”。 4. 在“名称”框中键入“SprocOnlyApp”。 5. 在“位置”框中,确认要用于存储项目文件的位置。 6. 单击“确定”。 Windows 窗体设计器即会打开。 页码,1/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/ecde4bf2-fa4d-425... 添加 LINQ to SQL 程序集引用 将 Northwind 代码文件添加到项目 创建数据库连接 设置用户界面 LINQ to SQL 程序集未包含在标准的 Windows 窗体应用程序模板中。您将需要按照以下步骤中的说明自行添 加此程序集: 添加 System.Data.Linq.dll 1. 在“解决方案资源管理器”中,右击“引用”,然后单击“添加引用”。 2. 在“添加引用”对话框中,依次单击“.NET”、System.Data.Linq 程序集,再单击“确定”。 此程序集即被添加到项目中。 此步骤假定您已使用 SqlMetal 工具从 Northwind 示例数据库生成代码文件。有关更多信息,请参见本演练前 面的“先决条件”一节。 将 northwind 代码文件添加到项目 1. 在“项目”菜单上单击“添加现有项”。 2. 在“添加现有项”对话框中,移动到 c:\linqtest7\northwind.cs,然后单击“添加”。 northwind.cs 文件即被添加到项目中。 在此步骤中,您要定义与 Northwind 示例数据库的连接。本演练使用“c:\linqtest7\northwnd.mdf”作为路径。 创建数据库连接 1. 在“解决方案资源管理器”中右击“Form1.cs”,再单击“查看代码”。 2. 将下面的代码键入到 Form1 类中: C# 复制代码 Northwnd db = new Northwnd(@"c:\linqtest7\northwnd.mdf"); 在此任务中,您要设置一个界面,供用户执行存储过程以访问数据库中的数据之用。在您按照本演练开发的应 用程序中,用户只能使用应用程序中嵌入的存储过程来访问数据库中的数据。 设置用户界面 1. 返回 Windows 窗体设计器(“Form1.cs[Design]”)。 2. 在“视图”菜单上单击“工具箱”。 工具箱即会打开。 3. 从工具箱中将两个按钮、两个文本框和两个标签拖到“Form1”上。 按照附图排列这些控件。扩展“Form1”,让控件更便于显示。 4. 右击“label1”,然后单击“属性”。 5. 将“Text”属性从“label1”更改为“Enter OrderID:”。 6. 对于“label2”,请按相同的方式将“Text”属性从“label2”更改为“Enter CustomerID:”。 7. 按相同的方式,将“button1”的“Text”属性更改为“Order Details”。 8. 将“button2”的“Text”属性更改为“Order History”。 将这些按钮控件加宽,以使所有文本均可见。 注意: 单击“自动隐藏”图钉,以使工具箱在您执行本节中剩余步骤的过程中保持打开。 页码,2/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/ecde4bf2-fa4d-425... 测试应用程序 处理按钮单击 1. 双击“Form1”上的“Order Details”(订单明细)在代码编辑器中打开 button1 事件处理程序。 2. 将如下代码键入到 button1 处理程序中: 3. 现在双击“Form1”上的“button2”,打开 button2 处理程序。 4. 将如下代码键入到 button2 处理程序中: C# 复制代码 // Declare a variable to hold the contents of // textBox1 as an argument for the stored // procedure. string param = textBox1.Text; // Declare a variable to hold the results // returned by the stored procedure. var custquery = db.CustOrdersDetail(Convert.ToInt32(param)); // Execute the stored procedure and display the results. string msg = ""; foreach (CustOrdersDetailResult custOrdersDetail in custquery) { msg = msg + custOrdersDetail.ProductName + "\n"; } if (msg == "") msg = "No results."; MessageBox.Show(msg); // Clear the variables before continuing. param = ""; textBox1.Text = ""; C# 复制代码 // Comments in the code for button2 are the same // as for button1. string param = textBox2.Text; var custquery = db.CustOrderHist(param); string msg = ""; foreach (CustOrderHistResult custOrdHist in custquery) { msg = msg + custOrdHist.ProductName + "\n"; } MessageBox.Show(msg); param = ""; textBox2.Text = ""; 现在,可以开始测试您的应用程序了。请注意,您可对数据存储施加的影响仅限于这两个存储过程能够执行的 操作。这些操作是要根据您输入的所有 orderID 返回相应订单包括的产品,或根据您输入的所有 CustomerID 返回相应客户的产品订购历史记录。 测试应用程序 1. 按 F5 开始调试。 页码,3/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/ecde4bf2-fa4d-425... 后续步骤 请参见 此时将显示 Form1。 2. 在“Enter OrderID”(输入 OrderID)框中,键入“10249”,然后单击“Order Details”(订单明细)。 随即会显示一个消息框,其中列出了 10249 号订单中所包括的产品。 单击“确定”关闭此消息框。 3. 在“Enter CustomerID”(输入 CustomerID)框中,键入 ALFKI,然后单击“Order History”(订单历史 记录)。 随即会显示一个消息框,其中列出了 ALFKI 客户的订单历史记录。 单击“确定”关闭此消息框。 4. 在“Enter OrderID”(输入 OrderID)框中,键入 123,然后单击“Order Details”(订单明细)。 随即会显示一个消息框,其中显示“无结果”。 单击“确定”关闭此消息框。 5. 在“调试”菜单上,单击“停止调试”。 调试会话即会关闭。 6. 如果您已试验完毕,可以单击“文件”菜单上的“关闭项目”,并在看到相应提示时保存您的项目。 您可以通过做一些更改来增强此项目的功能。例如,您可以在列表框中列出可用的存储过程,供用户选择要执 行哪些过程。您还可以将报告的输出以流的方式传输到文本文件。 概念 通过演练学习 (LINQ to SQL) 存储过程 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,4/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/ecde4bf2-fa4d-425... 全部折叠 代码:C# LINQ to SQL 编程指南 (LINQ to SQL) 发送反馈意见 本节包含有关如何创建和使用 LINQ to SQL 对象模型的信息。如果您使用的是 Visual Studio,您还可以使用对象关 系设计器来执行这些任务中的许多任务。有关更多信息,请参见Object Relational Designer (O/R Designer) 和对象关 系设计器(O/R 设计器). 您还可以在 MSDN Library 中搜索特定问题,并且可以参与 LINQ Forum(LINQ 论坛),在这里您可以与专家们详 细讨论更复杂的主题。最后,LINQ to SQL: .NET Language-Integrated Query for Relational Data(LINQ to SQL:关 系数据的 .NET 语言集成查询)白皮书详细介绍了 LINQ to SQL 技术并包含了 Visual Basic 和 C# 代码示例。 本节内容 相关章节 创建对象模型 (LINQ to SQL) 说明如何生成对象模型。 与数据库通信 (LINQ to SQL) 说明如何将 DataContext 对象用作与数据库交互的媒介。 查询数据库 (LINQ to SQL) 说明如何在 LINQ to SQL 中执行查询,并提供了许多示例。 做出和提交数据更改 (LINQ to SQL) 说明如何更改数据库中的数据。 调试支持 (LINQ to SQL) 说明可用于调试 LINQ to SQL 项目的支持。 背景信息 (LINQ to SQL) 包括针对更高级用户的附加项,如并发冲突解决方法、创建新数据库等。 LINQ to SQL 提供指向一些主题的链接,这些主题阐释了 LINQ to SQL 技术并演示了一些功能。 存储过程 (LINQ to SQL) 包含指向一些主题的链接,这些主题演示了如何使用存储过程。 LINQ 简介 提供可帮助您开始了解 LINQ to SQL 的一些资源。 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/ed1012d4-3ff2-487... 全部折叠 代码:C# LINQ to SQL 创建对象模型 (LINQ to SQL) 发送反馈意见 您可以从现有数据库中创建对象模型并以该模型的默认状态使用它。您还可以自定义该模型的许多方面及其行为。 如果您使用的是 Visual Studio,则可以使用对象关系设计器来创建对象模型。有关更多信息,请参见How to: Create LINQ to SQL Classes Mapped to Tables and Views (O/R Designer) 和如何:创建映射到表和视图的 LINQ to SQL 类 (O/R 设计器). 本节内容 相关章节 如何:用 Visual Basic 或 C# 生成对象模型 (LINQ to SQL) 介绍如何使用 SQLMetal 命令行工具。还为 Visual Studio 用户提供了指向对象关系设计器的链接。 如何:将对象模型生成为外部文件 (LINQ to SQL) 介绍如何生成外部映射文件,而不是使用基于属性的映射。 如何:通过修改 DBML 文件生成自定义代码 (LINQ to SQL) 介绍如何从 DBML 元数据文件生成 Visual Basic 或 C# 代码。 如何:验证 DBML 和外部映射文件 (LINQ to SQL) 介绍如何验证您已修改的映射文件(高级)。 如何:使实体可序列化 (LINQ to SQL) 介绍如何添加适当的属性以使实体可序列化。 如何:使用代码编辑器自定义实体类 (LINQ to SQL) 介绍如何使用代码编辑器编写您自己的映射代码,或自定义已自动生成的代码。 LINQ to SQL 对象模型 提供有关 LINQ to SQL 对象模型的详细信息。 使用 LINQ to SQL 的典型步骤 说明在实现 LINQ to SQL 应用程序时应遵循的典型步骤。 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/27afce86-9b1d-45f... 全部折叠 代码:C# LINQ to SQL 如何:用 Visual Basic 或 C# 生成对象模型 (LINQ to SQL) 示例 请参见 发送反馈意见 在 LINQ to SQL 中,采用您自己的编程语言的对象模型映射到关系数据库。有两种工具可用来利用现有数据库的元数据 自动生成 Visual Basic 或 C# 模型。 z 如果您使用的是 Visual Studio,则可以使用对象关系设计器来生成对象模型。O/R 设计器提供了丰富的用户界面来帮 助您生成 LINQ to SQL 对象模型。有关更多信息,请参见Object Relational Designer (O/R Designer) 和对象关系设计 器(O/R 设计器). z SQLMetal 命令行工具。有关更多信息,请参见代码生成工具 (SqlMetal.exe)。 O/R 设计器的文档提供了有关如何使用 O/R 设计器生成 Visual Basic 或 C# 对象模型的示例。有关更多信息,请参见 Object Relational Designer (O/R Designer) 和对象关系设计器(O/R 设计器).以下信息提供了有关如何使用 SQLMetal 命 令行工具的示例。有关更多信息,请参见代码生成工具 (SqlMetal.exe)。 示例 请参见 注意: 如果您没有现有数据库且希望利用对象模型创建一个,则可以使用代码编辑器和 CreateDatabase 来创建对象模 型。有关更多信息,请参见如何:动态创建数据库 (LINQ to SQL)。 下面的示例中显示的 SQLMetal 命令行会生成 Visual Basic 代码作为 Northwind 示例数据库的基于属性的对象模 型。还呈现了存储过程和函数。 下面的示例中显示的 SQLMetal 命令行会生成 C# 代码作为 Northwind 示例数据库的基于属性的对象模型。还呈现 了存储过程和函数,并自动将表名变为复数形式。 复制代码 sqlmetal /code:northwind.vb /language:vb "c:\northwnd.mdf" /sprocs /functions 复制代码 sqlmetal /code:northwind.cs /language:csharp "c:\northwnd.mdf" /sprocs /functions /pluralize 概念 编程指南 (LINQ to SQL) LINQ to SQL 对象模型 通过演练学习 (LINQ to SQL) 如何:使用代码编辑器自定义实体类 (LINQ to SQL) 基于属性的映射 (LINQ to SQL) 代码生成工具 (SqlMetal.exe) 外部映射引用 (LINQ to SQL) 下载示例数据库 (LINQ to SQL) 创建对象模型 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/a0c73b33-5650-42... 全部折叠 代码:C# LINQ to SQL 如何:将对象模型生成为外部文件 (LINQ to SQL) 示例 请参见 发送反馈意见 作为基于属性的映射的替代方法,可以使用 SQLMetal 命令行工具将您的对象模型生成为外部 XML 文件。有关更多信息,请参见代码生成工具 (SqlMetal.exe)。使用外部 XML 映射文件可以降低代码中的混乱程度。您还可以通过修改该外部文件来更改行为,而无需重新编译应用程序的 二进制文件。有关更多信息,请参见外部映射引用 (LINQ to SQL)。 示例 请参见 注意: 对象关系设计器不支持生成外部映射文件。有关更多信息,请参见Object Relational Designer (O/R Designer) 和对象关系设计器(O/R 设计器). 下面的命令从 Northwind 示例数据库生成一个外部映射文件。 下面的内容摘自一个外部映射文件,用于演示 Northwind 示例数据库中的 Customers 表的映射。这段摘录内容是通过使用 /map 选项执行 SQLMetal 而生成的。 复制代码 sqlmetal /server:myserver /database:northwind /map:externalfile.xml 复制代码
概念 创建对象模型 (LINQ to SQL) 外部映射引用 (LINQ to SQL) 如何:用 Visual Basic 或 C# 生成对象模型 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/2496fa06-3df4-4ec... 全部折叠 代码:C# LINQ to SQL 如何:通过修改 DBML 文件生成自定义代码 (LINQ to SQL) 示例 请参见 发送反馈意见 可以从数据库标记语言 (.dbml) 元数据文件生成 Visual Basic 或 C# 源代码。此方法提供了一个在生成应用程序映 射代码前自定义默认 .dbml 文件的机会。这是一项高级功能。 此过程中的步骤如下: 1. 生成 .dbml 文件。 2. 使用编辑器修改此 .dbml 文件。请注意,此 .dbml 文件必须通过 LINQ to SQL .dbml 文件的架构定义 (.xsd) 文件的验证。有关更多信息,请参见 LINQ to SQL 中的代码生成。 3. 生成 Visual Basic 或 C# 源代码。 下面的示例使用 SQLMetal 命令行工具。有关更多信息,请参见代码生成工具 (SqlMetal.exe)。 示例 请参见 下面的代码从 Northwind 示例数据库生成 .dbml 文件。您可以使用数据库的名称或 .mdf 文件的名称作为数 据库元数据的源。 下面的代码从 .dbml 文件生成 Visual Basic 或 C# 源代码文件。 复制代码 sqlmetal /server:myserver /database:northwind /dbml:mymeta.dbml sqlmetal /dbml:mymeta.dbml mydbfile.mdf 复制代码 sqlmetal /namespace:nwind /code:nwind.vb /language:vb DBMLFile.dbml sqlmetal /namespace:nwind /code:nwind.cs /language:csharp DBMLFile.dbml 概念 LINQ to SQL 中的代码生成 代码生成工具 (SqlMetal.exe) 创建对象模型 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/50ad597a-8598-42... 全部折叠 代码:C# LINQ to SQL 如何:验证 DBML 和外部映射文件 (LINQ to SQL) 请参见 发送反馈意见 您修改的外部映射文件和 .dbml 文件必须通过其各自架构定义的验证。本主题为 Visual Studio 用户提供了执行验证 过程的步骤。 验证 .dbml 或 XML 文件 1. 在 Visual Studio 的“文件”菜单上指向“打开”,再单击“文件”。 2. 在“打开文件”对话框中,单击您要验证的 .dbml 或 XML 映射文件。 随即会在“XML 编辑器”中打开该文件。 3. 右击此窗口,然后单击“属性”。 4. 在“属性”窗口中,单击“架构”属性的省略号。 随即会打开“XML 架构”对话框。 5. 请注意符合您需要的相应架构定义。 z DbmlSchema.xsd 是用于验证 .dbml 文件的架构定义。有关更多信息,请参见 LINQ to SQL 中的代码生 成。 z LinqToSqlMapping.xsd 是用于验证外部 XML 映射文件的架构定义。有关更多信息,请参见外部映射引用 (LINQ to SQL)。 6. 在所需架构定义行的“使用”列中,通过单击打开下拉框,然后单击“使用此架构”。 此架构定义文件现在即与您的 DBML 或 XML 映射文件关联。 请确保未选择其他架构定义。 7. 在“视图”菜单上单击“错误列表”。 确定是否已生成了错误、警告或消息。如果未生成,则说明此 XML 文件对此架构定义有效。 提供架构定义的另一种方法 请参见 注意: 对于在以下说明中使用的某些 Visual Studio 用户界面元素,您的计算机可能会显示不同的名称或位置。这些元 素取决于您所使用的 Visual Studio 版本和您所使用的设置。有关更多信息,请参见 Visual Studio 设置。 如果因某种原因导致相应的 .xsd 文件未出现在“XML 架构”对话框中,则您可以从帮助主题中下载此 .xsd 文件。以下步骤可帮助您将所下载的文件保存为 Visual Studio XML 编辑器所需的 Unicode 格式。 从帮助主题中复制架构定义文件 1. 找到包含本主题前面部分所述架构定义的帮助主题。 z 对于 .dbml 文件,请参见 LINQ to SQL 中的代码生成。 z 对于外部映射文件,请参见外部映射引用 (LINQ to SQL)。 2. 单击“复制代码”将代码文件复制到剪贴板。 3. 启动记事本以创建一个新文件。 4. 将剪贴板中的代码粘贴到记事本文件中。 5. 在记事本的“文件”菜单上,单击“另存为”。 6. 在“编码”框中,选择“Unicode”。 7. 在“文件名”框中,创建一个带 .xsd 扩展名的文件名。 重要说明: 这样选择可保证在此文本文件前面加上 Unicode 16 字节顺序标记 (FFFE)。 概念 参考 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/d9ea37f5-0a9e-440... 全部折叠 代码:C# LINQ to SQL 如何:使实体可序列化 (LINQ to SQL) 示例 请参见 发送反馈意见 当您生成代码时,可以使实体可序列化。实体类使用 DataContractAttribute 属性修饰,列使用 DataMemberAttribute 属性修饰。 使用 Visual Studio 的开发人员可以使用对象关系设计器来实现此目的。有关更多信息,请参见Object Relational Designer (O/R Designer) 和对象关系设计器(O/R 设 计器). 如果您使用的是 SQLMetal 命令行工具,请将 /serialization 选项与 unidirectional 参数一起使用。有关更多信息,请参见代码生成工具 (SqlMetal.exe)。 示例 请参见 以下 SQLMetal 命令行会产生包含可序列化实体的文件。 复制代码 sqlmetal /code:nwserializable.vb /language:vb "c:\northwnd.mdf" /sprocs /functions /pluralize /serialization:unidirectional 复制代码 sqlmetal /code:nwserializable.cs /language:csharp "c:\northwnd.mdf" /sprocs /functions /pluralize /serialization:unidirectional 概念 序列化 (LINQ to SQL) 创建对象模型 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/a6c5bf6e-064a-4f7... 全部折叠 代码:C# LINQ to SQL 如何:使用代码编辑器自定义实体类 (LINQ to SQL) 请参见 发送反馈意见 使用 Visual Studio 的开发人员可以使用对象关系设计器来创建或自定义其实体类。有关更多信息,请参见Object Relational Designer (O/R Designer) 和对象关系设计器(O/R 设计器). 您也可以使用 Visual Studio 代码编辑器编写您自己的映射代码或自定义已生成的代码。有关更多信息,请参见基于 属性的映射 (LINQ to SQL)。 本节中的主题说明如何自定义对象模型。 如何:指定数据库名称 (LINQ to SQL) 说明如何使用 Name。 如何:将表表示为类 (LINQ to SQL) 说明如何使用 TableAttribute。 如何:将列表示为类成员 (LINQ to SQL) 说明如何使用 ColumnAttribute。 如何:表示主键 (LINQ to SQL) 说明如何使用 IsPrimaryKey。 如何:映射数据库关系 (LINQ to SQL) 提供使用 AssociationAttribute 属性的示例。 如何:将列表示为由数据库生成的列 (LINQ to SQL) 说明如何使用 IsDbGenerated。 如何:将列表示为时间戳列或版本列 (LINQ to SQL) 说明如何使用 IsVersion。 如何:指定数据库数据类型 (LINQ to SQL) 说明如何使用 DbType。 如何:表示计算所得的列 (LINQ to SQL) 说明如何使用 Expression。 如何:指定私有存储字段 (LINQ to SQL) 说明如何使用 Storage。 如何:将列表示为允许 Null 值 (LINQ to SQL) 说明如何使用 CanBeNull。 如何:映射继承层次结构 (LINQ to SQL) 说明指定继承层次结构所需的映射。 如何:指定并发冲突检查 (LINQ to SQL) 说明如何使用 UpdateCheck。 请参见 概念 代码生成工具 (SqlMetal.exe) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/ec28332f-9f3c-4e0... 全部折叠 代码:C# LINQ to SQL 如何:指定数据库名称 (LINQ to SQL) 请参见 发送反馈意见 使用 DatabaseAttribute 属性 (Attribute) 的 Name 属性 (Property) 可在连接未提供名称时指定数据库的名称。 有关代码示例,请参见 Name。 指定数据库的名称 1. 将 DatabaseAttribute 属性添加到数据库的类声明中。 2. 将 Name 属性 (Property) 添加到 DatabaseAttribute 属性 (Attribute)。 3. 将 Name 属性值设置为您要指定的名称。 请参见 概念 LINQ to SQL 对象模型 如何:使用代码编辑器自定义实体类 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/b80f0fd2-7f75-45f... 全部折叠 代码:C# LINQ to SQL 如何:将表表示为类 (LINQ to SQL) 示例 请参见 发送反馈意见 使用 LINQ to SQL TableAttribute 属性可将类指定为与数据库表关联的实体类。 将类映射到数据库表 z 将 TableAttribute 属性添加到类声明中。 示例 请参见 下面的代码创建作为与 Customers 数据库表关联的实体类的 Customer 类。 如果可以推断出名称,您无需指定 Name 属性。如果您未指定名称,则假定此名称与此属性或字段的名称相 同。 C# 复制代码 [Table(Name = "Customers")] public class Customer { // ... } 概念 LINQ to SQL 对象模型 如何:使用代码编辑器自定义实体类 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/84dda12b-88a2-4c... 全部折叠 代码:C# LINQ to SQL 如何:将列表示为类成员 (LINQ to SQL) 示例 请参见 发送反馈意见 使用 LINQ to SQL ColumnAttribute 属性 (Attribute) 可将字段或属性 (Property) 与数据库列关联。 将字段或属性映射到数据库列 z 将 ColumnAttribute 属性 (Attribute) 添加到相应属性 (Property) 或字段的声明中。 示例 请参见 下面的代码将 Customer 类中的 CustomerID 字段映射到 Customers 数据库表中的 CustomerID 列。 如果可以推断出名称,您无需指定 Name 属性。如果您未指定名称,则假定此名称与此属性或字段的名称相 同。 C# 复制代码 [Table(Name="Customers")] public class customer { [Column(Name="CustomerID")] public string CustomerID; // ... } 概念 LINQ to SQL 对象模型 如何:使用代码编辑器自定义实体类 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/7ab28021-4d15-4d... 全部折叠 代码:C# LINQ to SQL 如何:表示主键 (LINQ to SQL) 请参见 发送反馈意见 使用 ColumnAttribute 属性 (Attribute) 的 LINQ to SQL IsPrimaryKey 属性 (Property) 可指定属性 (Property) 或字 段表示数据库列的主键。 有关代码示例,请参见IsPrimaryKey。 将属性或字段指定为主键 1. 将 IsPrimaryKey 属性 (Property) 添加到 ColumnAttribute 属性 (Attribute)。 2. 将其值指定为 true。 请参见 注意: LINQ to SQL 不支持作为主键的计算所得列。 概念 LINQ to SQL 对象模型 如何:使用代码编辑器自定义实体类 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/63c65289-6539-42... 全部折叠 代码:C# LINQ to SQL 如何:映射数据库关系 (LINQ to SQL) 示例 请参见 发送反馈意见 可以在您的实体类中将始终相同的任何数据关系编码为属性引用。例如,在 Northwind 示例数据库中,由于客户通 常会下订单,因此在模型中客户与其订单之间始终存在关系。 LINQ to SQL 定义了 AssociationAttribute 属性来帮助表示此类关系。此属性与 EntitySet(TEntity) 和 EntityRef (TEntity) 类型一起使用,来表示将作为数据库中的外键关系的内容。有关更多信息,请参见基于属性的映射 (LINQ to SQL) 的“关联属性”一节。 大多数关系都是一对多关系,这一点在本主题后面部分的示例中会有所体现。您还可以按如下方式来表示一对一和 多对多关系: z 一对一:通过向双方添加 EntitySet(TEntity) 来表示此类关系。 例如,假设有一个 Customer-SecurityCode 关系,创建此关系的目的是使得在 Customer 表中找不到客户的 安全码,而只有得到授权的人才能访问此安全码。 z 多对多:在多对多关系中,链接表(也称作联接表)的主键通常由来自其他两个表的外键组合而成。 例如,假设有一个通过使用链接表 EmployeeProject 构成的 Employee-Project 多对多关系。LINQ to SQL 要 求使用以下三个类对这种关系进行模型化: Employee、Project 和 EmployeeProject。在这种情况下,更改 Employee 和 Project 之间的关系似乎需要更新主键 EmployeeProject。但是,这种情况最好的模型化处理方 法是删除现有 EmployeeProject,然后创建新的 EmployeeProject。 示例 注意: 关系数据库中的关系通常模型化成引用其他表中主键的外键值。若要在它们之间导航,应使用关系联接运 算显式关联这两个表。 另一方面,LINQ to SQL 中的对象则是通过使用属性引用或引用集合互相引用的,您可以使用点表示法在这 种引用集合中导航。 在下面的一对多示例中,Customer 类具有一个声明客户与其订单之间的关系的属性。 Orders 属性为 EntitySet(TEntity) 类型。此类型表明这种关系是一对多的(一个客户对多个订单)。OtherKey 属性用来说明 如何实现这种关联,即通过指定相关类中要与此属性比较的属性的名称。在此示例中,比较的是 CustomerID 属性,比较过程就像数据库联接比较该列值一样。 这种情况也可以反过来处理。您可以不使用 Customer 类来说明客户与订单之间的关联,而改用 Order 类。 Order 类使用 EntityRef(TEntity) 类型反向说明与客户的关系,如下面的代码示例所示。 注意: 如果您使用的是 Visual Studio,则可以使用对象关系设计器来创建类之间的关联。 C# 复制代码 [Table(Name = "Customers")] public partial class Customer { [Column(IsPrimaryKey = true)] public string CustomerID; // ... private EntitySet _Orders; [Association(Storage = "_Orders", OtherKey = "CustomerID")] public EntitySet Orders { get { return this._Orders; } set { this._Orders.Assign(value); } } } 注意: EntityRef(TEntity) 类支持延迟加载。有关更多信息,请参见延迟加载与立即加载的比较 (LINQ to SQL)。 C# 复制代码 [Table(Name = "Orders")] public class Order { [Column(IsPrimaryKey = true)] public int OrderID; [Column] public string CustomerID; private EntityRef _Customer; 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/538def39-8399-46f... 请参见 [Association(Storage = "_Customer", ThisKey = "CustomerID")] public Customer Customer { get { return this._Customer.Entity; } set { this._Customer.Entity = value; } } } 概念 如何:使用代码编辑器自定义实体类 (LINQ to SQL) LINQ to SQL 对象模型 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/538def39-8399-46f... 全部折叠 代码:C# LINQ to SQL 如何:将列表示为由数据库生成的列 (LINQ to SQL) 请参见 发送反馈意见 使用 ColumnAttribute 属性 (Attribute) 的 LINQ to SQL IsDbGenerated 属性 (Property) 可指定字段或属性表示数据 库生成的列。 有关代码示例,请参见IsDbGenerated。 指定字段或属性表示数据库生成的列 1. 将 IsDbGenerated 属性 (Property) 添加到 ColumnAttribute 属性 (Attribute)。 2. 将此属性 (Property) 的值设置为 true。 请参见 概念 LINQ to SQL 对象模型 如何:使用代码编辑器自定义实体类 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/6524b8a6-e5d2-4a... 全部折叠 代码:C# LINQ to SQL 如何:将列表示为时间戳列或版本列 (LINQ to SQL) 请参见 发送反馈意见 使用 ColumnAttribute 属性 (Attribute) 的 LINQ to SQL IsVersion 属性 (Property) 可将字段或属性指定为表示保存 数据库时间戳或版本号的数据库列。 有关代码示例,请参见IsVersion。 将字段或属性指定为表示时间戳列或版本列 1. 将 IsVersion 属性 (Property) 添加到 ColumnAttribute 属性 (Attribute)。 2. 将 IsVersion 属性值设置为 true。 请参见 概念 LINQ to SQL 对象模型 如何:指定测试哪些成员是否发生并发冲突 (LINQ to SQL) 如何:使用代码编辑器自定义实体类 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/5afd5ce8-1d20-4bc... 全部折叠 代码:C# LINQ to SQL 如何:指定数据库数据类型 (LINQ to SQL) 请参见 发送反馈意见 使用 ColumnAttribute 属性 (Attribute) 的 LINQ to SQL DbType 属性 (Property) 可指定定义 T-SQL 表声明中列的确 切文本。 仅当您打算使用 CreateDatabase 来创建数据库实例时,才必须指定 DbType 属性。 有关代码示例,请参见DbType。 指定用于定义 T-SQL 表中数据类型的文本 1. 将 DbType 属性 (Property) 添加到 ColumnAttribute 属性 (Attribute)。 2. 将 DbType 属性的值设置为 T-SQL 使用的确切文本。 请参见 概念 LINQ to SQL 对象模型 如何:使用代码编辑器自定义实体类 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/2228fdad-7e6a-4b1... 全部折叠 代码:C# LINQ to SQL 如何:表示计算所得的列 (LINQ to SQL) 请参见 发送反馈意见 使用 ColumnAttribute 属性 (Attribute) 的 LINQ to SQL Expression 属性 (Property) 可表示其内容为计算结果的列。 有关代码示例,请参见Expression。 表示计算所得的列 1. 将 Expression 属性 (Property) 添加到 ColumnAttribute 属性 (Attribute)。 2. 将公式的字符串表示形式赋给 Expression 属性。 请参见 注意: LINQ to SQL 不支持作为主键的计算所得列。 概念 LINQ to SQL 对象模型 如何:使用代码编辑器自定义实体类 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/4025f1fd-9dfa-46c... 全部折叠 代码:C# LINQ to SQL 如何:指定私有存储字段 (LINQ to SQL) 请参见 发送反馈意见 使用 DataAttribute 属性 (Attribute) 的 LINQ to SQL Storage 属性 (Property) 可指定基础存储字段的名称。 有关代码示例,请参见Storage。 指定基础存储字段的名称 1. 将 Storage 属性 (Property) 添加到 ColumnAttribute 属性 (Attribute)。 2. 指定字段的名称作为 Storage 属性的值。 请参见 概念 LINQ to SQL 对象模型 如何:使用代码编辑器自定义实体类 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/5a40e816-cc6e-43a... 全部折叠 代码:C# LINQ to SQL 如何:将列表示为允许 Null 值 (LINQ to SQL) 请参见 发送反馈意见 使用 ColumnAttribute 属性 (Attribute) 的 LINQ to SQL CanBeNull 属性 (Property) 可指定关联的数据库列可以保存 null 值。 有关代码示例,请参见CanBeNull。 将列指定为允许 null 值 1. 将 CanBeNull 属性 (Property) 添加到 ColumnAttribute 属性 (Attribute)。 2. 将 CanBeNull 属性值设置为 true。 请参见 概念 LINQ to SQL 对象模型 如何:使用代码编辑器自定义实体类 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/ebb71a37-1f4c-4fa... 全部折叠 代码:C# LINQ to SQL 如何:映射继承层次结构 (LINQ to SQL) 示例 请参见 发送反馈意见 若要在 LINQ 中执行继承映射,您必须按以下步骤中的说明在继承层次结构的根类中指定属性 (Attribute) 和属性 (Attribute) 的属性 (Property)。使用 Visual Studio 的开发人员可以使用对象关系设计器来映射继承层次结构。 映射继承层次结构 1. 向根类添加 TableAttribute 属性。 2. 为层次结构中的每个类添加 InheritanceMappingAttribute 属性,同样是添加到根类中。 3. 为每个 InheritanceMappingAttribute 属性 (Attribute),定义一个 Code 属性 (Property)。 此属性 (Property) 保存一个值,该值显示在数据库表的 IsDiscriminator 列中,用来指示该行数据所属的类或 子类。 4. 为每个 InheritanceMappingAttribute 属性 (Attribute),也添加一个 Type 属性 (Property)。 此属性 (Property) 保存一个值,此值指定键值所表示的类或子类。 5. 仅在其中一个 InheritanceMappingAttribute 属性 (Attribute) 上,添加一个 IsDefault 属性 (Property)。 此属性 (Property) 用来在数据库表中的鉴别器值在继承映射中不与任何 Code 值匹配时指定回退映射。 6. 为 ColumnAttribute 属性 (Attribute) 添加一个 IsDiscriminator 属性 (Property)。 此属性 (Property) 表示这是保存 Code 值的列。 示例 注意: 子类中不需要具有特殊属性 (Attribute) 或属性 (Property)。请特别注意,子类不具有 TableAttribute 属性。 在下面的代码示例中,Vehicle 定义为根类,并且已执行前面的步骤来说明 LINQ 的层次结构。下图显示了这 些关系。 注意: 如果您使用的是 Visual Studio,则可以使用对象关系设计器来配置继承。 C# 复制代码 [Table] [InheritanceMapping(Code = "C", Type = typeof(Car))] [InheritanceMapping(Code = "T", Type = typeof(Truck))] [InheritanceMapping(Code = "V", Type = typeof(Vehicle), IsDefault = true)] public class Vehicle { [Column(IsDiscriminator = true)] public string DiscKey; [Column(IsPrimaryKey = true)] public string VIN; [Column] public string MfgPlant; 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/b27c779b-9355-4d... 请参见 } public class Car : Vehicle { [Column] public int TrimCode; [Column] public string ModelName; } public class Truck : Vehicle { [Column] public int Tonnage; [Column] public int Axles; } 概念 继承支持 (LINQ to SQL) 如何:使用代码编辑器自定义实体类 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/b27c779b-9355-4d... 全部折叠 代码:C# LINQ to SQL 如何:指定并发冲突检查 (LINQ to SQL) 示例 请参见 发送反馈意见 您可以指定在您调用 SubmitChanges 时要检查数据库的哪些列是否发生并发冲突。有关更多信息,请参见如何:指 定测试哪些成员是否发生并发冲突 (LINQ to SQL)。 示例 请参见 下面的代码指定在更新检查期间永远都不应该测试 HomePage 成员。有关更多信息,请参见 UpdateCheck。 C# 复制代码 [Column(Storage="_HomePage", DbType="NText", UpdateCheck=UpdateCheck.Never)] public string HomePage { get { return this._HomePage; } set { if ((this._HomePage != value)) { this.OnHomePageChanging(value); this.SendPropertyChanging(); this._HomePage = value; this.SendPropertyChanged("HomePage"); this.OnHomePageChanged(); } } } 概念 LINQ to SQL 对象模型 如何:使用代码编辑器自定义实体类 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/c2547fcb-58eb-437... 全部折叠 代码:C# LINQ to SQL 与数据库通信 (LINQ to SQL) 请参见 发送反馈意见 本节中的主题介绍有关如何建立和维护与数据库的通信的一些基本方面。 本节内容 请参见 如何:连接到数据库 (LINQ to SQL) 介绍如何使用 DataContext 类连接到数据库。 如何:直接执行 SQL 命令 (LINQ to SQL) 介绍如何能够使用 ExecuteCommand 来发送 SQL 语言命令。 如何:重用 ADO.NET 命令和 DataContext 之间的连接 (LINQ to SQL) 提供一些示例,演示如何使用 LINQ to SQL 应用程序中的现有 ADO.NET 连接。 概念 编程指南 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/659d9817-bcaa-45... 全部折叠 代码:C# LINQ to SQL 如何:连接到数据库 (LINQ to SQL) 示例 请参见 发送反馈意见 DataContext 是用来连接到数据库、从中检索对象以及将更改提交回数据库的主要渠道。使用 DataContext 时就像 使用 ADO.NET SqlConnection 一样。事实上,DataContext 是用您提供的连接或连接字符串初始化的。有关更多信 息,请参见 DataContext 方法(O/R 设计器)。 DataContext 的用途是将您对对象的请求转换成要对数据库执行的 SQL 查询,然后将查询结果汇编成对象。 DataContext 通过实现与标准查询运算符(如 Where 和 Select)相同的运算符模式来实现 语言集成查询 (LINQ)。 示例 请参见 安全说明: 维护安全连接最为重要。有关更多信息,请参见 LINQ to SQL 中的安全性。 在下面的示例中,使用 DataContext 连接到 Northwind 示例数据库并检索所在城市为伦敦的客户行。 每个数据库表表示为一个可借助 GetTable 方法(通过使用实体类来标识它)使用的 Table 集合。 最佳的做法是声明一个强类型化的 DataContext,而不是依靠基本 DataContext 类和 GetTable 方法。强类型 化的 DataContext 将所有 Table 集合声明为上下文的成员,如下例中所示。 然后您就可以更加简单地将对来自伦敦的客户的查询表达成: C# 复制代码 // DataContext takes a connection string. DataContext db = new DataContext(@"c:\Northwnd.mdf"); // Get a typed table to run queries. Table Customers = db.GetTable(); // Query for customers from London. var query = from cust in Customers where cust.City == "London" select cust; foreach (var cust in query) Console.WriteLine("id = {0}, City = {1}", cust.CustomerID, cust.City); C# 复制代码 public partial class Northwind : DataContext { public Table Customers; public Table Orders; public Northwind(string connection) : base(connection) { } } C# 复制代码 Northwnd db = new Northwnd(@"c:\Northwnd.mdf"); var query = from cust in db.Customers where cust.City == "London" select cust; foreach (var cust in query) Console.WriteLine("id = {0}, City = {1}", cust.CustomerID, cust.City); 概念 与数据库通信 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/c33d74b3-530d-42... 全部折叠 代码:C# LINQ to SQL 如何:直接执行 SQL 命令 (LINQ to SQL) 示例 请参见 发送反馈意见 采用 DataContext 连接时,可以使用 ExecuteCommand 来执行不返回对象的 SQL 命令。 示例 请参见 下面的示例会导致 SQL Server 将 UnitPrice 增加 1.00。 C# 复制代码 db.ExecuteCommand("UPDATE Products SET UnitPrice = UnitPrice + 1.00"); 概念 如何:直接执行 SQL 查询 (LINQ to SQL) 与数据库通信 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/04671bb0-40c0-44... 全部折叠 代码:C# LINQ to SQL 如何:重用 ADO.NET 命令和 DataContext 之间的连接 (LINQ to SQL) 示例 请参见 发送反馈意见 由于 LINQ to SQL 是 ADO.NET 系列技术的一部分并基于 ADO.NET 提供的服务,因此您可以重用 ADO.NET 命令和 DataContext 之间的连接。 示例 请参见 下面的代码说明了如何重用 ADO.NET 命令和 DataContext 之间的同一连接。 C# 复制代码 string connString = @"Data Source=.\SQLEXPRESS;AttachDbFilename=c:\northwind.mdf; Integrated Security=True; Connect Timeout=30; User Instance=True"; SqlConnection nwindConn = new SqlConnection(connString); nwindConn.Open(); Northwnd interop_db = new Northwnd(nwindConn); SqlTransaction nwindTxn = nwindConn.BeginTransaction(); try { SqlCommand cmd = new SqlCommand( "UPDATE Products SET QuantityPerUnit = 'single item' WHERE ProductID = 3"); cmd.Connection = nwindConn; cmd.Transaction = nwindTxn; cmd.ExecuteNonQuery(); interop_db.Transaction = nwindTxn; Product prod1 = interop_db.Products .First(p => p.ProductID == 4); Product prod2 = interop_db.Products .First(p => p.ProductID == 5); prod1.UnitsInStock -= 3; prod2.UnitsInStock -= 5; interop_db.SubmitChanges(); nwindTxn.Commit(); } catch (Exception e) { Console.WriteLine(e.Message); Console.WriteLine("Error submitting changes... all changes rolled back."); } nwindConn.Close(); 概念 背景信息 (LINQ to SQL) ADO.NET 与 LINQ to SQL 与数据库通信 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/7e26c7eb-c18a-43b... 全部折叠 代码:C# LINQ to SQL 查询数据库 (LINQ to SQL) 发送反馈意见 本组主题说明如何在 LINQ to SQL 项目中开发和执行查询。 本节内容 如何:查询信息 (LINQ to SQL) 简要说明为何 LINQ to SQL 查询大体上与 LINQ 查询基本相同。 如何:将信息作为只读信息检索 (LINQ to SQL) 说明在不打算更改数据时如何提高查询性能。 如何:控制检索的相关数据量 (LINQ to SQL) 说明如何控制与主目标一起检索的相关数据。 如何:筛选相关数据 (LINQ to SQL) 说明如何通过使用子查询检索相关数据。 如何:关闭延迟加载 (LINQ to SQL) 说明如何关闭延迟加载。 如何:直接执行 SQL 查询 (LINQ to SQL) 说明如何使用 SQL 语言提交查询。 如何:存储和重用查询 (LINQ to SQL) 说明如何编译查询一次,但将其与不同的参数一起使用多次。 如何:在查询中处理组合键 (LINQ to SQL) 说明如何在运算符只带一个参数的情况下在查询中包含多列。 如何:一次检索许多对象 (LINQ to SQL) 说明如何使用 LoadWith。 如何:在 DataContext 级别进行筛选 (LINQ to SQL) 介绍 LoadWith 的另一用法。 查询示例 (LINQ to SQL) 提供许多查询示例。 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/eefb8b0c-ff07-4e8... 全部折叠 代码:C# LINQ to SQL 如何:查询信息 (LINQ to SQL) 示例 请参见 发送反馈意见 LINQ to SQL 中的查询与 LINQ 中的查询使用相同的语法。唯一的差异是 LINQ to SQL 查询中引用的对象映射到数据 库中的元素。有关更多信息,请参见 LINQ 查询简介。 LINQ to SQL 将您编写的查询转换成等效的 SQL 查询,然后将它们发送至服务器进行处理。 在 LINQ to SQL 应用程序中,可能需要特别注意 LINQ 查询的某些功能。有关更多信息,请参见 LINQ to SQL 中的 查询概念。 示例 请参见 下面的查询请求来自伦敦的客户的列表。在此示例中,Customers 是 Northwind 示例数据库中的表。 C# 复制代码 Northwnd db = new Northwnd(@"c:\northwnd.mdf"); // Query for customers in London. IQueryable custQuery = from cust in db.Customers where cust.City == "London" select cust; 概念 创建对象模型 (LINQ to SQL) 下载示例数据库 (LINQ to SQL) 查询数据库 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/e538d288-2070-40... 全部折叠 代码:C# LINQ to SQL 如何:将信息作为只读信息检索 (LINQ to SQL) 示例 请参见 发送反馈意见 当您不打算更改数据时,您可以通过设法产生只读结果来提高查询性能。 通过将 ObjectTrackingEnabled 设置为 false 可实现只读处理。 示例 请参见 注意: 当 ObjectTrackingEnabled 设置为 false 时,DeferredLoadingEnabled 将隐式设置为 false。 下面的代码检索雇员雇佣日期的只读集合。 C# 复制代码 Northwnd db = new Northwnd(@"c:\northwnd.mdf"); db.ObjectTrackingEnabled = false; IOrderedQueryable hireQuery = from emp in db.Employees orderby emp.HireDate select emp; foreach (Employee empObj in hireQuery) { Console.WriteLine("EmpID = {0}, Date Hired = {1}", empObj.EmployeeID, empObj.HireDate); } 概念 LINQ to SQL 中的查询概念 查询数据库 (LINQ to SQL) 延迟加载与立即加载的比较 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/fb09e298-0b53-47e... 全部折叠 代码:C# LINQ to SQL 如何:控制检索的相关数据量 (LINQ to SQL) 示例 请参见 发送反馈意见 使用 LoadWith 方法可指定应同时检索与主目标相关的哪些数据。例如,如果您了解将需要有关客户订单的信息, 则可以使用 LoadWith 来确保在检索客户信息时会检索订单信息。这种方法的结果是只检索一次数据库即可获取这 两组信息。 示例 请参见 注意: 通过将叉积作为一个大型投影检索,可以检索与您的查询主目标相关的数据,例如在以客户为目标时检索订 单。但这种方法往往存在弊端。例如,所得结果仅仅为投影,而不是可以由 LINQ to SQL 更改和持久化的实 体。此外,您可能会检索到您并不需要的大量数据。 在下面的示例中,在执行查询时会检索到位于伦敦的所有 Customers 所下的所有 Orders。因此,继续访问 Customer 对象的 Orders 属性不会触发新的数据库查询。 C# 复制代码 Northwnd db = new Northwnd(@"c:\northwnd.mdf"); DataLoadOptions dlo = new DataLoadOptions(); dlo.LoadWith(c => c.Orders); db.LoadOptions = dlo; var londonCustomers = from cust in db.Customers where cust.City == "London" select cust; foreach (var custObj in londonCustomers) { Console.WriteLine(custObj.CustomerID); } 概念 查询数据库 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/efdc203e-3da9-447... 全部折叠 代码:C# LINQ to SQL 如何:筛选相关数据 (LINQ to SQL) 示例 请参见 发送反馈意见 使用 AssociateWith 方法可指定用来限制检索到的数据量的子查询。 示例 请参见 在下面的示例中,AssociateWith 方法将检索到的 Orders 限制为今天尚未发货的那些订单。如果不使用这种 方式,则即使只需要一部分订单,也会检索到所有 Orders。 C# 复制代码 Northwnd db = new Northwnd(@"c:\northwnd.mdf"); DataLoadOptions dlo = new DataLoadOptions(); dlo.AssociateWith(c => c.Orders.Where(p => p.ShippedDate != DateTime.Today)); db.LoadOptions = dlo; var custOrderQuery = from cust in db.Customers where cust.City == "London" select cust; foreach (Customer custObj in custOrderQuery) { Console.WriteLine(custObj.CustomerID); foreach (Order ord in custObj.Orders) { Console.WriteLine("\t {0}",ord.OrderDate); } } 概念 查询数据库 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/ec8b8f97-5d01-4f3... 全部折叠 代码:C# LINQ to SQL 如何:关闭延迟加载 (LINQ to SQL) 示例 请参见 发送反馈意见 可以通过将 DeferredLoadingEnabled 设置为 false 来关闭延迟加载。有关更多信息,请参见延迟加载与立即加载的 比较 (LINQ to SQL)。 示例 请参见 注意: 关闭对象跟踪时,也隐式地关闭了延迟加载。有关更多信息,请参见如何:将信息作为只读信息检索 (LINQ to SQL)。 下面的示例演示如何通过将 DeferredLoadingEnabled 设置为 false 来关闭延迟加载。 C# 复制代码 Northwnd db = new Northwnd(@"c:\northwnd.mdf"); db.DeferredLoadingEnabled = false; DataLoadOptions ds = new DataLoadOptions(); ds.LoadWith(c => c.Orders); ds.LoadWith(o => o.OrderDetails); db.LoadOptions = ds; var custQuery = from cust in db.Customers where cust.City == "London" select cust; 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); } } } 概念 LINQ to SQL 中的查询概念 查询数据库 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/1b84b852-3cad-41... 全部折叠 代码:C# LINQ to SQL 如何:直接执行 SQL 查询 (LINQ to SQL) 示例 请参见 发送反馈意见 LINQ to SQL 将您编写的查询转换成参数化 SQL 查询(以文本形式),然后将它们发送至 SQL 服务器进行处理。 SQL 无法执行在本地可能对您的应用程序可用的各种方法。LINQ to SQL 会设法将这些本地方法转换成在 SQL 环境 中可用的等效操作和函数。.NET Framework 内置类型中的大多数方法和运算符都能直接转换成 SQL 命令。有些则 可以用可用的函数生成。无法生成的那些方法和运算符会产生运行时异常。有关更多信息,请参见 SQL-CLR 类型映 射 (LINQ to SQL)。 如果 LINQ to SQL 查询不足以满足专门任务的需要,您可以使用 ExecuteQuery 方法来执行 SQL 查询,然后将查询 的结果直接转换成对象。 示例 请参见 在下面的示例中,假定 Customer 类的数据分布在两个表(customer1 和 customer2)中。此查询将返回 Customer 对象的序列。 只要表格结果中的列名与您的实体类的列属性匹配,LINQ to SQL 就会为您创建不在任何 SQL 查询范围之内 的对象。 ExecuteQuery 方法也允许带有参数。请使用类似如下内容的代码来执行参数化查询。 在查询文本中使用 Console.WriteLine() 和 String.Format() 所用的大括号表示法来表示参数。事实上, 实际会对您提供的查询字符串调用 String.Format(),从而将括在大括号内的参数替换为生成的参数名,如 @p0、@p1 … @p(n)。 C# 复制代码 Northwnd db = new Northwnd(@"c:\northwnd.mdf"); IEnumerable results = db.ExecuteQuery (@"SELECT c1.custid as CustomerID, c2.custName as ContactName FROM customer1 as c1, customer2 as c2 WHERE c1.custid = c2.custid" ); C# 复制代码 Northwnd db = new Northwnd(@"c:\northwnd.mdf"); IEnumerable results = db.ExecuteQuery ("SELECT contactname FROM customers WHERE city = {0}", "London"); 概念 背景信息 (LINQ to SQL) 查询数据库 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/e491b9bf-741a-429... 全部折叠 代码:C# LINQ to SQL 如何:存储和重用查询 (LINQ to SQL) 示例 请参见 发送反馈意见 当您的应用程序多次执行结构上相似的查询时,您通常可以通过如下方法提高性能:编译此查询一次,然后用不同的参数 执行它若干次。例如,应用程序可能需要检索位于特定城市的所有客户,其中此城市是在运行时由用户在窗体中指定的。 LINQ to SQL 支持使用已编译查询来实现此目的。 示例 请参见 注意: 这种使用模式代表了已编译查询最常见的用途。也可以使用其他方法。例如,已编译查询可以存储为扩展设计器所生 成代码的分部类的静态成员。 在很多情况下,您可能需要跨线程边界重复使用查询。在这种情况下,将已编译查询存储在静态变量中特别有效。下 面的代码示例采用设计用于存储已编译查询的 Queries,并采用表示强类型化 DataContext 的 Northwind 类。 目前您无法存储(存储在静态变量中)返回匿名类型的查询,因为类型没有可作为泛型参数提供的名称。下面的示例 演示如何创建可表示结果的类型,然后将其用作泛型参数,从而解决该问题。 C# 复制代码 public static Func> CustomersByCity = CompiledQuery.Compile((Northwnd db, string city) => from c in db.Customers where c.City == city select c); public static Func> CustomersById = CompiledQuery.Compile((Northwnd db, string id) => db.Customers.Where(c => c.CustomerID == id)); C# 复制代码 // The following example invokes such a compiled query in the main // program. public IEnumerable GetCustomersByCity(string city) { var myDb = GetNorthwind(); return Queries.CustomersByCity(myDb, city); } C# 复制代码 class SimpleCustomer { public string ContactName { get; set; } } class Queries2 { public static Func> CustomersByCity = CompiledQuery.Compile>( (Northwnd db, string city) => from c in db.Customers where c.City == city select new SimpleCustomer { ContactName = c.ContactName }); } 概念 LINQ to SQL 中的查询概念 查询数据库 (LINQ to SQL) 参考 CompiledQuery 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/a012bd79-1809-45... 全部折叠 代码:C# LINQ to SQL 如何:在查询中处理组合键 (LINQ to SQL) 示例 请参见 发送反馈意见 有些运算符只能带一个参数。如果您的参数必须包含数据库中的多个列,则您必须创建一个匿名类型来表示这种组 合。 示例 请参见 下面的示例显示了调用 GroupBy 运算符的查询,此运算符只能带一个 key 参数。 联接也属于这种情况,如下例中所示: C# 复制代码 var query = from cust in db.Customers group cust.ContactName by new { City = cust.City, Region = cust.Region }; foreach (var grp in query) { Console.WriteLine("\nLocation Key: {0}", grp.Key); foreach (var listing in grp) { Console.WriteLine("\t{0}", listing); } } C# 复制代码 var query = from ord in db.Orders from prod in db.Products join det in db.OrderDetails on new { ord.OrderID, prod.ProductID } equals new { det.OrderID, det.ProductID } into details from det in details select new { ord.OrderID, prod.ProductID, det.UnitPrice }; 概念 LINQ to SQL 中的查询概念 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/ce2f14fd-1038-458... 全部折叠 代码:C# LINQ to SQL 如何:一次检索许多对象 (LINQ to SQL) 示例 请参见 发送反馈意见 通过使用 LoadWith,可以在一个查询中检索许多对象。 示例 请参见 下面的代码使用 LoadWith 方法来同时检索 Customer 和 Order 对象。 C# 复制代码 Northwnd db = new Northwnd(@"northwnd.mdf"); DataLoadOptions ds = new DataLoadOptions(); ds.LoadWith(c => c.Orders); ds.LoadWith(o => o.OrderDetails); db.LoadOptions = ds; var custQuery = from cust in db.Customers where cust.City == "London" select cust; 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); } } } 概念 LINQ to SQL 中的查询概念 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/18aff4d8-bde8-461... 全部折叠 代码:C# LINQ to SQL 如何:在 DataContext 级别进行筛选 (LINQ to SQL) 示例 请参见 发送反馈意见 您可以在 DataContext 级别筛选 EntitySets。此类筛选器适用于用此 DataContext 实例执行的所有查询。 示例 请参见 在下面的示例中,使用 DataLoadOptions.AssociateWith(LambdaExpression) 来筛选截至 ShippedDate 的客户 的预加载订单。 C# 复制代码 Northwnd db = new Northwnd(@"northwnd.mdf"); // Preload Orders for Customer. // One directive per relationship to be preloaded. DataLoadOptions ds = new DataLoadOptions(); ds.LoadWith(c => c.Orders); ds.AssociateWith (c => c.Orders.Where(p => p.ShippedDate != DateTime.Today)); db.LoadOptions = ds; var custQuery = from cust in db.Customers where cust.City == "London" select cust; 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); } } } 概念 LINQ to SQL 中的查询概念 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/15505cd7-0df2-42... 全部折叠 代码:C# LINQ to SQL 查询示例 (LINQ to SQL) 发送反馈意见 本节提供典型的 LINQ to SQL 查询的 Visual Basic 和 C# 示例。使用 Visual Studio 的开发人员可以在“示例”一节中提供 的示例解决方案中找到许多其他示例。有关更多信息,请参见示例 (LINQ to SQL)。 本节内容 相关章节 重要说明: LINQ to SQL 文档中的代码示例中经常会用到 db。假定 db 是继承自 DataContext 的 Northwind 类的一个实例。 聚合查询 (LINQ to SQL) 介绍如何使用 Average、Count 等。 如何:返回序列中的第一个元素 (LINQ to SQL) 提供使用 First 的示例。 如何:返回或跳过序列中的元素 (LINQ to SQL) 提供使用 Take(TSource) 和 Skip(TSource) 的示例。 如何:对序列中的元素进行排序 (LINQ to SQL) 提供使用 OrderBy 的示例。 如何:对序列中的元素进行分组 (LINQ to SQL) 提供使用 GroupBy 的示例。 如何:从序列中消除重复元素 (LINQ to SQL) 提供使用 Distinct 的示例。 如何:确定序列中的元素是否部分或全部满足条件 (LINQ to SQL) 提供使用 All(TSource) 和 Any 的示例。 如何:串联两个序列 (LINQ to SQL) 提供使用 Concat(TSource) 的示例。 如何:返回两个序列之间的差集 (LINQ to SQL) 提供使用 Except 的示例。 如何:返回两个序列的交集 (LINQ to SQL) 提供使用 Intersect 的示例。 如何:返回两个序列的并集 (LINQ to SQL) 提供使用 Union 的示例。 如何:将序列转换为数组 (LINQ to SQL) 提供使用 ToArray(TSource) 的示例。 如何:将序列转换为泛型列表 (LINQ to SQL) 提供使用 ToList(TSource) 的示例。 如何:将类型转换为泛型 IEnumerable (LINQ to SQL) 提供使用 AsEnumerable(TSource) 的示例。 如何:构建联接和叉积查询 (LINQ to SQL) 提供在 from、where 和 select 子句中使用外键导航的示例。 如何:构建投影 (LINQ to SQL) 提供通过结合使用 select 和其他功能(如匿名类型)构建查询投影的示例。 标准查询运算符概述 解释标准查询运算符的概念。 LINQ to SQL 中的查询概念 解释 LINQ to SQL 如何使用适用于查询的概念。 编程指南 (LINQ to SQL) 提供用于访问一些主题的门户,这些主题解释了与 LINQ to SQL 相关的编程概念。 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/137f8677-494c-4d... 全部折叠 代码:C# LINQ to SQL 聚合查询 (LINQ to SQL) 发送反馈意见 LINQ to SQL 支持 Average、Count、Max、Min 和 Sum 聚合运算符。请注意 LINQ to SQL 中聚合运算符的以下 特征: z 聚合查询是立即执行的。 有关更多信息,请参见 LINQ 查询简介。 z 聚合查询通常返回一个数字,而非一个集合。 有关更多信息,请参见聚合运算。 z 不能对匿名类型调用聚合。 以下主题中的示例来自 Northwind 示例数据库。有关更多信息,请参见下载示例数据库 (LINQ to SQL)。 本节内容 相关章节 如何:返回数值序列中的平均值 (LINQ to SQL) 演示如何使用 Average 运算符。 如何:计算序列中的元素数目 (LINQ to SQL) 演示如何使用 Count 运算符。 如何:查找数值序列中的最大值 (LINQ to SQL) 演示如何使用 Max 运算符。 如何:查找数值序列中的最小值 (LINQ to SQL) 演示如何使用 Min 运算符。 如何:计算数值序列中的值之和 (LINQ to SQL) 演示如何使用 Sum 运算符。 查询示例 (LINQ to SQL) 提供指向 Visual Basic 和 C# 中 LINQ to SQL 查询的链接。 LINQ to SQL 中的查询概念 提供指向特定主题的链接,这些主题解释有关在 LINQ to SQL 中设计 LINQ 查询的一些概念。 LINQ 查询简介 解释 LINQ 中查询的工作原理。 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/13ec5580-05ce-4a1... 全部折叠 代码:C# LINQ to SQL 如何:返回数值序列中的平均值 (LINQ to SQL) 示例 请参见 发送反馈意见 Average 运算符用于计算数值序列的平均值。 示例 注意: 使用经 LINQ to SQL 转换后的 Average 计算整数值时,所得结果的数据类型为 integer,而非 double。 下面的示例返回 Orders 表中 Freight 值的平均值。 从 Northwind 示例数据库中得到的结果将为 78.2442。 下面的示例返回 Products 表中所有 Products 的单价。 从 Northwind 示例数据库中得到的结果将为 28.8663。 下面的示例使用 Average 运算符查找其单价高于其所属类别的平均单价的那些 Products。此示例随后会按 组显示结果。 请注意,此示例需要使用 C# 中的 var 关键字,这是因为返回类型为匿名类型。 如果您对 Northwind 示例数据库运行此查询,所得到的结果应与如下内容类似: 1 Côte de Blaye Ipoh Coffee C# 复制代码 System.Nullable averageFreight = (from ord in db.Orders select ord.Freight) .Average(); Console.WriteLine(averageFreight); C# 复制代码 System.Nullable averageUnitPrice = (from prod in db.Products select prod.UnitPrice) .Average(); Console.WriteLine(averageUnitPrice); C# 复制代码 var priceQuery = from prod in db.Products group prod by prod.CategoryID into grouping select new { grouping.Key, ExpensiveProducts = from prod2 in grouping where prod2.UnitPrice > grouping.Average(prod3 => prod3.UnitPrice) select prod2 }; foreach (var grp in priceQuery) { Console.WriteLine(grp.Key); foreach (var listing in grp.ExpensiveProducts) { Console.WriteLine(listing.ProductName); } } 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/ee3b8673-a2e7-4b... 请参见 2 Grandma's Boysenberry Spread Northwoods Cranberry Sauce Sirop d'érable Vegie-spread 3 Sir Rodney's Marmalade Gumbär Gummibärchen Schoggi Schokolade Tarte au sucre 4 Queso Manchego La Pastora Mascarpone Fabioli Raclette Courdavault Camembert Pierrot Gudbrandsdalsost Mozzarella di Giovanni 5 Gustaf's Knäckebröd Gnocchi di nonna Alice Wimmers gute Semmelknödel 6 Mishi Kobe Niku Thüringer Rostbratwurst 7 Rössle Sauerkraut Manjimup Dried Apples 8 Ikura Carnarvon Tigers Nord-Ost Matjeshering Gravad lax 概念 聚合查询 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/ee3b8673-a2e7-4b... 全部折叠 代码:C# LINQ to SQL 如何:计算序列中的元素数目 (LINQ to SQL) 示例 请参见 发送反馈意见 使用 Count 运算符可计算序列中的元素数目。 对 Northwind 示例数据库运行此查询产生的输出为 91。 示例 请参见 下面的示例计算数据库中的 Customers 数目。 下面的示例计算数据库中尚未停产的产品数目。 对 Northwind 示例数据库运行此示例产生的输出为 69。 C# 复制代码 System.Int32 customerCount = db.Customers.Count(); Console.WriteLine(customerCount); C# 复制代码 System.Int32 notDiscontinuedCount = (from prod in db.Products where !prod.Discontinued select prod) .Count(); Console.WriteLine(notDiscontinuedCount); 概念 聚合查询 (LINQ to SQL) 下载示例数据库 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/ccbe5d54-c9eb-4b1... 全部折叠 代码:C# LINQ to SQL 如何:查找数值序列中的最大值 (LINQ to SQL) 示例 请参见 发送反馈意见 使用 Max 运算符可查找数值序列中的最高值。 示例 下面的示例查找任何员工的最近雇佣日期。 如果您对 Northwind 示例数据库运行此查询,则输出为:11/15/1994 12:00:00 AM。 下面的示例查找任何产品的最大库存件数。 如果您对 Northwind 示例数据库运行此示例,则输出为:125。 下面的示例使用 Max 查找每个类别中单价最高的 Products。然后,按类别列出输出结果。 如果您对 Northwind 示例数据库运行上一个查询,所得到的结果将与如下内容类似: 1 Côte de Blaye 2 Vegie-spread 3 Sir Rodney's Marmalade 4 C# 复制代码 System.Nullable latestHireDate = (from emp in db.Employees select emp.HireDate) .Max(); Console.WriteLine(latestHireDate); C# 复制代码 System.Nullable maxUnitsInStock = (from prod in db.Products select prod.UnitsInStock) .Max(); Console.WriteLine(maxUnitsInStock); C# 复制代码 var maxQuery = from prod in db.Products group prod by prod.CategoryID into grouping select new { grouping.Key, MostExpensiveProducts = from prod2 in grouping where prod2.UnitPrice == grouping.Max(prod3 => prod3.UnitPrice) select prod2 }; foreach (var grp in maxQuery) { Console.WriteLine(grp.Key); foreach (var listing in grp.MostExpensiveProducts) { Console.WriteLine(listing.ProductName); } } 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/70d7c058-0280-48... 请参见 Raclette Courdavault 5 Gnocchi di nonna Alice 6 Thüringer Rostbratwurst 7 Manjimup Dried Apples 8 Carnarvon Tigers 概念 聚合查询 (LINQ to SQL) 下载示例数据库 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/70d7c058-0280-48... 全部折叠 代码:C# LINQ to SQL 如何:查找数值序列中的最小值 (LINQ to SQL) 示例 请参见 发送反馈意见 使用 Min 运算符可返回数值序列中的最小值。 示例 下面的示例查找所有产品的最低单价。 如果您对 Northwind 示例数据库运行此查询,则输出为:2.5000。 下面的示例查找所有订单的最低运费额。 如果您对 Northwind 示例数据库运行此查询,则输出为:0.0200。 下面的示例使用 Min 查找每个类别中单价最低的 Products。输出按类别排列。 如果您对 Northwind 示例数据库运行上一个查询,所得到的结果将与如下内容类似: 1 Guaraná Fantástica 2 Aniseed Syrup 3 Teatime Chocolate Biscuits 4 C# 复制代码 System.Nullable lowestUnitPrice = (from prod in db.Products select prod.UnitPrice) .Min(); Console.WriteLine(lowestUnitPrice); C# 复制代码 System.Nullable lowestFreight = (from ord in db.Orders select ord.Freight) .Min(); Console.WriteLine(lowestFreight); C# 复制代码 var minQuery = from prod in db.Products group prod by prod.CategoryID into grouping select new { grouping.Key, LeastExpensiveProducts = from prod2 in grouping where prod2.UnitPrice == grouping.Min(prod3 => prod3.UnitPrice) select prod2 }; foreach (var grp in minQuery) { Console.WriteLine(grp.Key); foreach (var listing in grp.LeastExpensiveProducts) { Console.WriteLine(listing.ProductName); } } 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/78203093-f242-45... 请参见 Geitost 5 Filo Mix 6 Tourtière 7 Longlife Tofu 8 Konbu 概念 聚合查询 (LINQ to SQL) 下载示例数据库 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/78203093-f242-45... 全部折叠 代码:C# LINQ to SQL 如何:计算数值序列中的值之和 (LINQ to SQL) 示例 请参见 发送反馈意见 使用 Sum 运算符可以计算序列中数值的和。 请注意 LINQ to SQL 中 Sum 运算符的以下特征: z 使用标准查询运算符中的聚合运算符 Sum 计算空序列或只包含 null 的序列时,所得结果为零。在 LINQ to SQL 中,SQL 的语义保持不变。因此,使用 Sum 计算空序列或只包含 null 的序列时,所得结果为 null 而非 零。 z 针对中间结果的 SQL 限制适用于 LINQ to SQL 中的聚合。32 位整型量之和不是使用 64 位结果计算的,且在 LINQ to SQL 转换 Sum 时可能会发生溢出。即使对于内存中的对应序列,标准查询运算符的实现不会造成溢 出,仍存在这种可能性。 示例 请参见 下面的示例查找 Order 表中所有订单的总运费。 如果您对 Northwind 示例数据库运行此查询,则输出为:64942.6900。 下面的示例查找所有产品总的订购件数。 如果您对 Northwind 示例数据库运行此查询,则输出为:780。 请注意,您必须对 short 类型(例如,UnitsOnOrder)进行强制转换,因为 Sum 没有 short 类型的重载。 C# 复制代码 System.Nullable totalFreight = (from ord in db.Orders select ord.Freight) .Sum(); Console.WriteLine(totalFreight); C# 复制代码 System.Nullable totalUnitsOnOrder = (from prod in db.Products select (long)prod.UnitsOnOrder) .Sum(); Console.WriteLine(totalUnitsOnOrder); 概念 聚合查询 (LINQ to SQL) 下载示例数据库 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/24e335b0-984e-48... 全部折叠 代码:C# LINQ to SQL 如何:返回序列中的第一个元素 (LINQ to SQL) 示例 请参见 发送反馈意见 使用 First 运算符可返回序列中的第一个元素。使用 First 的查询是立即执行的。 示例 请参见 注意: LINQ to SQL 不支持 Last 运算符。 下面的代码查找表中的第一个 Shipper: 如果您对 Northwind 示例数据库运行此查询,则结果为 ID = 1, Company = Speedy Express. 下面的代码查找具有 CustomerID BONAP 的单个 Customer。 如果您对 Northwind 示例数据库运行此查询,则结果为 ID = BONAP, Contact = Laurence Lebihan。 C# 复制代码 Shipper shipper = db.Shippers.First(); Console.WriteLine("ID = {0}, Company = {1}", shipper.ShipperID, shipper.CompanyName); C# 复制代码 Customer custQuery = (from custs in db.Customers where custs.CustomerID == "BONAP" select custs) .First(); Console.WriteLine("ID = {0}, Contact = {1}", custQuery.CustomerID, custQuery.ContactName); 概念 查询示例 (LINQ to SQL) 下载示例数据库 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/ccdc3777-b2c2-44e... 全部折叠 代码:C# LINQ to SQL 如何:返回或跳过序列中的元素 (LINQ to SQL) 示例 请参见 发送反馈意见 使用 Take(TSource) 运算符可返回序列中给定数目的元素,然后跳过其余元素。 使用 Skip(TSource) 运算符可跳过序列中给定数目的元素,然后返回其余元素。 LINQ to SQL 通过使用带有 SQL NOT EXISTS 子句的子查询来转换 Skip(TSource)。这种转换存在以下局限性: z 参数必须为集合。不支持多重集,即便为有序多重集也不例外。 z 生成的查询可能要比为应用了 Skip(TSource) 的基查询生成的查询复杂得多。这种复杂性可能会导致性能降 低,甚至发生超时。 示例 注意: Take(TSource) 和 Skip(TSource) 用在针对 SQL Server 2000 的查询中时存在一定的限制。有关更多信息,请参 见疑难解答 (LINQ to SQL) 中的“SQL Server 2000 中的 Skip 和 Take 异常”项。 下面的示例使用 Take 选择前五个受雇的 Employees。请注意,此集合首先按 HireDate 排序。 下面的示例使用 Skip(TSource) 选择 10 种最贵的 Products 以外的所有产品。 下面的示例结合使用 Skip(TSource) 和 Take(TSource) 方法来跳过前 50 条记录,然后返回下 10 条记录。 Take(TSource) 和 Skip(TSource) 运算仅对有序集而言是定义完善的。未定义针对无序集或多重集的语义。 由于 SQL 中的排序存在限制,因此 LINQ to SQL 会设法将 Take(TSource) 或 Skip(TSource) 运算符的参数的 排序操作移到相应运算符的结果中进行。 C# 复制代码 IQueryable firstHiredQuery = (from emp in db.Employees orderby emp.HireDate select emp) .Take(5); foreach (Employee empObj in firstHiredQuery) { Console.WriteLine("{0}, {1}", empObj.EmployeeID, empObj.HireDate); } C# 复制代码 IQueryable lessExpensiveQuery = (from prod in db.Products orderby prod.UnitPrice descending select prod) .Skip(10); foreach (Product prodObj in lessExpensiveQuery) { Console.WriteLine(prodObj.ProductName); } C# 复制代码 var custQuery2 = (from cust in db.Customers orderby cust.ContactName select cust) .Skip(50).Take(10); foreach (var custRecord in custQuery2) { Console.WriteLine(custRecord.ContactName); } 注意: 对于 SQL Server 2000 和 SQL Server 2005,转换是不同的。如果您打算将 Skip(TSource) 与具有任意复 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/81a31acd-e0f1-4bc... 请参见 请考虑下面这个针对 SQL Server 2000 的 LINQ to SQL 查询: LINQ to SQL 将排序操作移到 SQL 代码的结尾进行,如下所示: 当 Take(TSource) 和 Skip(TSource) 链接在一起时,所有指定的排序都必须一致。否则,结果将是不确定的。 对于非负的、基于 SQL 规范的整型常量参数,Take(TSource) 和 Skip(TSource) 都是定义完善的。 杂度的查询一起使用,请使用 SQL Server 2005。 C# 复制代码 IQueryable custQuery3 = (from custs in db.Customers where custs.City == "London" orderby custs.CustomerID select custs) .Skip(1).Take(1); foreach (var custObj in custQuery3) { Console.WriteLine(custObj.CustomerID); } 复制代码 SELECT TOP 1 [t0].[CustomerID], [t0].[CompanyName], FROM [Customers] AS [t0] WHERE (NOT (EXISTS( SELECT NULL AS [EMPTY] FROM ( SELECT TOP 1 [t1].[CustomerID] FROM [Customers] AS [t1] WHERE [t1].[City] = @p0 ORDER BY [t1].[CustomerID] ) AS [t2] WHERE [t0].[CustomerID] = [t2].[CustomerID] ))) AND ([t0].[City] = @p1) ORDER BY [t0].[CustomerID] 概念 查询示例 (LINQ to SQL) 标准查询运算符转换 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/81a31acd-e0f1-4bc... 全部折叠 代码:C# LINQ to SQL 如何:对序列中的元素进行排序 (LINQ to SQL) 示例 请参见 发送反馈意见 使用 OrderBy 运算符可按一个或多个键对序列进行排序。 示例 注意: LINQ to SQL 设计为支持按简单的基元类型(如 string、int 等)进行排序。它不支持对复杂的多值类(如匿名 类型)进行排序。它也不支持 byte 数据类型。 下面的示例按雇佣日期对 Employees 进行排序。 下面的示例使用 where 按运费对运往 London 的 Orders 进行排序。 下面的示例按单价从高到底对 Products 进行排序。 下面的示例使用复合的 OrderBy 先按城市后按联系人姓名对 Customers 进行排序。 C# 复制代码 IOrderedQueryable hireQuery = from emp in db.Employees orderby emp.HireDate select emp; foreach (Employee empObj in hireQuery) { Console.WriteLine("EmpID = {0}, Date Hired = {1}", empObj.EmployeeID, empObj.HireDate); } C# 复制代码 IOrderedQueryable freightQuery = from ord in db.Orders where ord.ShipCity == "London" orderby ord.Freight select ord; foreach (Order ordObj in freightQuery) { Console.WriteLine("Order ID = {0}, Freight = {1}", ordObj.OrderID, ordObj.Freight); } C# 复制代码 IOrderedQueryable priceQuery = from prod in db.Products orderby prod.UnitPrice descending select prod; foreach (Product prodObj in priceQuery) { Console.WriteLine("Product ID = {0}, Unit Price = {1}", prodObj.ProductID, prodObj.UnitPrice); } C# 复制代码 IOrderedQueryable custQuery = from cust in db.Customers orderby cust.City, cust.ContactName select cust; foreach (Customer custObj in custQuery) { Console.WriteLine("City = {0}, Name = {1}", custObj.City, custObj.ContactName); } 页码,1/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/d59b93a9-50c8-47... 请参见 下面的示例按运往国家/地区对来自 EmployeeID 1 的订单进行排序,然后按运费从高到低进行排序。 下面的示例结合使用 OrderBy、Max 和 GroupBy 运算符查找每个类别中单价最高的 Products,然后按类别 ID 对组进行排序。 如果您对 Northwind 示例数据库运行上一个查询,所得到的结果将与如下内容类似: 1 Côte de Blaye 2 Vegie-spread 3 Sir Rodney's Marmalade 4 Raclette Courdavault 5 Gnocchi di nonna Alice 6 Thüringer Rostbratwurst 7 Manjimup Dried Apples 8 Carnarvon Tigers C# 复制代码 IOrderedQueryable ordQuery = from ord in db.Orders where ord.EmployeeID == 1 orderby ord.ShipCountry, ord.Freight descending select ord; foreach (Order ordObj in ordQuery) { Console.WriteLine("Country = {0}, Freight = {1}", ordObj.ShipCountry, ordObj.Freight); } C# 复制代码 var highPriceQuery = from prod in db.Products group prod by prod.CategoryID into grouping orderby grouping.Key select new { grouping.Key, MostExpensiveProducts = from prod2 in grouping where prod2.UnitPrice == grouping.Max(p3 => p3.UnitPrice) select prod2 }; foreach (var prodObj in highPriceQuery) { Console.WriteLine(prodObj.Key); foreach (var listing in prodObj.MostExpensiveProducts) { Console.WriteLine(listing.ProductName); } } 页码,2/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/d59b93a9-50c8-47... 概念 查询示例 (LINQ to SQL) 下载示例数据库 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/d59b93a9-50c8-47... 全部折叠 代码:C# LINQ to SQL 如何:对序列中的元素进行分组 (LINQ to SQL) 示例 请参见 发送反馈意见 GroupBy 运算符用于对序列中的元素进行分组。下面的示例使用 Northwind 数据库。 示例 注意: GroupBy 查询中的 Null 列值有时可能引发 InvalidOperationException。有关更多信息,请参见疑难解答 (LINQ to SQL) 中的“GroupBy InvalidOperationException”部分。 下面的示例按照 CategoryID 对 Products 进行分区。 下面的示例使用 Max 来查找每个 CategoryID 的最高单价。 下面的示例使用 Average 来查找每个 CategoryID 的平均 UnitPrice。 下面的示例使用 Sum 来查找每个 CategoryID 的总 UnitPrice。 C# 复制代码 IQueryable> prodQuery = from prod in db.Products group prod by prod.CategoryID into grouping select grouping; foreach (IGrouping grp in prodQuery) { Console.WriteLine("\nCategoryID Key = {0}:", grp.Key); foreach (Product listing in grp) { Console.WriteLine("\t{0}", listing.ProductName); } } C# 复制代码 var q = from p in db.Products group p by p.CategoryID into g select new { g.Key, MaxPrice = g.Max(p => p.UnitPrice) }; C# 复制代码 var q2 = from p in db.Products group p by p.CategoryID into g select new { g.Key, AveragePrice = g.Average(p => p.UnitPrice) }; C# 复制代码 var priceQuery = from prod in db.Products group prod by prod.CategoryID into grouping select new { grouping.Key, TotalPrice = grouping.Sum(p => p.UnitPrice) }; foreach (var grp in priceQuery) { Console.WriteLine("Category = {0}, Total price = {1}", grp.Key, grp.TotalPrice); } 页码,1/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/1d50c8b4-f550-47... 下面的示例使用 Count 来查找每个 CategoryID 中已停产 Products 的数量。 下面的示例使用后跟的 where 子句来查找至少包含 10 种产品的所有类别。 下面的示例按 CategoryID 和 SupplierID 对产品进行分组。 下面的示例返回两个产品序列。第一个序列包含单价小于或等于 10 的产品。第二个序列包含单价大于 10 的 产品。 C# 复制代码 var disconQuery = from prod in db.Products group prod by prod.CategoryID into grouping select new { grouping.Key, NumProducts = grouping.Count(p => p.Discontinued) }; foreach (var prodObj in disconQuery) { Console.WriteLine("CategoryID = {0}, Discontinued# = {1}", prodObj.Key, prodObj.NumProducts); } C# 复制代码 var prodCountQuery = from prod in db.Products group prod by prod.CategoryID into grouping where grouping.Count() >= 10 select new { grouping.Key, ProductCount = grouping.Count() }; foreach (var prodCount in prodCountQuery) { Console.WriteLine("CategoryID = {0}, Product count = {1}", prodCount.Key, prodCount.ProductCount); } C# 复制代码 var prodQuery = from prod in db.Products group prod by new { prod.CategoryID, prod.SupplierID } into grouping select new { grouping.Key, grouping }; foreach (var grp in prodQuery) { Console.WriteLine("\nCategoryID {0}, SupplierID {1}", grp.Key.CategoryID, grp.Key.SupplierID); foreach (var listing in grp.grouping) { Console.WriteLine("\t{0}", listing.ProductName); } } C# 复制代码 var priceQuery = from prod in db.Products group prod by new { Criterion = prod.UnitPrice > 10 } into grouping select grouping; 页码,2/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/1d50c8b4-f550-47... 请参见 GroupBy 运算符只能采用单个键参数。如果您需要按多个键进行分组,则必须创建匿名类型,如下例所示: foreach (var prodObj in priceQuery) { if (prodObj.Key.Criterion == false) Console.WriteLine("Prices 10 or less:"); else Console.WriteLine("\nPrices greater than 10"); foreach (var listing in prodObj) { Console.WriteLine("{0}, {1}", listing.ProductName, listing.UnitPrice); } } C# 复制代码 var custRegionQuery = from cust in db.Customers group cust.ContactName by new { City = cust.City, Region = cust.Region }; foreach (var grp in custRegionQuery) { Console.WriteLine("\nLocation Key: {0}", grp.Key); foreach (var listing in grp) { Console.WriteLine("\t{0}", listing); } } 概念 查询示例 (LINQ to SQL) 下载示例数据库 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/1d50c8b4-f550-47... 全部折叠 代码:C# LINQ to SQL 如何:从序列中消除重复元素 (LINQ to SQL) 示例 请参见 发送反馈意见 使用 Distinct 运算符可从序列中消除重复元素。 示例 请参见 下面的示例使用 Distinct 来选择有客户的唯一城市序列。 C# 复制代码 IQueryable cityQuery = (from cust in db.Customers select cust.City).Distinct(); foreach (String cityString in cityQuery) { Console.WriteLine(cityString); } 概念 查询示例 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/2b224a84-bad5-48... 全部折叠 代码:C# LINQ to SQL 如何:确定序列中的元素是否部分或全部满足条件 (LINQ to SQL) 示例 请参见 发送反馈意见 如果序列中的所有元素都满足某一项条件,则 All(TSource) 运算符会返回 true。 如果序列中的任意一个元素满足某一项条件,则 Any 运算符会返回 true。 示例 请参见 下面的示例返回由至少下了一个订单的客户组成的序列。如果给定的 Customer 下了任何 Order,则 Where/where 子句的计算结果将为 true。 下面的 Visual Basic 代码确定未下订单的客户的列表,并确保对于该列表中的每一位客户,都提供了联系人名 称。 下面的 C# 示例返回由订单包含以“C”开头的 ShipCity 的客户组成的序列。返回结果中还包括未下订单的 客户。(按照设计,对于空序列,All(TSource) 运算符返回 true。) 通过使用 Count 运算符在控制台输出中 消除了未下订单的客户。 C# 复制代码 var OrdersQuery = from cust in db.Customers where cust.Orders.Any() select cust; C# 复制代码 var custEmpQuery = from cust in db.Customers where cust.Orders.All(o => o.ShipCity.StartsWith("C")) orderby cust.CustomerID select cust; foreach (Customer custObj in custEmpQuery) { if (custObj.Orders.Count > 0) Console.WriteLine("CustomerID: {0}", custObj.CustomerID); foreach (Order ordObj in custObj.Orders) { Console.WriteLine("\t OrderID: {0}; ShipCity: {1}", ordObj.OrderID, ordObj.ShipCity); } } 概念 查询示例 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/339ec145-826c-46... 全部折叠 代码:C# LINQ to SQL 如何:串联两个序列 (LINQ to SQL) 示例 请参见 发送反馈意见 使用 Concat(TSource) 运算符可串联两个序列。 Concat(TSource) 运算符是为有序多重集定义的,其中接收方的顺序与参数的顺序相同。 在 SQL 中排序是产生结果前的最后一步。因此,Concat(TSource) 运算符是通过使用 UNION ALL 实现的,并且不 保留其参数的顺序。为确保结果中的顺序正确,一定要显式地对结果进行排序。 示例 请参见 此示例使用 Concat(TSource) 返回由所有 Customer 和 Employee 的电话和传真号码组成的序列。 此示例使用 Concat(TSource) 返回由所有 Customer 和 Employee 的名字和电话号码映射组成的序列。 C# 复制代码 IQueryable custQuery = (from cust in db.Customers select cust.Phone) .Concat (from cust in db.Customers select cust.Fax) .Concat (from emp in db.Employees select emp.HomePhone) ; foreach (var custData in custQuery) { Console.WriteLine(custData); } C# 复制代码 var infoQuery = (from cust in db.Customers select new { Name = cust.CompanyName, cust.Phone } ) .Concat (from emp in db.Employees select new { Name = emp.FirstName + " " + emp.LastName, Phone = emp.HomePhone } ); foreach (var infoData in infoQuery) { Console.WriteLine("Name = {0}, Phone = {1}", infoData.Name, infoData.Phone); } 概念 查询示例 (LINQ to SQL) 标准查询运算符转换 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/76767e7c-0607-4e... 全部折叠 代码:C# LINQ to SQL 如何:返回两个序列之间的差集 (LINQ to SQL) 示例 请参见 发送反馈意见 使用 Except 运算符可返回两个序列之间的差集。 示例 请参见 此示例使用 Except 返回有 Customers 居住但无 Employees 居住的所有国家/地区的序列。 在 LINQ to SQL 中,Except 运算仅对集合而言是定义完善的。针对多重集的语义尚未定义。 C# 复制代码 var infoQuery = (from cust in db.Customers select cust.Country) .Except (from emp in db.Employees select emp.Country) ; 概念 查询示例 (LINQ to SQL) 标准查询运算符转换 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/62efb546-c898-408... 全部折叠 代码:C# LINQ to SQL 如何:返回两个序列的交集 (LINQ to SQL) 示例 请参见 发送反馈意见 使用 Intersect 运算符可返回两个序列的交集。 示例 请参见 此示例使用 Intersect 返回既有 Customers 居住又有 Employees 居住的所有国家/地区的序列。 在 LINQ to SQL 中,Intersect 运算仅对集合而言是定义完善的。针对多重集的语义尚未定义。 C# 复制代码 var infoQuery = (from cust in db.Customers select cust.Country) .Intersect (from emp in db.Employees select emp.Country) ; 概念 查询示例 (LINQ to SQL) 标准查询运算符转换 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/d09c344e-3548-49... 全部折叠 代码:C# LINQ to SQL 如何:返回两个序列的并集 (LINQ to SQL) 示例 请参见 发送反馈意见 使用 Union 运算符可返回两个序列的并集。 示例 请参见 此示例使用 Union 返回有 Customers 或 Employees 的所有国家/地区的序列。 在 LINQ to SQL 中,Union 运算符是为多重集定义的,定义为多重集的无序串联(实际上是 SQL 中的 UNION ALL 子句的执行结果)。 C# 复制代码 var infoQuery = (from cust in db.Customers select cust.Country) .Union (from emp in db.Employees select emp.Country) ; 概念 查询示例 (LINQ to SQL) 标准查询运算符转换 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/8b8bd3cb-86d4-4a... 全部折叠 代码:C# LINQ to SQL 如何:将序列转换为数组 (LINQ to SQL) 示例 请参见 发送反馈意见 使用 ToArray(TSource) 可从序列创建数组。 示例 请参见 下面的示例使用 ToArray(TSource) 直接将查询的计算结果放入数组并获取第三个元素。 C# 复制代码 var custQuery = from cust in db.Customers where cust.City == "London" select cust; Customer[] qArray = custQuery.ToArray(); 概念 查询示例 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/bf0af444-890d-43e... 全部折叠 代码:C# LINQ to SQL 如何:将序列转换为泛型列表 (LINQ to SQL) 示例 请参见 发送反馈意见 使用 ToList(TSource) 可从序列创建泛型列表。 示例 请参见 下面的示例使用 ToList(TSource) 直接将查询的计算结果放入泛型 List(T)。 C# 复制代码 var empQuery = from emp in db.Employees where emp.HireDate >= new DateTime(1994, 1, 1) select emp; List qList = empQuery.ToList(); 概念 查询示例 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/7ab76d93-6898-4e... 全部折叠 代码:C# LINQ to SQL 如何:将类型转换为泛型 IEnumerable (LINQ to SQL) 示例 请参见 发送反馈意见 使用 AsEnumerable(TSource) 可返回类型化为泛型 IEnumerable 的参数。 示例 请参见 在此示例中,LINQ to SQL(使用默认泛型 Query)会尝试将查询转换为 SQL 并在服务器上执行。但 where 子句引用用户定义的客户端方法 (isValidProduct),此方法无法转换为 SQL。 解决方法是指定 where 的客户端泛型 IEnumerable(T) 实现以替换泛型 IQueryable(T)。可通过调用 AsEnumerable(TSource) 运算符来执行此操作。 C# 复制代码 private bool isValidProduct(Product prod) { return prod.ProductName.LastIndexOf('C') == 0; } void ConvertToIEnumerable() { Northwnd db = new Northwnd(@"c:\test\northwnd.mdf"); Program pg = new Program(); var prodQuery = from prod in db.Products.AsEnumerable() where isValidProduct(prod) select prod; } 概念 查询示例 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/64774fb5-7447-42... 全部折叠 代码:C# LINQ to SQL 如何:构建联接和叉积查询 (LINQ to SQL) 示例 请参见 发送反馈意见 下面的示例演示如何组合来自多个表的结果。 示例 下面的示例在 Visual Basic 中的 From 子句(在 C# 中为 from 子句)中使用外键导航来选择位于伦敦的客 户所下的所有订单。 下面的示例在 Visual Basic 中的 Where 子句(在 C# 中为 where 子句)中使用外键导航来筛选出 Supplier 位于美国的脱销 Products。 下面的示例在 Visual Basic 中的 From 子句(在 C# 中为 from 子句)中使用外键导航来筛选出位于西雅图 的员工并列出它们所在的地区。 下面的示例在 Visual Basic 中的 Select 子句(在 C# 中为 select 子句)中使用外键导航来筛选出存在以下 关系的雇员对:其中一位雇员是另一位雇员的下属,且这两位雇员来自同一 City。 下面的 Visual Basic 示例查找所有客户和订单,确保相应的订单与客户匹配,并保证对于该列表中的每位客 户,都提供了联系人姓名。 下面的示例显式联接两个表并投影来自这两个表的结果。 C# 复制代码 var infoQuery = from cust in db.Customers from ord in cust.Orders where cust.City == "London" select ord; C# 复制代码 var infoQuery = from prod in db.Products where prod.Supplier.Country == "USA" && prod.UnitsInStock == 0 select prod; C# 复制代码 var infoQuery = from emp in db.Employees from empterr in emp.EmployeeTerritories where emp.City == "Seattle" select new { emp.FirstName, emp.LastName, empterr.Territory.TerritoryDescription }; C# 复制代码 var infoQuery = from emp1 in db.Employees from emp2 in emp1.Employees where emp1.City == emp2.City select new { FirstName1 = emp1.FirstName, LastName1 = emp1.LastName, FirstName2 = emp2.FirstName, LastName2 = emp2.LastName, emp1.City }; C# 复制代码 var q = from c in db.Customers 页码,1/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/d8072ede-0521-46... 请参见 下面的示例显式联接三个表并投影来自其中各个表的结果。 下面的示例演示如何通过使用 DefaultIfEmpty() 实现 LEFT OUTER JOIN。如果对应的 Employee 没有 Order,则 DefaultIfEmpty() 方法将返回 null。 下面的示例投影通过联接获得的 let 表达式。 下面的示例显示了一个带有组合键的 join。 下面的示例演示如何构造其中一端可以为 null、另一端不可以为 null 的 join。 join o in db.Orders on c.CustomerID equals o.CustomerID into orders select new { c.ContactName, OrderCount = orders.Count() }; C# 复制代码 var q = from c in db.Customers join o in db.Orders on c.CustomerID equals o.CustomerID into ords join e in db.Employees on c.City equals e.City into emps select new { c.ContactName, ords = ords.Count(), emps = emps.Count() }; C# 复制代码 var q = from e in db.Employees join o in db.Orders on e equals o.Employee into ords from o in ords.DefaultIfEmpty() select new { e.FirstName, e.LastName, Order = o }; C# 复制代码 var q = from c in db.Customers join o in db.Orders on c.CustomerID equals o.CustomerID into ords let z = c.City + c.Country from o in ords select new { c.ContactName, o.OrderID, z }; C# 复制代码 var q = from o in db.Orders from p in db.Products join d in db.OrderDetails on new { o.OrderID, p.ProductID } equals new { d.OrderID, d.ProductID } into details from d in details select new { o.OrderID, p.ProductID, d.UnitPrice }; C# 复制代码 var q = from o in db.Orders join e in db.Employees on o.EmployeeID equals (int?)e.EmployeeID into emps from e in emps select new { o.OrderID, e.FirstName }; 概念 查询示例 (LINQ to SQL) 页码,2/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/d8072ede-0521-46... 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/d8072ede-0521-46... 全部折叠 代码:C# LINQ to SQL 如何:构建投影 (LINQ to SQL) 示例 请参见 发送反馈意见 下面的示例演示如何将 C# 中的 select 语句和 Visual Basic 中的 Select 语句与其他功能结合使用以构建查询投 影。 示例 下面的示例使用 Visual Basic 中的 Select 子句(在 C# 中为 select 子句)返回由 Customers 的联系人姓名 组成的序列。 下面的示例使用 Visual Basic 中的 Select 子句(在 C# 中为 select 子句)和匿名类型返回由 Customers 的 联系人姓名和电话号码组成的序列。 下面的示例使用 Visual Basic 中的 Select 子句(在 C# 中为 select 子句)和匿名类型返回由雇员的姓名和 电话号码组成的序列。在产生的序列中,FirstName 和 LastName 字段组合成单个字段 (Name),HomePhone 字段重命名为 Phone。 下面的示例使用 Visual Basic 中的 Select 子句(在 C# 中为 select 子句)和匿名类型返回由所有 ProductID 和名为 HalfPrice 的计算所得的值组成的序列。此值设置为 UnitPrice 的 1/2。 下面的示例使用 Visual Basic 中的 Select 子句(在 C# 中为 select 子句)和一个条件语句返回由产品名和 产品可用性组成的序列。 下面的示例使用 Visual Basic Select 子句(在 C# 中为 select 子句)和一个已知类型 (Name) 返回由雇员的 姓名组成的序列。 C# 复制代码 var nameQuery = from cust in db.Customers select cust.ContactName; C# 复制代码 var infoQuery = from cust in db.Customers select new { cust.ContactName, cust.Phone }; C# 复制代码 var info2Query = from emp in db.Employees select new { Name = emp.FirstName + " " + emp.LastName, Phone = emp.HomePhone }; C# 复制代码 var specialQuery = from prod in db.Products select new { prod.ProductID, HalfPrice = prod.UnitPrice / 2 }; C# 复制代码 var prodQuery = from prod in db.Products select new { prod.ProductName, Availability = prod.UnitsInStock - prod.UnitsOnOrder < 0 ? "Out Of Stock" : "In Stock" }; C# 复制代码 public class Name { 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/745742df-0eda-479... 请参见 下面的示例使用 Visual Basic 中的 Select 和 Where(在 C# 中为 select 和 where)返回由位于伦敦的客 户的联系人姓名组成的筛选序列。 下面的示例使用 Visual Basic 中的 Select 子句(在 C# 中为 select 子句)和匿名类型返回有关客户的数据 的成形子集。 下面的示例使用嵌套查询返回以下结果: z 由所有订单及其对应的 OrderID 组成的序列。 z 由订单中具有折扣的项组成的子序列。 z 不含运费时节省的资金数额。 public string FirstName = ""; public string LastName = ""; } void empMethod() { Northwnd db = new Northwnd(@"c:\northwnd.mdf"); var empQuery = from emp in db.Employees select new Name { FirstName = emp.FirstName, LastName = emp.LastName }; } C# 复制代码 var contactQuery = from cust in db.Customers where cust.City == "London" select cust.ContactName; C# 复制代码 var custQuery = from cust in db.Customers select new { cust.CustomerID, CompanyInfo = new { cust.CompanyName, cust.City, cust.Country }, ContactInfo = new { cust.ContactName, cust.ContactTitle } }; C# 复制代码 var ordQuery = from ord in db.Orders select new { ord.OrderID, DiscountedProducts = from od in ord.OrderDetails where od.Discount > 0.0 select od, FreeShippingDiscount = ord.Freight }; 概念 查询示例 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/745742df-0eda-479... 全部折叠 代码:C# LINQ to SQL 做出和提交数据更改 (LINQ to SQL) 发送反馈意见 本节中的主题介绍如何做出更改并将更改传输至数据库,以及如何处理开放式并发冲突。 本节内容 注意: 您可以重写 Insert、Update 和 Delete 数据库操作的 LINQ to SQL 默认方法。有关更多信息,请参见自定义 插入、更新和删除操作 (LINQ to SQL)。 使用 Visual Studio 的开发人员可以使用对象关系设计器来开发用于实现同一目的的存储过程。有关更多信息, 请参见Object Relational Designer (O/R Designer) 和对象关系设计器(O/R 设计器). 如何:向数据库中插入行 (LINQ to SQL) 介绍如何通过向对象模型添加对象将行插入到数据库中。 如何:更新数据库中的行 (LINQ to SQL) 介绍如何通过更新对象模型中的对象来更新数据库中的行。 如何:从数据库中删除行 (LINQ to SQL) 介绍如何通过删除对象模型中的对象来删除数据库中的行。 如何:将更改提交到数据库 (LINQ to SQL) 介绍如何将对象模型更改发送到数据库。 如何:使用事务封闭数据提交 (LINQ to SQL) 介绍如何将操作包含在事务中。 如何:动态创建数据库 (LINQ to SQL) 介绍如何动态生成数据库,以及此方法的一些典型方案。 如何:管理更改冲突 (LINQ to SQL) 介绍用于处理开放式并发问题的技术。 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/d68c2dc3-99b3-49... 全部折叠 代码:C# LINQ to SQL 如何:向数据库中插入行 (LINQ to SQL) 示例 请参见 发送反馈意见 向数据库中插入行的方法为:将对象添加到关联的 LINQ to SQL Table(TEntity) 集合中,然后将所做的更改提交至数 据库。LINQ to SQL 会将您所做的更改转换成相应的 SQL INSERT 命令。 以下步骤假定您已通过有效的 DataContext 连接到 Northwind 数据库。有关更多信息,请参见如何:连接到数据库 (LINQ to SQL)。 向数据库中插入行 1. 创建一个包含要提交的列数据的新对象。 2. 将这个新对象添加到与数据库中的目标表关联的 LINQ to SQL Table 集合。 3. 将更改提交到数据库。 示例 请参见 注意: 您可以重写 Insert、Update 和 Delete 数据库操作的 LINQ to SQL 默认方法。有关更多信息,请参见自定义 插入、更新和删除操作 (LINQ to SQL)。 使用 Visual Studio 的开发人员可以使用对象关系设计器来开发用于实现同一目的的存储过程。有关更多信息, 请参见Object Relational Designer (O/R Designer) 和对象关系设计器(O/R 设计器). 下面的代码示例创建一个类型为 Order 的新对象,并用相应的值填充此对象。然后,它将这个新对象添加到 Order 集合中。最后,它将所做的更改提交到数据库中,使之成为 Orders 表中的一个新行。 C# 复制代码 // Create a new Order object. Order ord = new Order { OrderID = 12000, ShipCity = "Seattle", OrderDate = DateTime.Now // … }; // Add the new object to the Orders collection. db.Orders.InsertOnSubmit(ord); // Submit the change to the database. try { db.SubmitChanges(); } catch (Exception e) { Console.WriteLine(e); // Make some adjustments. // ... // Try again. db.SubmitChanges(); } 概念 如何:管理更改冲突 (LINQ to SQL) DataContext 方法(O/R 设计器) 如何:分配存储过程以执行更新、插入和删除操作(O/R 设计器) 做出和提交数据更改 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/44d99680-69c7-48... 全部折叠 代码:C# LINQ to SQL 如何:更新数据库中的行 (LINQ to SQL) 示例 请参见 发送反馈意见 您可以通过如下方法更新数据库中的行:修改与 LINQ to SQL Table(TEntity) 集合关联的对象的成员值,然后将所做 的更改提交至数据库。LINQ to SQL 会将您所做的更改转换成相应的 SQL UPDATE 命令。 以下步骤假定您已通过有效的 DataContext 连接到 Northwind 数据库。有关更多信息,请参见如何:连接到数据库 (LINQ to SQL)。 更新数据库中的行 1. 查询数据库中要更新的行。 2. 对得到的 LINQ to SQL 对象中的成员值进行所需的更改。 3. 将更改提交到数据库。 示例 请参见 注意: 您可以重写 Insert、Update 和 Delete 数据库操作的 LINQ to SQL 默认方法。有关更多信息,请参见自定义 插入、更新和删除操作 (LINQ to SQL)。 使用 Visual Studio 的开发人员可以使用对象关系设计器来开发用于实现同一目的的存储过程。有关更多信息, 请参见Object Relational Designer (O/R Designer) 和对象关系设计器(O/R 设计器). 下面的示例查询数据库中编号为 11000 的订单,然后更改所得到的 Order 对象中的 ShipName 和 ShipVia 值。最后,对这些成员值的更改将作为对 ShipName 和 ShipVia 列的更改提交到数据库。 C# 复制代码 // Query the database for the row to be updated. var query = from ord in db.Orders where ord.OrderID == 11000 select ord; // Execute the query, and change the column values // you want to change. foreach (Order ord in query) { ord.ShipName = "Mariner"; ord.ShipVia = 2; // Insert any additional changes to column values. } // Submit the changes to the database. try { db.SubmitChanges(); } catch (Exception e) { Console.WriteLine(e); // Provide for exceptions. } 概念 如何:管理更改冲突 (LINQ to SQL) 如何:分配存储过程以执行更新、插入和删除操作(O/R 设计器) 做出和提交数据更改 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/a2b5c90f-6cc3-412... 全部折叠 代码:C# LINQ to SQL 如何:从数据库中删除行 (LINQ to SQL) 示例 请参见 发送反馈意见 可以通过将对应的 LINQ to SQL 对象从其与表相关的集合中删除来删除数据库中的行。LINQ to SQL 会将更改转换为 相应的 SQL DELETE 命令。 LINQ to SQL 不支持且无法识别级联删除操作。如果要在对行有约束的表中删除行,则必须完成以下任务之一: z 在数据库的外键约束中设置 ON DELETE CASCADE 规则。 z 使用自己的代码首先删除阻止删除父对象的子对象。 否则会引发异常。请参见本主题中后面的第二个代码示例。 以下步骤假定您已通过有效的 DataContext 连接到 Northwind 数据库。有关更多信息,请参见如何:连接到数据库 (LINQ to SQL)。 删除数据库中的行 1. 查询数据库中要删除的行。 2. 调用 DeleteOnSubmit 方法。 3. 将更改提交到数据库。 示例 注意: 您可以重写 Insert、Update 和 Delete 数据库操作的 LINQ to SQL 默认方法。有关更多信息,请参见自定义 插入、更新和删除操作 (LINQ to SQL)。 使用 Visual Studio 的开发人员可以使用对象关系设计器来开发用于实现同一目的的存储过程。有关更多信息, 请参见Object Relational Designer (O/R Designer) 和对象关系设计器(O/R 设计器). 这第一个代码示例查询数据库中 11000 号订单的详细信息,将这些订单详细信息标记为删除,然后将这些更 改提交到数据库。 在第二个示例中,目的是删除订单(10250 号)。代码首先检查 OrderDetails 表以查看要删除的订单是否 有子项。如果订单有子项,则首先将子项标为删除,然后将订单标为删除。DataContext 为实际删除设置正确 的顺序,以使发送到数据库的删除命令遵守数据库约束。 C# 复制代码 // Query the database for the rows to be deleted. var deleteOrderDetails = from details in db.OrderDetails where details.OrderID == 11000 select details; foreach (var detail in deleteOrderDetails) { db.OrderDetails.DeleteOnSubmit(detail); } try { db.SubmitChanges(); } catch (Exception e) { Console.WriteLine(e); // Provide for exceptions. } C# 复制代码 Northwnd db = new Northwnd(@"c:\northwnd.mdf"); db.Log = Console.Out; // Specify order to be removed from database int reqOrder = 10250; // Fetch OrderDetails for requested order. var ordDetailQuery = 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/2144c99b-8055-40... 请参见 from odq in db.OrderDetails where odq.OrderID == reqOrder select odq; foreach (var selectedDetail in ordDetailQuery) { Console.WriteLine(selectedDetail.Product.ProductID); db.OrderDetails.DeleteOnSubmit(selectedDetail); } // Display progress. Console.WriteLine("detail section finished."); Console.ReadLine(); // Determine from Detail collection whether parent exists. if (ordDetailQuery.Any()) { Console.WriteLine("The parent is presesnt in the Orders collection."); // Fetch Order. try { var ordFetch = (from ofetch in db.Orders where ofetch.OrderID == reqOrder select ofetch).First(); db.Orders.DeleteOnSubmit(ordFetch); Console.WriteLine("{0} OrderID is marked for deletion.", ordFetch.OrderID); } catch (Exception e) { Console.WriteLine(e.Message); Console.ReadLine(); } } else { Console.WriteLine("There was no parent in the Orders collection."); } // Display progress. Console.WriteLine("Order section finished."); Console.ReadLine(); try { db.SubmitChanges(); } catch (Exception e) { Console.WriteLine(e.Message); Console.ReadLine(); } // Display progress. Console.WriteLine("Submit finished."); Console.ReadLine(); 概念 如何:管理更改冲突 (LINQ to SQL) 如何:分配存储过程以执行更新、插入和删除操作(O/R 设计器) 做出和提交数据更改 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/2144c99b-8055-40... 全部折叠 代码:C# LINQ to SQL 如何:将更改提交到数据库 (LINQ to SQL) 示例 请参见 发送反馈意见 无论您对对象做了多少项更改,都只是在更改内存中的副本。您并未对数据库中的实际数据做任何更改。直到您对 DataContext 显式调用 SubmitChanges,您所做的更改才会传输到服务器。 当您进行此调用时,DataContext 会设法将您所做的更改转换为等效的 SQL 命令。您可以使用自己的自定义逻辑来 重写这些操作,但提交顺序是由 DataContext 的一项称作“更改处理器”的服务来协调的。事件的顺序如下: 1. 当您调用 SubmitChanges 时,LINQ to SQL 会检查已知对象的集合以确定新实例是否已附加到它们。如果已 附加,这些新实例将添加到被跟踪对象的集合。 2. 所有具有挂起更改的对象将按照它们之间的依赖关系排序成一个对象序列。如果一个对象的更改依赖于其他 对象,则这个对象将排在其依赖项之后。 3. 在即将传输任何实际更改时,LINQ to SQL 会启动一个事务来封装由各条命令组成的系列。 4. 对对象的更改会逐个转换为 SQL 命令,然后发送到服务器。 此时,如果数据库检测到任何错误,都会造成提交进程停止并引发异常。将回滚对数据库的所有更改,就像未进行 过提交一样。DataContext 仍具有所有更改的完整记录。因此您可以设法修正问题并重新调用 SubmitChanges,就 像下面的代码示例中那样。 示例 请参见 用于执行提交的事务成功完成后,DataContext 就会通过忽略更改跟踪信息接受对对象的更改。 C# 复制代码 Northwnd db = new Northwnd(@"c:\northwnd.mdf"); // Make changes here. try { db.SubmitChanges(); } catch (ChangeConflictException e) { Console.WriteLine(e.Message); // Make some adjustments. // ... // Try again. db.SubmitChanges(); } 概念 如何:检测和解决提交冲突 (LINQ to SQL) 如何:管理更改冲突 (LINQ to SQL) 下载示例数据库 (LINQ to SQL) 做出和提交数据更改 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/c7cba174-9d40-49... 全部折叠 代码:C# LINQ to SQL 如何:使用事务封闭数据提交 (LINQ to SQL) 示例 请参见 发送反馈意见 您可以使用 TransactionScope 来封闭您提交到数据库的数据。有关更多信息,请参见事务 (LINQ to SQL)。 示例 请参见 下面的代码将数据库提交数据封闭在 TransactionScope 中。 C# 复制代码 Northwnd db = new Northwnd(@"c:\northwnd.mdf"); using (TransactionScope ts = new TransactionScope()) { try { Product prod1 = db.Products.First(p => p.ProductID == 4); Product prod2 = db.Products.First(p => p.ProductID == 5); prod1.UnitsInStock -= 3; prod2.UnitsInStock -= 5; db.SubmitChanges(); } catch (Exception e) { Console.WriteLine(e.Message); } } 概念 下载示例数据库 (LINQ to SQL) 做出和提交数据更改 (LINQ to SQL) 事务 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/94044a31-de90-47... 全部折叠 代码:C# LINQ to SQL 如何:动态创建数据库 (LINQ to SQL) 示例 请参见 发送反馈意见 实体类具有描述关系数据库表和列的结构的属性。您可以使用此信息来创建数据库的新实例。当对您的 DataContext 调用 CreateDatabase 方法时,LINQ to SQL 会用您的对象定义的结构构造一个新的数据库实例。 您可以在任意数量的方案中使用此功能,尤其是在已知的数据提供程序(如 SQL Server Express 2005)可用时更是 如此。典型的方案包括: z 您正在生成自动将自身安装到客户系统上的应用程序。 z 您正在生成需要用本地数据库来保存其脱机状态的客户端应用程序。 您还可以通过使用 .mdf 文件或只使用目录名(取决于您的连接字符串),将 CreateDatabase 与 SQL Server 一起 使用。LINQ to SQL 使用连接字符串来定义要创建的数据库和作为数据库创建位置的服务器。 示例 请参见 注意: 您的对象模型中的数据属性可能不会对有关现有数据库结构的所有内容都进行编码。属性不表示用户定义 的函数、存储过程、触发器和 CHECK 约束的内容。CreateDatabase 函数仅在对象模型中已编码的信息范围 内创建数据库的副本。这种行为足以满足各种数据库的需要。 下面的代码提供了一个示例,此示例演示了如何创建一个名为 MyDVDs.mdf 的新数据库。 您可以使用对象模型按如下方式创建数据库: LINQ to SQL 还提供了一个用于在创建新数据库前删除现有数据库的 API。您可以修改方案 1(在本文档前面 部分)中的代码,以便先检查是否存在现有版本的数据库。使用 DatabaseExists 和 DeleteDatabase 方法可实 现这种方法。在您调用 CreateDatabase 后,新的数据库就会存在并且会接受一般的查询和命令。 您可以使用诸如以下内容的代码来实现这种方法: C# 复制代码 public class MyDVDs : DataContext { public Table DVDs; public MyDVDs(string connection) : base(connection) { } } [Table(Name = "DVDTable")] public class DVD { [Column(IsPrimaryKey = true)] public string Title; [Column] public string Rating; } C# 复制代码 public void CreateDatabase() { MyDVDs db = new MyDVDs("c:\\mydvds.mdf"); db.CreateDatabase(); } C# 复制代码 public void CreateDatabase2() { MyDVDs db = new MyDVDs(@"c:\mydvds.mdf"); if (db.DatabaseExists()) { Console.WriteLine("Deleting old database..."); db.DeleteDatabase(); } db.CreateDatabase(); } 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/fb7f23c4-4572-4c3... 概念 背景信息 (LINQ to SQL) 做出和提交数据更改 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/fb7f23c4-4572-4c3... 全部折叠 代码:C# LINQ to SQL 如何:管理更改冲突 (LINQ to SQL) 发送反馈意见 LINQ to SQL 提供了一个 API 集合,用以帮助您发现、评估和解决并发冲突。 本节内容 相关章节 如何:检测和解决提交冲突 (LINQ to SQL) 介绍如何检测和解决并发冲突。 如何:指定并发异常的引发时间 (LINQ to SQL) 介绍如何指定应何时通知您出现并发冲突。 如何:指定测试哪些成员是否发生并发冲突 (LINQ to SQL) 介绍如何通过设置成员的属性来指定是否检查其有无并发冲突。 如何:检索实体冲突信息 (LINQ to SQL) 介绍如何收集有关实体冲突的信息。 如何:检索成员冲突信息 (LINQ to SQL) 介绍如何收集有关成员冲突的信息。 如何:通过保留数据库值解决并发冲突 (LINQ to SQL) 介绍如何用数据库值覆盖当前值。 如何:通过覆盖数据库值解决并发冲突 (LINQ to SQL) 介绍如何通过覆盖数据库值保留当前值。 如何:通过与数据库值合并解决并发冲突 (LINQ to SQL) 介绍如何通过将数据库值与当前值合并来解决冲突。 开放式并发概述 (LINQ to SQL) 解释与 LINQ to SQL 中的开放式并发有关的一些术语。 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/cd292c51-a3d1-4c... 全部折叠 代码:C# LINQ to SQL 如何:检测和解决提交冲突 (LINQ to SQL) 示例 请参见 发送反馈意见 LINQ to SQL 提供了许多资源,用于检测和解决因多个用户更改数据库而产生的冲突。有关更多信息,请参见如何:管 理更改冲突 (LINQ to SQL)。 示例 请参见 下面的示例显示了捕获 ChangeConflictException 异常的 try/catch 块。每个冲突的实体和成员信息会显示在控制 台窗口中。 注意: 您必须将 using System.Reflection 指令(在 Visual Basic 中为 Imports System.Reflection)包含在 内,以支持信息检索。有关更多信息,请参见 System.Reflection。 C# 复制代码 // using System.Reflection; Northwnd db = new Northwnd(@"c:\northwnd.mdf"); Customer newCust = new Customer(); newCust.City = "Auburn"; newCust.CustomerID = "AUBUR"; newCust.CompanyName = "AubCo"; db.Customers.InsertOnSubmit(newCust); try { db.SubmitChanges(ConflictMode.ContinueOnConflict); } catch (ChangeConflictException e) { Console.WriteLine("Optimistic concurrency error."); Console.WriteLine(e.Message); Console.ReadLine(); foreach (ObjectChangeConflict occ in db.ChangeConflicts) { MetaTable metatable = db.Mapping.GetTable(occ.Object.GetType()); Customer entityInConflict = (Customer)occ.Object; Console.WriteLine("Table name: {0}", metatable.TableName); Console.Write("Customer ID: "); Console.WriteLine(entityInConflict.CustomerID); foreach (MemberChangeConflict mcc in occ.MemberConflicts) { object currVal = mcc.CurrentValue; object origVal = mcc.OriginalValue; object databaseVal = mcc.DatabaseValue; MemberInfo mi = mcc.Member; Console.WriteLine("Member: {0}", mi.Name); Console.WriteLine("current value: {0}", currVal); Console.WriteLine("original value: {0}", origVal); Console.WriteLine("database value: {0}", databaseVal); } } } catch (Exception ee) { // Catch other exceptions. Console.WriteLine(ee.Message); } finally { Console.WriteLine("TryCatch block has finished."); } 概念 做出和提交数据更改 (LINQ to SQL) 如何:管理更改冲突 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/91e27206-01fb-4c7... 全部折叠 代码:C# LINQ to SQL 如何:指定并发异常的引发时间 (LINQ to SQL) 示例 请参见 发送反馈意见 在 LINQ to SQL 中,当因出现开放式并发冲突而导致对象不能更新时,会引发 ChangeConflictException 异常。有关 更多信息,请参见开放式并发概述 (LINQ to SQL)。 在向数据库提交您所做的更改前,您可以指定应何时引发并发异常: z 第一次失败时引发异常 (FailOnFirstConflict)。 z 完成所有更新尝试,积累所有失败,然后在异常中报告积累的失败 (ContinueOnConflict)。 引发 ChangeConflictException 异常时,该异常会提供对 ChangeConflictCollection 集合的访问。此集合提供了有关 每个冲突(映射到单个失败的更新尝试)的详细信息,包括对 MemberConflicts 集合的访问。每个成员冲突映射到 未通过并发检查的更新中的单个成员。 示例 请参见 下面的代码显示了这两个值的示例。 C# 复制代码 Northwnd db = new Northwnd("..."); // Create, update, delete code. db.SubmitChanges(ConflictMode.FailOnFirstConflict); // or db.SubmitChanges(ConflictMode.ContinueOnConflict); 概念 如何:管理更改冲突 (LINQ to SQL) 做出和提交数据更改 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/344ae068-ff63-4a2... 全部折叠 代码:C# LINQ to SQL 如何:指定测试哪些成员是否发生并发冲突 (LINQ to SQL) 示例 请参见 发送反馈意见 通过将三个枚举之一应用于 ColumnAttribute 属性 (Attribute) 的 LINQ to SQL UpdateCheck 属性 (Property),可指 定将哪些成员包含在用于检测开放式并发冲突的更新检查范围内。 UpdateCheck 属性(在设计时映射)与 LINQ to SQL 中的运行时并发功能一起使用。有关更多信息,请参见开放式 并发概述 (LINQ to SQL)。 有关代码示例,请参见UpdateCheck。 始终使用此成员检测冲突 1. 将 UpdateCheck 属性 (Property) 添加到 ColumnAttribute 属性 (Attribute)。 2. 将 UpdateCheck 属性值设置为 Always。 永不使用此成员检测冲突 1. 将 UpdateCheck 属性 (Property) 添加到 ColumnAttribute 属性 (Attribute)。 2. 将 UpdateCheck 属性值设置为 Never。 仅在应用程序已更改此成员的值时才使用此成员检测冲突 1. 将 UpdateCheck 属性 (Property) 添加到 ColumnAttribute 属性 (Attribute)。 2. 将 UpdateCheck 属性值设置为 WhenChanged。 示例 请参见 注意: 只要未将任何成员指定为 IsVersion=true,就会将原始成员值与当前数据库状态进行比较。有关更多信息, 请参见 IsVersion。 下面的示例指定在更新检查期间永远都不应该测试 HomePage 对象。有关更多信息,请参见 UpdateCheck。 C# 复制代码 [Column(Storage="_HomePage", DbType="NText", UpdateCheck=UpdateCheck.Never)] public string HomePage { get { return this._HomePage; } set { if ((this._HomePage != value)) { this.OnHomePageChanging(value); this.SendPropertyChanging(); this._HomePage = value; this.SendPropertyChanged("HomePage"); this.OnHomePageChanged(); } } } 概念 如何:管理更改冲突 (LINQ to SQL) 做出和提交数据更改 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/d2cda293-1e2f-487... 全部折叠 代码:C# LINQ to SQL 如何:检索实体冲突信息 (LINQ to SQL) 示例 请参见 发送反馈意见 您可以使用 ObjectChangeConflict 类的对象来提供有关 ChangeConflictException 异常指出的冲突的信息。有关更多 信息,请参见开放式并发概述 (LINQ to SQL)。 示例 请参见 下面的示例循环访问累积冲突的列表。 C# 复制代码 Northwnd db = new Northwnd("..."); try { db.SubmitChanges(ConflictMode.ContinueOnConflict); } catch (ChangeConflictException e) { Console.WriteLine("Optimistic concurrency error."); Console.WriteLine(e.Message); foreach (ObjectChangeConflict occ in db.ChangeConflicts) { MetaTable metatable = db.Mapping.GetTable(occ.Object.GetType()); Customer entityInConflict = (Customer)occ.Object; Console.WriteLine("Table name: {0}", metatable.TableName); Console.Write("Customer ID: "); Console.WriteLine(entityInConflict.CustomerID); Console.ReadLine(); } } 概念 如何:管理更改冲突 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/9a02b608-e7bb-40... 全部折叠 代码:C# LINQ to SQL 如何:检索成员冲突信息 (LINQ to SQL) 示例 请参见 发送反馈意见 您可以使用 MemberChangeConflict 类检索有关发生冲突的各成员的信息。在此上下文中,您可以提供任何成员的冲 突的自定义处理方法。有关更多信息,请参见开放式并发概述 (LINQ to SQL)。 示例 请参见 下面的代码循环访问 ObjectChangeConflict 对象。对于每个对象,它会接着循环访问 MemberChangeConflict 对象。 注意: 请将 System.Reflection 包含在内以提供 Member 信息。 C# 复制代码 // Add 'using System.Reflection' for this section. Northwnd db = new Northwnd("..."); try { db.SubmitChanges(ConflictMode.ContinueOnConflict); } catch (ChangeConflictException e) { Console.WriteLine("Optimistic concurrency error."); Console.WriteLine(e.Message); foreach (ObjectChangeConflict occ in db.ChangeConflicts) { MetaTable metatable = db.Mapping.GetTable(occ.Object.GetType()); Customer entityInConflict = (Customer)occ.Object; Console.WriteLine("Table name: {0}", metatable.TableName); Console.Write("Customer ID: "); Console.WriteLine(entityInConflict.CustomerID); foreach (MemberChangeConflict mcc in occ.MemberConflicts) { object currVal = mcc.CurrentValue; object origVal = mcc.OriginalValue; object databaseVal = mcc.DatabaseValue; MemberInfo mi = mcc.Member; Console.WriteLine("Member: {0}", mi.Name); Console.WriteLine("current value: {0}", currVal); Console.WriteLine("original value: {0}", origVal); Console.WriteLine("database value: {0}", databaseVal); Console.ReadLine(); } } } 概念 如何:管理更改冲突 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/7dd6829e-79a5-44... 全部折叠 代码:C# LINQ to SQL 如何:通过保留数据库值解决并发冲突 (LINQ to SQL) 示例 请参见 发送反馈意见 若要先协调预期数据库值与实际数据库值之间的差异,再尝试重新提交更改,则可以使用 OverwriteCurrentValues 保留在数据库中找到的值。然后会覆盖对象模型中的当前值。有关更多信息,请参见开放式并发概述 (LINQ to SQL)。 示例 请参见 注意: 在所有情况下,都会先通过从数据库中检索更新后的数据来刷新客户端上的记录。此操作确保了下一次更新尝 试将通过相同的并发检查。 在本方案中,当 User1 尝试提交更改时将引发 ChangeConflictException 异常,原因是 User2 同时已更改了 Assistant 和 Department 列。下表说明了这种情况。 User1 决定通过用更新的数据库值覆盖对象模型中的当前值来解决此冲突。 User1 通过使用 OverwriteCurrentValues 解决了此冲突后,数据库中的结果将如下表中所示: 下面的示例代码演示了如何用数据库值覆盖对象模型中的当前值。(未对各个成员冲突进行检查或自定义处 理。) Manager Assistant Department 原始数据库在被 User1 和 User2 查询时的状态。 Alfreds Maria Sales User1 准备提交这些更改。 Alfred Marketing User2 已经提交了这些更改。 Mary Service Manager Assistant Department 解决冲突后的新状态。 Alfreds (原始) Mary (来自 User2) Service (来自 User2) C# 复制代码 Northwnd db = new Northwnd("..."); try { db.SubmitChanges(ConflictMode.ContinueOnConflict); } catch (ChangeConflictException e) { Console.WriteLine(e.Message); foreach (ObjectChangeConflict occ in db.ChangeConflicts) { // All database values overwrite current values. occ.Resolve(RefreshMode.OverwriteCurrentValues); } } 概念 如何:管理更改冲突 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/b475cf72-9e64-4f6... 全部折叠 代码:C# LINQ to SQL 如何:通过覆盖数据库值解决并发冲突 (LINQ to SQL) 示例 请参见 发送反馈意见 若要先协调预期数据库值与实际数据库值之间的差异,再尝试重新提交更改,则可以使用 KeepCurrentValues 覆盖 数据库值。有关更多信息,请参见开放式并发概述 (LINQ to SQL)。 示例 请参见 注意: 在所有情况下,都会先通过从数据库中检索更新后的数据来刷新客户端上的记录。此操作确保了下一次更新尝 试将通过相同的并发检查。 在本方案中,当 User1 尝试提交更改时将引发 ChangeConflictException 异常,原因是 User2 同时已更改了 Assistant 和 Department 列。下表说明了这种情况。 User1 决定通过用当前客户端成员值覆盖数据库值来解决此冲突。 User1 通过使用 KeepCurrentValues 解决了此冲突后,数据库中的结果将如下表中所示: 下面的示例代码演示了如何用当前客户端成员值覆盖数据库值。(未对各个成员冲突进行检查或自定义处 理。) Manager Assistant Department 原始数据库在被 User1 和 User2 查询时的状态。 Alfreds Maria Sales User1 准备提交这些更改。 Alfred Marketing User2 已经提交了这些更改。 Mary Service Manager Assistant Department 解决冲突后的新状态。 Alfred (来自 User1) Maria (原始) Marketing (来自 User1) C# 复制代码 try { db.SubmitChanges(ConflictMode.ContinueOnConflict); } catch (ChangeConflictException e) { Console.WriteLine(e.Message); foreach (ObjectChangeConflict occ in db.ChangeConflicts) { //No database values are merged into current. occ.Resolve(RefreshMode.KeepCurrentValues); } } 概念 如何:管理更改冲突 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/fd6db0b8-c29c-48f... 全部折叠 代码:C# LINQ to SQL 如何:通过与数据库值合并解决并发冲突 (LINQ to SQL) 示例 请参见 发送反馈意见 若要先协调预期数据库值与实际数据库值之间的差异,再尝试重新提交更改,则可以使用 KeepChanges 将数据库值 与当前客户端成员值合并。有关更多信息,请参见开放式并发概述 (LINQ to SQL)。 示例 请参见 注意: 在所有情况下,都会先通过从数据库中检索更新后的数据来刷新客户端上的记录。此操作确保了下一次更新尝 试将通过相同的并发检查。 在本方案中,当 User1 尝试提交更改时将引发 ChangeConflictException 异常,原因是 User2 同时已更改了 Assistant 和 Department 列。下表说明了这种情况。 User1 决定通过将数据库值与当前客户端成员值合并来解决此冲突。结果将是,数据库值仅在当前变更集也修 改了该值时才会被覆盖。 User1 通过使用 KeepChanges 解决了此冲突后,数据库中的结果将如下表所示: 下面的示例演示如何将数据库值与当前客户端成员值合并(除非此客户端也已经更改了该值)。未对各个成员 冲突进行检查或自定义处理。 Manager Assistant Department 原始数据库在被 User1 和 User2 查询时的状态。 Alfreds Maria Sales User1 准备提交这些更改。 Alfred Marketing User2 已经提交了这些更改。 Mary Service Manager Assistant Department 解决冲突后的新状态。 Alfred (来自 User1) Mary (来自 User2) Marketing (来自 User1) C# 复制代码 try { db.SubmitChanges(ConflictMode.ContinueOnConflict); } catch (ChangeConflictException e) { Console.WriteLine(e.Message); // Automerge database values for members that client // has not modified. foreach (ObjectChangeConflict occ in db.ChangeConflicts) { occ.Resolve(RefreshMode.KeepChanges); } } // Submit succeeds on second try. db.SubmitChanges(ConflictMode.FailOnFirstConflict); 概念 如何:通过覆盖数据库值解决并发冲突 (LINQ to SQL) 如何:通过保留数据库值解决并发冲突 (LINQ to SQL) 如何:管理更改冲突 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/1988b79c-3bfc-4c5... 全部折叠 代码:C# LINQ to SQL 调试支持 (LINQ to SQL) 请参见 发送反馈意见 LINQ to SQL 为 LINQ to SQL 项目提供了常规调试支持。有关更多信息,请参见Debugging LINQ 和调试 LINQ. LINQ to SQL 还提供了用于查看 SQL 代码的特殊工具。有关更多信息,请参见本节中的各主题。 本节内容 请参见 如何:显示生成的 SQL (LINQ to SQL) 介绍如何使用 DataContext 属性查看查询活动。 如何:显示变更集 (LINQ to SQL) 介绍如何显示正发送到数据库的更改。 如何:显示 LINQ to SQL 命令 (LINQ to SQL) 介绍如何显示 SQL 命令及其他信息。 疑难解答 (LINQ to SQL) 提供可能难以确定其出现原因的一些常见情况。 概念 编程指南 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/262d8e60-8e71-42... 全部折叠 代码:C# LINQ to SQL 如何:显示生成的 SQL (LINQ to SQL) 示例 请参见 发送反馈意见 您可以通过使用 Log 属性查看为查询生成的 SQL 代码和更改处理方式。此方法对了解 LINQ to SQL 功能和调试特 定的问题可能很有用。 示例 请参见 下面的示例使用 Log 属性在 SQL 代码执行前在控制台窗口中显示此代码。 您可以将此属性与查询、插入、 更新和删除命令一起使用。 控制台窗口中显示的行就是您在执行后面的 Visual Basic 或 C# 代码时将看到的内容。 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].[City] = @p0 -- @p0: Input String (Size = 6; Prec = 0; Scale = 0) [London] -- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.20810.0 AROUT BSBEV CONSH EASTC NORTS SEVES C# 复制代码 db.Log = Console.Out; IQueryable custQuery = from cust in db.Customers where cust.City == "London" select cust; foreach(Customer custObj in custQuery) { Console.WriteLine(custObj.CustomerID); } 概念 调试支持 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/626492c0-5ee3-46... 全部折叠 代码:C# LINQ to SQL 如何:显示变更集 (LINQ to SQL) 示例 请参见 发送反馈意见 可以通过使用 GetChangeSet 来查看由 DataContext 跟踪的更改。 示例 下面的示例检索所在城市为伦敦的客户,将其所在城市更改为巴黎,然后将所做的更改提交回数据库。 执行此代码所得到的输出与如下内容类似。请注意,结尾处的摘要显示做了八项更改。 CustomerID: AROUT Original value: London Updated value: Paris CustomerID: BSBEV Original value: London Updated value: Paris CustomerID: CONSH Original value: London Updated value: Paris CustomerID: EASTC Original value: London Updated value: Paris CustomerID: NORTS Original value: London Updated value: Paris CustomerID: PARIS Original value: London Updated value: Paris CustomerID: SEVES Original value: London Updated value: Paris C# 复制代码 Northwnd db = new Northwnd(@"c:\northwnd.mdf"); var custQuery = from cust in db.Customers where cust.City == "London" select cust; foreach (Customer custObj in custQuery) { Console.WriteLine("CustomerID: {0}", custObj.CustomerID); Console.WriteLine("\tOriginal value: {0}", custObj.City); custObj.City = "Paris"; Console.WriteLine("\tUpdated value: {0}", custObj.City); } ChangeSet cs = db.GetChangeSet(); Console.Write("Total changes: {0}", cs); // Freeze the console window. Console.ReadLine(); db.SubmitChanges(); 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/126e7245-c5a0-4e... 请参见 CustomerID: SPECD Original value: London Updated value: Paris Total changes: {Added: 0, Removed: 0, Modified: 8} 概念 调试支持 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/126e7245-c5a0-4e... 全部折叠 代码:C# LINQ to SQL 如何:显示 LINQ to SQL 命令 (LINQ to SQL) 示例 请参见 发送反馈意见 使用 GetCommand 可显示 SQL 命令及其他信息。 示例 在下面的示例中,控制台窗口会显示执行查询所产生的输出,接着显示所生成的 SQL 命令、命令的类型和连 接的类型。 输出形式如下: Customers from London: Thomas Hardy Victoria Ashworth Elizabeth Brown Ann Devon Simon Crowther Marie Bertrand Hari Kumar Dominique Perrier Command Text: 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].[City] = @p0 Command Type: Text Connection: System.Data.SqlClient.SqlConnection C# 复制代码 // using System.Data.Common; Northwnd db = new Northwnd(@"c:\northwnd.mdf"); var q = from cust in db.Customers where cust.City == "London" select cust; Console.WriteLine("Customers from London:"); foreach (var z in q) { Console.WriteLine("\t {0}",z.ContactName); } DbCommand dc = db.GetCommand(q); Console.WriteLine("\nCommand Text: \n{0}",dc.CommandText); Console.WriteLine("\nCommand Type: {0}",dc.CommandType); Console.WriteLine("\nConnection: {0}",dc.Connection); Console.ReadLine(); 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/1decb05e-37ad-4ed... 请参见 概念 调试支持 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/1decb05e-37ad-4ed... 全部折叠 代码:C# LINQ to SQL 疑难解答 (LINQ to SQL) 请参见 发送反馈意见 下面的信息揭示您在 LINQ to SQL 应用程序中可能遇到的一些问题,并提供建议以避免这些问题或减少这些问题的 影响。 其他问题在常见问题 (LINQ to SQL) 中进行了解答。 不支持的标准查询运算符 内存问题 文件名和 SQLMetal 类库项目 级联删除 不可查询的表达式 DuplicateKeyException 字符串串联异常 SQL Server 2000 中的 Skip 和 Take 异常 LINQ to SQL 不支持某些标准查询运算符方法(例如,ElementAt(TSource))。因此,编译后的项目仍然可能 产生运行时错误。有关更多信息,请参见标准查询运算符转换 (LINQ to SQL)。 如果查询涉及到内存中的集合以及 LINQ to SQL Table(TEntity),则该查询可能在内存中执行,具体取决于指 定这两个集合的顺序。如果该查询必须在内存中执行,则需要检索数据库表中的数据。 此方法的效率十分低下,并可能占用大量内存和处理器时间。请尽量避免这种多域查询。 若要指定一个输入文件名,请将该名称作为输入文件添加到命令行。不支持在连接字符串中包含文件名(使 用 /conn 选项)。有关更多信息,请参见代码生成工具 (SqlMetal.exe)。 对象关系设计器会在项目的 app.config 文件中创建一个连接字符串。在类库项目中,不使用 app.config 文件。LINQ to SQL 使用在设计时文件中提供的连接字符串。更改 app.config 中的值不会更改应用程序连接 到的数据库。 LINQ to SQL 不支持或识别级联删除操作。如果要在表中删除一个具有约束的行,必须执行以下操作之一: z 在数据库的外键约束中设置 ON DELETE CASCADE 规则。 z 使用您自己的代码先删除阻止删除父对象的子对象。 否则,将引发 SqlException 异常。 有关更多信息,请参见如何:从数据库中删除行 (LINQ to SQL)。 如果收到错误消息“表达式 [表达式] 不可查询;是否缺少程序集引用?”,请确保满足以下要求: z 应用程序面向的是 .NET Compact Framework 3.5。 z 您具有对 System.Core.dll 和 System.Data.Linq.dll 的引用。 z 您具有用于 System.Linq 和 System.Data.Linq 的 Imports (Visual Basic) 或 using (C#) 指令。 在调试 LINQ to SQL 项目的过程中,可能会遍历某个实体的关系。这样做会使这些项进入缓存,而 LINQ to SQL 会检测到这些项的存在。如果随后试图执行 Attach 或 InsertOnSubmit,或者执行一个生成具有相同键的 多个行的类似方法,则会引发 DuplicateKeyException。 不支持映射到 [n]text 和其他 [n][var]char 的操作数的串联。映射到两个不同类型集的字符串的串联会引 发异常。有关更多信息,请参见 System.String 方法 (LINQ to SQL)。 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/8cd4401c-b12c-41... GroupBy InvalidOperationException OnCreated() 分部方法 请参见 在对 SQL Server 2000 数据库使用 Take(TSource) 或 Skip(TSource) 时,必须使用标识成员 (IsPrimaryKey)。查询必须针对单个表(即,不是联接),或必须为 Distinct、Except、Intersect 或 Union 操 作,且不得包含 Concat(TSource) 操作。有关更多信息,请参见标准查询运算符转换 (LINQ to SQL) 中的 “SQL Server 2000 支持”部分。 此要求不适用于 SQL Server 2005。 如果在按 boolean 表达式进行分组的 GroupBy 查询(如 group x by (Phone==@phone))中有一个列值为 null,则会引发此异常。因为表达式的类型为 boolean,所以会将键的类型推理为 boolean 而不是 nullable boolean。在转换后的比较生成 null 值时,系统会试图将一个 nullable boolean 值赋给一个 boolean,从 而引发该异常。 若要避免发生这种情况(假定您希望将 null 视为 false),请使用如下方式: GroupBy="(Phone != null) && (Phone=@Phone)" 每次调用对象构造函数时都会调用生成的方法 OnCreated(),这包括以下这种情况,即 LINQ to SQL 调用构 造函数以生成原始值的副本。如果您在自己的分部类中实现 OnCreated() 方法,请考虑此行为。 概念 调试支持 (LINQ to SQL) 常见问题 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/8cd4401c-b12c-41... 全部折叠 代码:C# LINQ to SQL 背景信息 (LINQ to SQL) 发送反馈意见 本节中的主题介绍有关使用 LINQ to SQL 的基础知识以外的概念和过程。 请按以下这些步骤查找 LINQ to SQL 代码和应用程序的其他示例: z 在 MSDN Library 中搜索特定问题。 z 参与 LINQ Forum(LINQ 论坛),在这里您可以与专家们详细讨论更复杂的主题。 z 学习详细介绍 LINQ to SQL 技术并包含 Visual Basic 和 C# 代码示例的白皮书。有关更多信息,请参见 LINQ to SQL: .NET Language-Integrated Query for Relational Data(LINQ to SQL:关系数据的 .NET 语言集成查询)。 本节内容 相关章节 ADO.NET 与 LINQ to SQL 介绍 ADO.NET 与 LINQ to SQL 的关系。 分析 LINQ to SQL 源代码 介绍如何通过从 Northwind 示例数据库生成和查看源代码来分析 LINQ to SQL 映射。 自定义插入、更新和删除操作 (LINQ to SQL) 介绍如何添加验证代码及其他自定义内容。 数据绑定 (LINQ to SQL) 介绍 LINQ to SQL 如何使用 IListSource 支持数据绑定。 继承支持 (LINQ to SQL) 介绍继承在 LINQ to SQL 对象模型中的作用,以及如何在查询中使用相关运算符。 本地方法调用 (LINQ to SQL) 介绍 LINQ to SQL 对局部方法调用的支持。 N 层应用程序中的数据检索和 CUD 操作 (LINQ to SQL) 提供有关使用 LINQ to SQL 的多层应用程序的详细信息。 对象标识 (LINQ to SQL) 介绍 LINQ to SQL 对象模型中的对象标识,并解释此功能与数据库中的对象标识的不同之处。 LINQ to SQL 对象模型 介绍对象模型及其与关系数据模型的关系。 对象状态与更改跟踪 (LINQ to SQL) 提供有关 LINQ to SQL 如何跟踪更改的详细信息。 开放式并发概述 (LINQ to SQL) 介绍了开放式并发并定义了一些术语。 LINQ to SQL 中的查询概念 介绍 LINQ to SQL 中的查询与 LINQ 中的查询不同的方面。 LINQ to SQL 中的安全性 介绍确保数据库连接安全的正确方法。 序列化 (LINQ to SQL) 介绍 LINQ to SQL 应用程序中的序列化过程。 存储过程 (LINQ to SQL) 介绍如何在设计时映射存储过程以及如何从应用程序中调用它们。 事务 (LINQ to SQL) 概述了 LINQ to SQL 支持的三种事务模型。 类型系统不匹配 (LINQ to SQL) 介绍了在结合不同类型系统方面存在的挑战。 用户定义的函数 (LINQ to SQL) 介绍如何在设计时映射用户定义的函数以及如何从应用程序中调用它们。 编程指南 (LINQ to SQL) 包含一些链接,这些链接指向解释 LINQ to SQL 的各个方面的各节。 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/724888f2-d3f1-4be... 全部折叠 代码:C# LINQ to SQL ADO.NET 与 LINQ to SQL 请参见 发送反馈意见 LINQ to SQL 是 ADO.NET 系列技术的一部分。它基于由 ADO.NET 提供程序模型提供的服务。因此,您可以将 LINQ to SQL 代码与现有的 ADO.NET 应用程序混合在一起,将当前 ADO.NET 解决方案迁移到 LINQ to SQL。下图高度概 括了这种关系。 连接 事务 在创建 LINQ to SQL DataContext 时,可以提供现有 ADO.NET 连接。对 DataContext 的所有操作(包括查 询)都使用所提供的这个连接。如果此连接已经打开,则在您使用完此连接时,LINQ to SQL 会保持它的打开 状态不变。 您始终可以访问此连接,并可以使用 Connection 属性自行关闭它,如下面的代码所示: C# 复制代码 string connString = @"Data Source=.\SQLEXPRESS;AttachDbFilename=c:\northwind.mdf; Integrated Security=True; Connect Timeout=30; User Instance=True"; SqlConnection nwindConn = new SqlConnection(connString); nwindConn.Open(); Northwnd interop_db = new Northwnd(nwindConn); SqlTransaction nwindTxn = nwindConn.BeginTransaction(); try { SqlCommand cmd = new SqlCommand( "UPDATE Products SET QuantityPerUnit = 'single item' WHERE ProductID = 3"); cmd.Connection = nwindConn; cmd.Transaction = nwindTxn; cmd.ExecuteNonQuery(); interop_db.Transaction = nwindTxn; Product prod1 = interop_db.Products .First(p => p.ProductID == 4); Product prod2 = interop_db.Products .First(p => p.ProductID == 5); prod1.UnitsInStock -= 3; prod2.UnitsInStock -= 5; interop_db.SubmitChanges(); nwindTxn.Commit(); } catch (Exception e) { Console.WriteLine(e.Message); Console.WriteLine("Error submitting changes... all changes rolled back."); } nwindConn.Close(); C# 复制代码 db.Connection.Close(); 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/49ac6da0-f2e1-46f... 直接 SQL 命令 请参见 当您的应用程序已经启动了您自己的数据库事务并且您希望您的 DataContext 包含在内时,您可以向您的 DataContext 提供此事务。 通过 .NET Framework 创建事务的首选方法是使用 TransactionScope 对象。通过使用此方法,您可以创建跨 数据库及其他驻留在内存中的资源管理器执行的分布式事务。事务范围几乎不需要资源就可以启动。它们仅在 事务范围内存在多个连接时才将自身提升为分布式事务。 不能将此方法用于所有数据库。例如,SqlClient 连接在针对 SQL Server 2000 服务器使用时无法提升系统事 务。它采取的方法是,只要它发现有使用事务范围的情况,它就会自动向完整的分布式事务登记。 C# 复制代码 using (TransactionScope ts = new TransactionScope()) { db.SubmitChanges(); ts.Complete(); } 有时您可能会遇到这样的情况:DataContext 查询或提交更改的能力不足以满足您需要执行的专门任务的需 要。在这些情况下,您可以使用 ExecuteQuery 方法向数据库发出 SQL 命令,将查询结果转换成对象。 例如,假定 Customer 类的数据分布在两个表(customer1 和 customer2)中。下面的查询将返回 Customer 对象的序列: 只要表格结果中的列名与您的实体类的列属性匹配,LINQ to SQL 就会为您创建不在任何 SQL 查询范围之内 的对象。 参数 ExecuteQuery 方法接受参数。下面的代码执行参数化查询: C# 复制代码 IEnumerable results = db.ExecuteQuery( @"select c1.custid as CustomerID, c2.custName as ContactName from customer1 as c1, customer2 as c2 where c1.custid = c2.custid" ); C# 复制代码 IEnumerable results = db.ExecuteQuery( "select contactname from customers where city = {0}", "London" ); 注意: 在查询文本中使用 Console.WriteLine() 和 String.Format() 所用的大括号表示法来表示参数。 String.Format() 获取您提供的查询字符串,然后将括在大括号内的参数替换为所生成的参数名,如 @p0、@p1 … @p(n)。 概念 背景信息 (LINQ to SQL) 如何:重用 ADO.NET 命令和 DataContext 之间的连接 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/49ac6da0-f2e1-46f... 全部折叠 代码:C# LINQ to SQL 分析 LINQ to SQL 源代码 请参见 发送反馈意见 通过按以下步骤操作,您可以用 Northwind 示例数据库生成 LINQ to SQL 源代码。您可以将对象模型的元素与数据 库的元素作一个比较,以便更好地了解不同项的映射方式。 1. 如果您的开发计算机上还没有 Northwind 示例数据库,您可以免费下载它。有关更多信息,请参见下载示例 数据库 (LINQ to SQL)。 2. 使用 SqlMetal 命令行工具生成 Visual Basic 或 C# 源文件。有关更多信息,请参见代码生成工具 (SqlMetal.exe)。通过在命令提示符处键入以下命令,您可以生成包含存储过程和函数的 Visual Basic 和 C# 源文件。 z sqlmetal /code:northwind.vb /language:vb "c:\northwnd.mdf" /sprocs /functions /pluralize z sqlmetal /code:northwind.cs /language:csharp "c:\northwnd.mdf" /sprocs /functions /pluralize 请参见 注意: 使用 Visual Studio 的开发人员可以使用 O/R 设计器来生成此代码。有关更多信息,请参见Object Relational Designer (O/R Designer) 和对象关系设计器(O/R 设计器). 概念 参考 (LINQ to SQL) 背景信息 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/cba3eef8-e108-447... 全部折叠 代码:C# LINQ to SQL 自定义插入、更新和删除操作 (LINQ to SQL) 发送反馈意见 默认情况下,LINQ to SQL 会生成动态 SQL 来实现插入、读取、更新和删除操作。但实际上,您通常要自定义应用 程序以满足您的业务需要。 本节中的主题介绍了 LINQ to SQL 提供的用于在应用程序中自定义插入、读取、更新和删除操作的技术。 本节内容 注意: 如果您使用的是 Visual Studio,则可以使用对象关系设计器来自定义插入、更新和删除操作。 自定义操作:概述 (LINQ to SQL) 介绍 LINQ to SQL 提供的用于自定义插入、读取、更新和删除操作的各种技术。 插入、更新和删除操作 (LINQ to SQL) 介绍用于操作数据库数据的 LINQ to SQL 默认过程。 开发人员在重写默认行为方面的责任 (LINQ to SQL) 介绍开发人员在实现非 LINQ to SQL 强制需求方面的责任。 使用分部方法添加业务逻辑 (LINQ to SQL) 介绍如何使用分部方法重写自动生成的方法。 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/07eef055-8f6c-414... 全部折叠 代码:C# LINQ to SQL 自定义操作:概述 (LINQ to SQL) 请参见 发送反馈意见 默认情况下,LINQ to SQL 会根据映射生成动态 SQL 来执行插入、更新和删除操作。但在实践中,您通常需要添加 您自己的业务逻辑来提供安全、验证等。 用于自定义这些操作的 LINQ to SQL 技术包括: 加载选项 分部方法 存储过程和用户定义的函数 请参见 在您的查询中,您可以控制在连接到数据库时检索多少与您的主目标相关的数据。此功能主要通过使用 DataLoadOptions 实现。有关更多信息,请参见延迟加载与立即加载的比较 (LINQ to SQL)。 LINQ to SQL 在其默认映射中提供了分部方法,以帮助您实现自己的业务逻辑。有关更多信息,请参见使用分 部方法添加业务逻辑 (LINQ to SQL)。 LINQ to SQL 支持使用存储过程和用户定义的函数。存储过程经常用来自定义操作。有关更多信息,请参见存 储过程 (LINQ to SQL)。 概念 自定义插入、更新和删除操作 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/a3546296-1443-4b... 全部折叠 代码:C# LINQ to SQL 插入、更新和删除操作 (LINQ to SQL) 请参见 发送反馈意见 在 LINQ to SQL 中执行 Insert、Update 和 Delete 操作的方法是:向对象模型中添加对象、更改和移除对象模型 中的对象。默认情况下,LINQ to SQL 会将您所做的操作转换成 SQL,然后将这些更改提交至数据库。 LINQ to SQL 在操作和保持对对象所做更改方面有着最大的灵活性。实体对象可用(通过查询检索它们或通过重新构 造它们)后,就可以像应用程序中的典型对象一样更改实体对象。也就是说,可以更改它们的值,将它们添加到集 合,以及从集合中移除它们。LINQ to SQL 会跟踪您所做的更改,并且在您调用 SubmitChanges 时就可以将这些更 改传回数据库。 以下摘录会用到 Northwind 示例数据库中的 Customer 和 Order 类。为简洁起见,不显示类定义。 当您调用 SubmitChanges 时,LINQ to SQL 会自动生成并执行它为将您所做的更改传回数据库而必须具备的 SQL 命 令。 请参见 注意: LINQ to SQL 不支持且无法识别级联删除操作。如果要在对行有约束的表中删除行,则必须在数据库的外键约束 中设置 ON DELETE CASCADE 规则,或者使用自己的代码首先删除防止删除父对象的子对象。否则会引发异 常。有关更多信息,请参见如何:从数据库中删除行 (LINQ to SQL)。 C# 复制代码 Northwnd db = new Northwnd(@"c:\Northwnd.mdf"); // Query for a specific customer. var cust = (from c in db.Customers where c.CustomerID == "ALFKI" select c).First(); // Change the name of the contact. cust.ContactName = "New Contact"; // Create and add a new Order to the Orders collection. Order ord = new Order { OrderDate = DateTime.Now }; cust.Orders.Add(ord); // Delete an existing Order. Order ord0 = cust.Orders[0]; // Removing it from the table also removes it from the Customer’s list. db.Orders.DeleteOnSubmit(ord0); // Ask the DataContext to save all the changes. db.SubmitChanges(); 注意: 您可以使用自己的自定义逻辑来重写此行为,这通常是通过存储过程来实现的。有关更多信息,请参见开发人 员在重写默认行为方面的责任 (LINQ to SQL)。 使用 Visual Studio 的开发人员可以使用对象关系设计器来开发用于实现此目的的存储过程。有关更多信息,请 参见Object Relational Designer (O/R Designer) 和对象关系设计器(O/R 设计器). 概念 下载示例数据库 (LINQ to SQL) 自定义插入、更新和删除操作 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/26a43a4f-83c9-473... 全部折叠 代码:C# LINQ to SQL 开发人员在重写默认行为方面的责任 (LINQ to SQL) 请参见 发送反馈意见 LINQ to SQL 不强制满足以下要求,但如果这些要求未得到满足,就会导致行为不明确。 z 重写方法不能调用 SubmitChanges 或 Attach。如果在重写方法中调用这些方法,LINQ to SQL 会引发异常。 z 重写方法不能用来启动、提交或停止事务。SubmitChanges 操作以事务的形式执行。内嵌的事务可能会干扰外 层事务。加载重写方法只能在它们确定 Transaction 中未在执行相应操作后启动事务。 z 重写方法应遵循适用的开放式并发映射。发生开放式并发冲突时,重写方法应引发 ChangeConflictException。 LINQ to SQL 会捕获此异常,以便您可以正确处理 SubmitChanges 时提供的 SubmitChanges 选项。 z Create (Insert) 和 Update 重写方法在相应操作成功完成后应使数据库生成的列的值流回对应的对象成员。 例如,如果 Order.OrderID 映射到标识列(autoincrement 主键),则 InsertOrder() 重写方法必须检索数 据库生成的 ID 并将 Order.OrderID 成员设置为该 ID。同样,时间戳成员必须更新为数据库生成的时间戳 值,以确保更新后的对象一致。如果未能传播数据库生成的值,则会造成数据库与 DataContext 跟踪的对象之 间不一致。 z 调用正确的动态 API 是用户的责任。例如,在更新重写方法中,只能调用 ExecuteDynamicUpdate。LINQ to SQL 不检测或验证调用的动态方法是否与适用的操作匹配。如果调用了不适用的方法(例如,为要更新的对象 调用了 ExecuteDynamicDelete),则结果是不明确的。 z 最后,重写方法应执行明确的操作。LINQ to SQL 操作(如紧急加载、延迟加载和 SubmitChanges)的语义要 求重写提供明确的服务。例如,只返回空集合而不检查数据库内容的加载重写有可能会造成数据不一致。 请参见 概念 自定义插入、更新和删除操作 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/c6909ddd-e053-46... 全部折叠 代码:C# LINQ to SQL 使用分部方法添加业务逻辑 (LINQ to SQL) 请参见 发送反馈意见 可以通过使用分部方法在 LINQ to SQL 项目中自定义 Visual Basic 和 C# 生成的代码。LINQ to SQL 生成的代码定义 签名作为分部方法的一部分。如果您要实现此方法,您可以添加自己的分部方法。如果您不添加自己的实现,编译 器将丢弃分部方法签名并调用 LINQ to SQL 中的默认方法。 例如,Northwind 示例数据库中 Customer 类的默认映射包括下面的分部方法: 您可以向自己的分部 Customer 类添加诸如以下内容的代码来实现自己的方法。 在 LINQ to SQL 中通常使用这种方式来重写 Insert、Update、Delete 的默认方法以及在对象生命周期事件过程中 验证属性。 有关更多信息,请参见分部方法 (Visual Basic) 或 分部(方法) (C#)。 示例 示例 注意: 如果您使用的是 Visual Studio,您可以使用对象关系设计器来向实体类模型添加验证及其他自定义内容。 C# 复制代码 partial void OnAddressChanged(); C# 复制代码 public partial class Customer { partial void OnAddressChanged(); partial void OnAddressChanged() { // Insert business logic here. } } 说明 下面的示例首先显示了 ExampleClass,因为它可能是由像 SQLMetal 这样的代码生成工具定义的;然后,此 示例演示了如何只实现两个方法之一。 代码 C# 复制代码 // Code-generating tool defines a partial class, including // two partial methods. partial class ExampleClass { partial void onFindingMaxOutput(); partial void onFindingMinOutput(); } // Developer implements one of the partial methods. Compiler // discards the signature of the other method. partial class ExampleClass { partial void onFindingMaxOutput() { Console.WriteLine("Maximum has been found."); } } 说明 下面的示例用到了 Shipper 和 Order 实体之间的关系。请注意这些方法中的分部方法 InsertShipper 和 DeleteShipper。这两个方法重写了由 LINQ to SQL 映射提供的默认分部方法。 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/3a73991e-fd4e-461... 请参见 代码 C# 复制代码 public static int LoadOrdersCalled = 0; private IEnumerable LoadOrders(Shipper shipper) { LoadOrdersCalled++; return this.Orders.Where(o => o.ShipVia == shipper.ShipperID); } public static int LoadShipperCalled = 0; private Shipper LoadShipper(Order order) { LoadShipperCalled++; return this.Shippers.Single(s => s.ShipperID == order.ShipVia); } public static int InsertShipperCalled = 0; partial void InsertShipper(Shipper shipper) { InsertShipperCalled++; // Call a Web service to perform an insert operation. InsertShipperService(shipper); } public static int UpdateShipperCalled = 0; private void UpdateShipper(Shipper original, Shipper current) { Shipper shipper = new Shipper(); UpdateShipperCalled++; // Call a Web service to update shipper. InsertShipperService(shipper); } public static bool DeleteShipperCalled; partial void DeleteShipper(Shipper shipper) { DeleteShipperCalled = true; } 概念 做出和提交数据更改 (LINQ to SQL) 自定义插入、更新和删除操作 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/3a73991e-fd4e-461... 全部折叠 代码:C# LINQ to SQL 数据绑定 (LINQ to SQL) 请参见 发送反馈意见 LINQ to SQL 支持绑定到公共控件,如网格控件。具体而言,LINQ to SQL 定义用于绑定到数据网格和处理主-从绑定 的基本模式,这两者都与显示和更新有关。 基本原理 操作 IListSource 实现 专用集合 LINQ to SQL 将 LINQ 查询转换成 SQL 以便在数据库上执行。所得结果为强类型化的 IEnumerables。由于 这些对象是一般的公共语言运行库 (CLR) 对象,因此可以使用一般的对象数据绑定来显示结果。另一方面, 更改操作(插入、更新和删除)则需要额外的步骤。 隐式绑定到 Windows 窗体控件是通过实现 IListSource 完成的。数据源的泛型 Table(TEntity)(C# 中的 Table 或 Visual Basic 中的 Table(Of T))和泛型 DataQuery 已得到了更新,以便实现 IListSource。 用户界面 (UI) 的数据绑定引擎(Windows 窗体和 Windows Presentation Foundation)都会测试其数据源是否 实现了 IListSource。因此,如果将查询的直接影响结果写入控件的数据源,则会隐式调用 LINQ to SQL 集合 生成,如下例中所示: 对于 Windows Presentation Foundation 而言也是如此: 集合生成是由泛型 Table(TEntity) 和泛型 DataQuery 在 GetList 中实现的。 C# 复制代码 DataGrid dataGrid1 = new DataGrid(); DataGrid dataGrid2 = new DataGrid(); DataGrid dataGrid3 = new DataGrid(); var custQuery = from cust in db.Customers select cust; dataGrid1.DataSource = custQuery; dataGrid2.DataSource = custQuery; dataGrid2.DataMember = "Orders"; BindingSource bs = new BindingSource(); bs.DataSource = custQuery; dataGrid3.DataSource = bs; C# 复制代码 ListView listView1 = new ListView(); var custQuery2 = from cust in db.Customers select cust; ListViewItem ItemsSource = new ListViewItem(); ItemsSource = (ListViewItem)custQuery2; LINQ to SQL 在两个位置实现 IListSource: z 数据源为 Table(TEntity):LINQ to SQL 浏览相应的表以填充保留对该表的引用的 DataBindingList 集 合。 z 数据源为 IQueryable(T)。存在两种情况: z 如果 LINQ to SQL 从 IQueryable(T) 中找到基础 Table(TEntity),则源允许进行编辑,这种情形与第 一项中的情形相同。 z 如果 LINQ to SQL 找不到基础 Table(TEntity),则源不允许进行编辑(例如,groupby)。LINQ to SQL 会浏览相应的查询以填充泛型 SortableBindingList(是一个简单的 BindingList(T),实现根据 给定属性对 T 实体进行排序的功能)。 对于本文档前面部分介绍的许多功能而言,已将 BindingList(T) 专门用于某些不同的类。这些类为泛型 页码,1/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/cbec8b02-a1e8-4ae... 绑定到 EntitySet 缓存 取消 疑难解答 SortableBindingList 和泛型 DataBindingList。这两种类都声明为内部类。 泛型 SortableBindingList 此类继承自 BindingList(T),是 BindingList(T) 的可排序版本。排序是在内存中完成的解决方案,绝不会接触 到数据库本身。BindingList(T) 实现 IBindingList,但默认情况下不支持排序。但是,BindingList(T) 使用虚拟 核心方法实现 IBindingList。您可以很容易地重写这些方法。泛型 SortableBindingList 可以重写 SupportsSortingCore、SortPropertyCore、SortDirectionCore 和 ApplySortCore。ApplySortCore 由 ApplySort 调用,用于根据给定属性对 T 项列表进行排序。 如果相应属性不属于 T,则会引发异常。 为实现排序,LINQ to SQL 可以创建一个泛型 SortableBindingList.PropertyComparer 类,此类继承自泛型 IComparer.Compare 并可以实现一个用于给定类型 T 的默认比较器、一个 PropertyDescriptor 和一个方 向。此类可以动态地创建 T 的 Comparer,其中 T 是 PropertyDescriptor 的 PropertyType。然后,将从 静态泛型 Comparer 中检索默认比较器。默认实例是通过使用反射获取的。 泛型 SortableBindingList 也是 DataBindingList 的基类。泛型 SortableBindingList 提供两个虚方法,用 于挂起或继续项的添加/移除跟踪。这两个方法可以用于像排序这样的基本功能,但实际上将由像泛型 DataBindingList 这样的上层类实现。 泛型 DataBindingList 此类继承自泛型 SortableBindingLIst。泛型 DataBindingList 保留对用于在最初填充集合的泛型 IQueryable 的基础泛型 Table 的引用。泛型 DatabindingList 通过重写 InsertItem() 和 RemoveItem() 将对项添加/移除操作的跟踪添加到集合。它还可以实现抽象的挂起/继续跟踪功能,以使跟踪成为有条件的跟 踪。此功能使泛型 DataBindingList 能利用父类跟踪功能的所有多态用法。 绑定到 EntitySet 是一种特殊情况,因为 EntitySet 已经是实现 IBindingList 的集合。LINQ to SQL 增添排序 并取消 (ICancelAddNew) 支持。EntitySet 类使用内部列表来存储实体。此列表是一个基于泛型数组(泛型 ItemList 类)的底层集合。 添加排序功能 数组提供一个可以与 T 的 Comparer 一起使用的排序方法 (Array.Sort())。LINQ to SQL 使用本主题前面 部分介绍的泛型 SortableBindingList.PropertyComparer 类来获取属性的这个 Comparer 以及排序方向。 将 ApplySort 方法添加到泛型 ItemList,以调用此功能。 在 EntitySet 端,您现在必须声明排序支持: z SupportsSorting 返回 true。 z ApplySort 调用 entities.ApplySort(),然后调用 OnListChanged()。 z SortDirection 和 SortProperty 属性公开存储在本地成员中的当前排序定义。 LINQ to SQL 查询实现 GetList。当 Windows 窗体 BindingSource 类遇到此接口时,它会为单个连接调用 GetList() 三次。为避免出现这种情况,LINQ to SQL 为每个实例实现一个缓存,以存储并始终返回生成的同一 个集合。 IBindingList 定义一个 AddNew 方法,控件使用此方法从绑定的集合中创建新项。DataGridView 控件在最后 一个可见行在其标题中包含星号时会很好地演示此功能。此星号向您说明您可以添加新项。 除此功能以外,集合还可以实现 ICancelAddNew。此功能允许控件取消新编辑的项或验证此项是否已经过验 证。 ICancelAddNew 在所有 LINQ to SQL 数据绑定集合(泛型 SortableBindingList 和泛型 EntitySet)中均得 到了实现。在这两种实现中,代码均按如下方式执行: z 允许向集合中插入项,然后将其从集合中移除。 z 只要 UI 不提交编辑内容,就不会跟踪更改。 z 只要已取消 (CancelNew) 编辑,就不会跟踪更改。 z 允许在提交 (EndNew) 编辑内容时进行跟踪。 z 如果新项不是来自 AddNew,则让集合正常工作。 页码,2/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/cbec8b02-a1e8-4ae... 请参见 本部分提出几点可能有助于您解决 LINQ to SQL 数据绑定应用程序难题的事项。 z 您必须使用属性;仅使用字段是不够的。Windows 窗体需要这种用法。 z 默认情况下,image、varbinary 和 timestamp 数据库类型映射到字节数组。由于在这种方案中不支持 ToString(),因此无法显示这些对象。 z 映射到主键的类成员具有 setter,但 LINQ to SQL 不支持对象标识更改。因此,在映射中使用的主键/唯 一键无法在数据库中更新。当您调用 SubmitChanges 时,如果网格中发生更改,则会引发异常。 z 如果一个实体绑定在两个不同的网格(例如,一个是主网格,另一个是从网格)中,则不会将主网格中发 生的 Delete 传播到从网格。 概念 背景信息 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/cbec8b02-a1e8-4ae... 全部折叠 代码:C# LINQ to SQL 继承支持 (LINQ to SQL) 请参见 发送反馈意见 LINQ to SQL 支持单表映射。换言之,整个继承层次结构存储在单个数据库表中。该表包含整个层次结构的所有可能 数据列的平展联合。(联合是将两个表组合成一个表的结果,组合后的表包含任一原始表中存在的行。) 每行中不 适用于该行所表示的实例类型的列为 null。 单表映射策略是最简单的继承表示形式,为许多不同类别的查询提供了良好的性能特征。 若要在 LINQ to SQL 中实现这种映射,必须在继承层次结构的根类中指定属性 (Attribute) 和属性 (Attribute) 的属 性 (Property)。有关更多信息,请参见如何:映射继承层次结构 (LINQ to SQL)。 使用 Visual Studio 的开发人员还可以使用对象关系设计器来映射继承层次结构。 请参见 概念 背景信息 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/19bb2794-b4e7-40... 全部折叠 代码:C# LINQ to SQL 本地方法调用 (LINQ to SQL) 请参见 发送反馈意见 本地方法调用是在对象模型中执行的方法调用。远程方法调用是 LINQ to SQL 转换成 SQL 并传输至数据库引擎进行 执行的方法调用。当 LINQ to SQL 无法将调用转换成 SQL 时,将需要本地方法调用。否则会引发 InvalidOperationException。 示例 1 请参见 在下面的示例中,Order 类将映射到 Northwind 示例数据库中的 Orders 表。将一个本地实例方法添加到了此 类。 在查询 1 中,Order 类的构造函数是在本地执行的。在查询 2 中,如果 LINQ to SQL 尝试将 LocalInstanceMethod() 转换成 SQL,此尝试将失败并且将引发 InvalidOperationException 异常。但由于 LINQ to SQL 为本地方法调用提供了支持,查询 2 将不会引发异常。 C# 复制代码 // Query 1. var q1 = from ord in db.Orders where ord.EmployeeID == 9 select ord; foreach (var ordObj in q1) { Console.WriteLine("{0}, {1}", ordObj.OrderID, ordObj.ShipVia.Value); } C# 复制代码 // Query 2. public int LocalInstanceMethod(int x) { return x + 1; } void q2() { var q2 = from ord in db.Orders where ord.EmployeeID == 9 select new { member0 = ord.OrderID, member1 = ord.LocalInstanceMethod(ord.ShipVia.Value) }; } 概念 背景信息 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/c34b5012-aee9-49... 全部折叠 代码:C# LINQ to SQL 使用 LINQ to SQL 的 N 层应用程序和远程应用程序 请参见 发送反馈意见 可以创建使用 LINQ to SQL 的 n 层或多层应用程序。通常,LINQ to SQL 数据上下文、实体类和查询构造逻辑作为数据访问 层 (DAL) 位于中间层上。业务逻辑和任何非持久性数据都可以在实体的分部类和分部方法中以及数据上下文中完整实现,也 可以在单独的类中实现。 客户端或表示层调用中间层的远程接口上的方法,而该层上的 DAL 会执行映射到 DataContext 方法的查询或存储过程。中 间层通常以实体或代理对象的 XML 表示形式向客户端返回数据。 在中间层上,实体由数据上下文创建,数据上下文会跟踪实体的状态,并管理从数据库延迟加载实体以及将更改提交给数据 库的过程。这些实体被“附加”到 DataContext。但是,在实体通过序列化发送到其他层后,这些实体会分离出来,这意 味着 DataContext 不再跟踪它们的状态。必须在将客户端发送回来进行更新的实体重新附加到数据上下文之后,LINQ to SQL 才能将更改提交给数据库。如果原始值和/或时间戳是开放式并发检查所必需的,则客户端负责将它们返回给中间层。 在 ASP.NET 应用程序中,LinqDataSource 管理这一复杂过程的大部分工作。有关更多信息,请参见 LinqDataSource Web 服务器控件概述。 下图演示在数据访问层中使用 LINQ to SQL 的 n 层应用程序的基本体系结构。 其他资源 请参见 有关如何实现使用 LINQ to SQL 的 n 层应用程序的更多信息,请参见以下主题: z 用于 ASP.NET 的 LINQ to SQL N 层 z 使用 Web 服务的 LINQ to SQL N 层 z 用于紧耦合的客户端-服务器应用程序的 LINQ to SQL z 实现业务逻辑 (LINQ to SQL) z N 层应用程序中的数据检索和 CUD 操作 (LINQ to SQL) 有关使用 ADO.NET 数据集的 n 层应用程序的更多信息,请参见 N 层数据应用程序。 概念 背景信息 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/854a1cdd-53cb-45f... 全部折叠 代码:C# LINQ to SQL 用于 ASP.NET 的 LINQ to SQL N 层 请参见 发送反馈意见 在使用 LINQ to SQL 的 ASP.NET 应用程序中,可以使用 LinqDataSource Web 服务器控件。此控件处理查询 LINQ to SQL 时必须使用的大部分逻辑,将数据传递到浏览器,检索数据,并将数据提交给 LINQ to SQL DataContext,然 后由其更新数据库。只需要在标记中配置该控件,然后由该控件处理 LINQ to SQL 和浏览器之间的所有数据传输。 由于该控件处理与表示层之间的交互,LINQ to SQL 处理与数据层之间的通信,因此对于 ASP.NET 多层应用程序, 您的主要重点是编写自定义业务逻辑。 有关 LINQDataSource 的更多信息,请参见 LinqDataSource Web 服务器控件概述。 请参见 概念 使用 LINQ to SQL 的 N 层应用程序和远程应用程序 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/f6cc863a-d6a6-428... 全部折叠 代码:C# LINQ to SQL 使用 Web 服务的 LINQ to SQL N 层 请参见 发送反馈意见 LINQ to SQL 特别针对在松耦合数据访问层 (DAL) 的中间层(如 Web 服务)上使用而设计。如果表示层为 ASP.NET 网 页,则使用 LinqDataSource Web 服务器控件管理用户界面与中间层上的 LINQ to SQL 之间的数据传输。如果表示层不是 ASP.NET 页,则中间层和表示层都必须执行一些附加工作以管理数据的序列化和反序列化。 在中间层上设置 LINQ to SQL 定义可序列化类型 检索和插入数据 跟踪更新和删除的更改 请参见 在 Web 服务或 n 层应用程序中,中间层包含数据上下文和实体类。可以手动创建这些类,或是按照文档中其他部分 的说明使用 SQLMetal.exe 或对象关系设计器创建。在设计时,您可以选择使实体类可序列化。有关更多信息,请参 见如何:使实体可序列化 (LINQ to SQL)。也可以选择创建封装要序列化的数据的一组单独类,然后当在 LINQ 查询 中返回数据时投射到这些可序列化类型中。 随后可以使用客户端为进行检索、插入和更新数据而调用的方法来定义接口。这些接口方法包装 LINQ 查询。可以使 用任何类型的序列化机制处理远程方法调用和数据的序列化。唯一的要求是如果您的对象模型中具有循环或双向关系 (如标准 Northwind 对象模型中的 Customers 与 Orders 之间的关系),则必须使用支持这种关系的序列化程序。 Windows Communication Foundation (WCF) DataContractSerializer 支持双向关系,但是用于非 WCF Web 服务的 XmlSerializer 不支持这种关系。如果您选择使用 XmlSerializer,则必须确保您的对象模型没有循环关系。 有关 Windows Communication Foundation 的更多信息,请参见 Visual Studio 中的 Windows Communication Foundation 服务简介。 通过使用 DataContext 和实体类上的分部类和方法挂钩到 LINQ to SQL 运行时事件,来实现您的业务规则或其他特 定于域的逻辑。有关更多信息,请参见实现业务逻辑 (LINQ to SQL)。 客户端或表示层必须具有将从中间层接收的用于类的类型定义。这些类型可能为实体类本身,或是仅包装实体类中的 特定字段以进行远程处理的特殊类。在任何情况下,LINQ to SQL 都完全不涉及表示层获取这些类型定义的方式。例 如,表示层可以使用 WCF 自动生成类型,也可以具有在其中定义这些类型的 DLL 的副本,还可以仅定义其自己的 类型版本。 中间层会定义一个接口,用于指定表示层访问数据的方式。例如 GetProductByID(int productID) 或 GetCustomers()。在中间层上,方法体通常创建 DataContext 的新实例,对其一个或多个表执行查询。随后中间层 会返回结果作为 IEnumerable(T),其中 T 为实体类或用于序列化的另一个类型。表示层从不直接向中间层发送查询 变量,也从不直接从中间层接收查询变量。这两个层交换值、对象和具体数据的集合。在已接收到一个集合后,如有 必要,表示层可以使用 LINQ to Objects 查询该集合。 在插入数据时,表示层可以构造一个新的对象并将其发送到中间层,或是使中间层基于其提供的值来构造对象。通 常,在 n 层应用程序中检索和插入数据与 2 层应用程序中的过程并无显著差异。有关更多信息,请参见查询数据库 (LINQ to SQL) 和做出和提交数据更改 (LINQ to SQL)。 LINQ to SQL 支持基于时间戳(也称为 RowVersion)和基于原始值的开放式并发。如果数据库表具有时间戳,则更 新和删除几乎不需要在中间层或表示层上进行额外工作。但是,如果必须使用原始值进行开放式并发检查,则表示层 在其进行更新时负责跟踪这些值并将这些值发送回去。这是因为在表示层上对实体所做的更改不会在中间层上进行跟 踪。实际上,实体的原始检索以及对其所进行的最终更新通常由 DataContext 的两个完全独立的实例执行。 表示层进行的更改越多,跟踪这些更改以及将这些更改打包发送回中间层的过程就越复杂。传达更改的机制的实现完 全由应用程序负责。唯一的要求是必须为 LINQ to SQL 提供进行开放式并发检查所必需的那些原始值。 有关更多信息,请参见 N 层应用程序中的数据检索和 CUD 操作 (LINQ to SQL)。 概念 使用 LINQ to SQL 的 N 层应用程序和远程应用程序 LinqDataSource Web 服务器控件概述 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/9cb10eb8-957f-4be... 全部折叠 代码:C# LINQ to SQL 用于紧耦合的客户端-服务器应用程序的 LINQ to SQL 请参见 发送反馈意见 LINQ to SQL 可在中间层上与表示层上的紧密耦合智能客户端结合使用。在有些情况下,涉及到只读数据访问,没有 进行开放式并发检查,或者具有时间戳的开放式并发,这种情况并不比非远程情况复杂很多。但是,当数据库要求 使用原始值进行开放式并发检查时,LINQ to SQL 不提供对数据集中数据往返的支持级别。但是,LINQ to SQL 中间 层可以在任何平台上与客户端交换数据。 Visual Studio 2008 中的 LINQ to SQL 未提供在实体状态序列化到客户端后用于跟踪实体状态的基础结构。LINQ to SQL 支持面向服务的结构,其中数据和表示层之间的交互很小,是相对原子的,但不执行原始值的任何往返。因 此,如果要将 LINQ to SQL 与紧密耦合的智能客户端结合使用,并且数据库使用具有原始值的开放式并发,那么您 必须实现自己的机制,用于在表示层和中间层之间通告更改。系统设计器负责决定通过以这部分额外工作来换取 LINQ to SQL 在中间层上提供的优点是否有意义。另一方面,如果数据库具有时间戳,那么就无需自定义的更改跟踪 逻辑。 请参见 概念 使用 LINQ to SQL 的 N 层应用程序和远程应用程序 使用 Web 服务的 LINQ to SQL N 层 N 层数据应用程序 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/e083d805-dcf6-459... 全部折叠 代码:C# LINQ to SQL 实现业务逻辑 (LINQ to SQL) 请参见 发送反馈意见 本主题中的术语“业务逻辑”指的是在对数据库数据进行插入、更新或删除操作之前,应用于数据的任何自定义规则或验证测试。业务逻辑 有时也称为“业务规则”或“域逻辑”。在 n 层应用程序中,业务逻辑通常设计为逻辑层,因此可以独立于表示层或数据访问层进行修改。 在对数据库数据进行任何更新、插入或删除操作前后,数据访问层可以调用业务逻辑。 业务逻辑可以和架构验证一样简单,以确保字段类型与表列类型兼容。它也可以包含一组以任意复杂方式进行交互的对象。这些规则可以作 为数据库上的存储过程或内存中的对象来实现。无论通过何种方式实现业务逻辑,LINQ to SQL 都允许您使用分部类和分部方法,将业务逻 辑与数据访问代码分开。下图阐释业务逻辑与 LINQ to SQL 类的关系。 LINQ to SQL 如何调用业务逻辑 详细了解扩展性点 当在设计时生成实体类时,无论是通过手动方式还是使用对象关系设计器或 SQLMetal,都将该类定义为分部类。这意味着,在单独的 代码文件中,可以定义包含自定义业务逻辑的另一部分实体类。在编译时,这两个部分将合并成一个类。但如果必须使用对象关系设 计器或 SQLMetal 来重新生成实体类,您可以这样做,并且不会修改类的自定义部分。 定义实体和 DataContext 的分部类包含分部方法。这些是扩展性点,可以在进行任何更新、插入或删除前后用于对实体或实体属性应 用业务逻辑。分部方法可以视为编译时事件。代码生成器定义方法签名,并在 get 和 set 属性访问器、DataContext 构造函数中调用 这些方法,有些情况下还在调用 SubmitChanges 时在后台调用方法。但是,如果未实现特殊的分部方法,那么在编译时将移除对该分 部方法的所有引用和定义。 在您在单独的代码文件中编写的实现定义中,可以执行所需的任何自定义逻辑。可以将分部类本身用作域层,也可以从分部方法的实 现定义,将分部类调入单独对象或多个对象。无论采用何种方式,业务逻辑都将与数据访问代码和表示层代码完全分开。 下面的示例演示对象关系设计器为 DataContext 类生成的代码部分,该类包括两个表:Customers 和 Orders。注意为该类的每个表 都定义了插入、更新和删除方法。 如果在分部类中实现插入、更新和删除方法,LINQ to SQL 运行时将在调用 SubmitChanges 时调用这些方法,而不是自己的默认方 法。这使您能够重写创建/读取/更新/删除操作的默认行为。有关更多信息,请参见演练:自定义实体类的插入、更新和删除行为。 OnCreated 方法在类构造函数中调用。 此实体类有三个方法,当创建、加载和验证实体时(调用 SubmitChanges 时),LINQ to SQL 运行时会调用这些方法。此实体类还 为每个属性提供两个分部方法,一个在设置属性前调用,另一个在设置属性后调用。下面的代码示例显示为 Customer 类生成的一些 方法: C# 复制代码 public partial class MyNorthWindDataContext : System.Data.Linq.DataContext { private static System.Data.Linq.Mapping.MappingSource mappingSource = new AttributeMappingSource(); #region Extensibility Method Definitions partial void OnCreated(); partial void InsertCustomer(Customer instance); partial void UpdateCustomer(Customer instance); partial void DeleteCustomer(Customer instance); partial void InsertOrder(Order instance); partial void UpdateOrder(Order instance); partial void DeleteOrder(Order instance); #endregion C# 复制代码 public MyNorthWindDataContext(string connection) : base(connection, mappingSource) { OnCreated(); } C# 复制代码 #region Extensibility Method Definitions partial void OnLoaded(); partial void OnValidate(); partial void OnCreated(); 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/c4577590-7b12-42... 请参见 在属性 set 访问器中调用这些方法,如下面的 CustomerID 属性示例中所示。 在类的自定义部分,编写方法的实现定义。在 Visual Studio 中,在键入 partial 后,将在类的其他部分中看到方法定义的 IntelliSense。 有关如何使用分部方法向应用程序添加业务逻辑的更多信息,请参见下列主题: 如何:在实体类中添加验证 演练:自定义实体类的插入、更新和删除行为 演练:向实体类中添加验证 partial void OnCustomerIDChanging(string value); partial void OnCustomerIDChanged(); partial void OnCompanyNameChanging(string value); partial void OnCompanyNameChanged(); // ...additional Changing/Changed methods for each property C# 复制代码 public string CustomerID { set { if ((this._CustomerID != value)) { this.OnCustomerIDChanging(value); this.SendPropertyChanging(); this._CustomerID = value; this.SendPropertyChanged("CustomerID"); this.OnCustomerIDChanged(); } } } C# 复制代码 partial class Customer { partial void OnCustomerIDChanging(string value) { //Perform custom validation logic here. } } 概念 分部类和方法(C# 编程指南) 分部方法 对象关系设计器(O/R 设计器) 代码生成工具 (SqlMetal.exe) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/c4577590-7b12-42... 全部折叠 代码:C# LINQ to SQL N 层应用程序中的数据检索和 CUD 操作 (LINQ to SQL) 请参见 发送反馈意见 在将实体对象(如 Customers 或 Orders)通过网络序列化到客户端时,这些实体会与其数据上下文分离。数据上下文不再跟 踪这些实体的更改或它们与其他对象的关联。只要客户端只读取数据,这就不会成为问题。要使客户端可以向数据库添加新 行,也比较容易做到。但是,如果应用程序要求客户端能够更新或删除数据,则必须在调用 DataContext.SubmitChanges 之 前将实体附加到新的数据上下文。此外,如果对原始值使用开放式并发检查,则还需要一种为数据库同时提供原始实体和修改 后的实体的方式。使用 Attach 方法可以在实体分离后将其放入新的数据上下文中。 即使要序列化代理对象来代替 LINQ to SQL 实体,仍然必须在数据访问层 (DAL) 上构造一个实体,并将其附加到新的 System.Data.Linq.DataContext,以便将数据提交给数据库。 LINQ to SQL 完全不关心实体的序列化方式。有关如何使用对象关系设计器和 SQLMetal 工具通过 Windows Communication Foundation (WCF) 生成可序列化的类的更多信息,请参见如何:使实体可序列化 (LINQ to SQL)。 检索数据 注意: 仅对新实体或反序列化后的实体调用 Attach 方法。将实体与其原始数据上下文分离的唯一方式是将其序列化。如果试图 将未分离的实体附加到新的数据上下文,并且该实体仍然具有来自其以前的数据上下文的延迟加载程序,则 LINQ to SQL 会引发异常。如果一个实体具有来自两个不同数据上下文的延迟加载程序,则在对该实体执行插入、更新和删除操作 时,可能产生意外的结果。有关延迟加载程序的更多信息,请参见延迟加载与立即加载的比较 (LINQ to SQL)。 客户端方法调用 下面的示例演示一个从 Windows 窗体客户端对 DAL 进行的示例方法调用。在此示例中,DAL 被实现为 Windows 服务 库: 中间层实现 下面的示例演示中间层上的接口方法的实现。下面是要注意的两个要点: z DataContext 是在方法范围上声明的。 z 该方法返回实际结果的 IEnumerable 集合。序列化程序将执行查询,以便将结果发送回客户端/表示层。若要在中 间层上对查询结果进行本地访问,可以通过对查询变量调用 ToList 或 ToArray 来强制执行。然后,可以将该列表 或数组作为 IEnumerable 返回。 数据上下文的实例应具有一个“工作单元”的生存期。在松耦合环境中,工作单元通常较小,它可能是一个开放式事 C# 复制代码 private void GetProdsByCat_Click(object sender, EventArgs e) { // Create the WCF client proxy. NorthwindServiceReference.Service1Client proxy = new NorthwindClient.NorthwindServiceReference.Service1Client(); // Call the method on the service. NorthwindServiceReference.Product[] products = proxy.GetProductsByCategory(1); // If the database uses original values for concurrency checks, // the client needs to store them and pass them back to the // middle tier along with the new values when updating data. foreach (var v in products) { // Persist to a list declared at class scope. // Additional change-tracking logic is the responsibility // of the presentation tier and/or middle tier. originalProducts.Add(v); } // (Not shown) Bind the products list to a control // and/or perform whatever processing is necessary. } C# 复制代码 public IEnumerable GetProductsByCategory(int categoryID) { NorthwindClasses1DataContext db = new NorthwindClasses1DataContext(connectionString); IEnumerable productQuery = from prod in db.Products where prod.CategoryID == categoryID select prod; return productQuery.AsEnumerable(); } 页码,1/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/c3133d53-83ed-4a... 插入数据 删除数据 更新数据 务,其中包含对 SubmitChanges 的单个调用。因此,数据上下文在方法范围上创建和释放。如果工作单元包含对业务 规则逻辑的调用,则通常需要为整个操作保持 DataContext 实例。在任何情况下,都不应该使 DataContext 实例在 任意数量的事务之间长时间保持活动状态。 此方法会返回 Product 对象,但不会返回与每个 Product 相关联的 Order_Detail 对象的集合。使用 DataLoadOptions 对象可以更改此默认行为。有关更多信息,请参见如何:控制检索的相关数据量 (LINQ to SQL)。 为了插入新对象,表示层只是调用中间层接口上的相关方法,并传入要插入的新对象。在某些情况下,对于客户端而 言,仅传入一些值并让中间层来构造完整对象可能更加高效。 中间层实现 在中间层上,创建一个新的 DataContext,使用 InsertOnSubmit 方法将该对象附加到 DataContext,并在调用 SubmitChanges 时插入该对象。异常、回调和错误条件可以像在任何其他 Web 服务方案中一样进行处理。 C# 复制代码 // No call to Attach is necessary for inserts. public void InsertOrder(Order o) { NorthwindClasses1DataContext db = new NorthwindClasses1DataContext(connectionString); db.Orders.InsertOnSubmit(o); // Exception handling not shown. db.SubmitChanges(); } 为了从数据库删除现有对象,表示层调用中间层接口上的相关方法,并传入要删除的对象的副本(其中包含该对象的原 始值)。 删除操作涉及到开放式并发检查,并且必须首先将要删除的对象附加到新的数据上下文。在此示例中,Boolean 参数设 置为 false,以指示该对象没有时间戳 (RowVersion)。如果数据库表确实为每个记录生成了时间戳,则并发检查会简单 得多(特别是对客户端而言)。只需传入原始对象或已修改的对象,并将 Boolean 参数设置为 true。在任何情况下, 通常都需要在中间层上捕捉 ChangeConflictException。有关如何处理开放式并发冲突的更多信息,请参见开放式并发概 述 (LINQ to SQL)。 如果要删除的实体具有对关联表的外键约束,则必须首先删除该实体的 EntitySet(TEntity) 集合中的所有对象。 C# 复制代码 // Attach is necessary for deletes. public void DeleteOrder(Order order) { NorthwindClasses1DataContext db = new NorthwindClasses1DataContext(connectionString); db.Orders.Attach(order, false); // This will throw an exception if the order has order details. db.Orders.DeleteOnSubmit(order); try { // ConflictMode is an optional parameter. db.SubmitChanges(ConflictMode.ContinueOnConflict); } catch (ChangeConflictException e) { // Get conflict information, and take actions // that are appropriate for your application. // See MSDN Article How to: Manage Change Conflicts (LINQ to SQL). } } LINQ to SQL 支持以下这些涉及开放式并发的方案中的更新: z 基于时间戳或 RowVersion 号的开放式并发。 z 基于实体属性子集的原始值的开放式并发。 z 基于完整原始实体和已修改实体的开放式并发。 还可以对实体及其关系(如一个 Customer 及其关联 Order 对象的集合)一起执行更新或删除。如果在客户端上对实体 对象及其子代 (EntitySet) 集合的关系图进行修改,并且开放式并发检查需要原始值,则客户端必须为每个实体和 EntitySet(TEntity) 对象提供这些原始值。如果需要使客户端可以在单个方法调用中进行一组相关的更新、删除和插入操 作,则必须为客户端提供一种相应的方式,以便指示要对每个实体执行的操作的类型。然后,在调用 SubmitChanges 之前,必须在中间层上为每个实体调用适当的 Attach 方法,然后依次调用 InsertOnSubmit、DeleteAllOnSubmit 或 InsertOnSubmit()(对于插入操作,不调用 Attach)。在尝试进行更新之前,不要将从数据库中检索数据作为一种获取 原始值的方式。 页码,2/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/c3133d53-83ed-4a... 有关开放式并发的更多信息,请参见开放式并发概述 (LINQ to SQL)。有关解决开放式并发更改冲突的详细信息,请参 见如何:管理更改冲突 (LINQ to SQL)。 下面的示例演示每种方案: 使用时间戳的开放式并发 使用原始值子集的开放式并发 在此方法中,客户端返回完整的序列化对象以及要修改的值。 使用完整实体的开放式并发 C# 复制代码 // Assume that "customer" has been sent by client. // Attach with "true" to say this is a modified entity // and it can be checked for optimistic concurrency because // it has a column that is marked with "RowVersion" attribute db.Customers.Attach(customer, true) try { // Optional: Specify a ConflictMode value // in call to SubmitChanges. db.SubmitChanges(); } catch(ChangeConflictException e) { // Handle conflict based on options provided // See MSDN article How to: Manage Change Conflicts (LINQ to SQL). } C# 复制代码 public void UpdateProductInventory(Product p, short? unitsInStock, short? unitsOnOrder) { using (NorthwindClasses1DataContext db = new NorthwindClasses1DataContext(connectionString)) { // p is the original unmodified product // that was obtained from the database. // The client kept a copy and returns it now. db.Products.Attach(p, false); // Now that the original values are in the data context, apply the changes. p.UnitsInStock = unitsInStock; p.UnitsOnOrder = unitsOnOrder; try { // Optional: Specify a ConflictMode value // in call to SubmitChanges. db.SubmitChanges(); } catch (ChangeConflictException e) { // Handle conflict based on provided options. // See MSDN article How to: Manage Change Conflicts // (LINQ to SQL). } } } C# 复制代码 public void UpdateProductInfo(Product newProd, Product originalProd) { using (NorthwindClasses1DataContext db = new NorthwindClasses1DataContext(connectionString)) { db.Products.Attach(newProd, originalProd); try { // Optional: Specify a ConflictMode value // in call to SubmitChanges. db.SubmitChanges(); } catch (ChangeConflictException e) { // Handle potential change conflict in whatever way // is appropriate for your application. // For more information, see the MSDN article // How to: Manage Change Conflicts (LINQ to SQL)/ } } } 页码,3/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/c3133d53-83ed-4a... 请参见 若要更新集合,请调用 AttachAll 而不是 Attach。 期望的实体成员 如前所述,在调用 Attach 方法之前,只需设置实体对象的特定成员。需要设置的实体成员必须满足以下条件: z 属于实体的标识。 z 需要进行修改。 z 为时间戳,或将其 UpdateCheck 属性设置为除 Never 之外的某个值。 如果某个表使用时间戳或版本号进行开放式并发检查,则在调用 Attach 之前必须设置这些成员。当该 Column 属性 (Attribute) 上的 IsVersion 属性 (Property) 设置为 true 时,相应的成员将专门用于进行开放式并发检查。只有当数据 库具有相同的版本号或时间戳值时,才会提交所请求的任何更新。 只要成员的 UpdateCheck 未设置为 Never,那么也会在开放式并发检查中使用该成员。如果未指定其他值,则默认值 为 Always。 如果缺少这些必需成员中的任何一个成员,都会在 SubmitChanges 期间引发 ChangeConflictException(“找不到行或 行已更改”)。 状态 在将一个实体对象附加到 DataContext 实例之后,会将该对象视为处于 PossiblyModified 状态。可通过三种方式强制 所附加的对象被视为 Modified。 1. 将对象作为未修改的对象进行附加,然后直接修改其字段。 2. 使用接受当前对象实例和原始对象实例作为参数的 Attach 重载附加对象。这会向更改跟踪程序提供旧值和新 值,以便跟踪程序自动了解哪些字段已更改。 3. 使用接受另一个布尔型参数(设置为 true)的 Attach 重载附加对象。这将告诉更改跟踪程序将该对象视为已修 改的对象,而无需提供任何原始值。在此方式中,对象必须具有一个版本/时间戳字段。 有关更多信息,请参见对象状态与更改跟踪 (LINQ to SQL)。 如果 ID 缓存中已存在一个实体对象且该对象具有与要附加的对象相同的标识,则会引发 DuplicateKeyException。 在附加一组 IEnumerable 对象时,将在出现已存在的键时引发 DuplicateKeyException。剩余的对象将不会附加。 概念 使用 LINQ to SQL 的 N 层应用程序和远程应用程序 背景信息 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,4/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/c3133d53-83ed-4a... 全部折叠 代码:C# LINQ to SQL 对象标识 (LINQ to SQL) 请参见 发送反馈意见 运行库中的对象具有唯一标识。引用同一对象的两个变量实际上是引用此对象的同一实例。因为这一事实,您通过一个变 量做出更改后,立即就可以通过另一个变量看到这些更改。 关系数据库表中的行不具有唯一标识。由于每一行都具有唯一的主键,因此任何两行都不会共用同一键值。但是,这一事 实仅限于数据库表的内容。 实际上,通常都是将数据从数据库中提取出来放入另一层中,应用程序在该层对数据进行处理。这就是 LINQ to SQL 支持 的模型。将数据作为行从数据库中提取出来时,您不期望表示相同数据的两行实际上对应于相同的行实例。如果您查询特 定客户两次,您将获得两行数据。每一行包含相同的信息。 对于对象,您的期望则大不一样。您期望在您反复向 DataContext 索取相同的信息时,它实际上会为您提供同一对象实 例。您之所以期望这种行为,是因为对象对您的应用程序而言有着特殊的含义,您期望它们的行为像实物一样。您将它们 设计为层次结构或关系图。您希望像检索实物一样检索它们,而不希望仅仅因为您多次索要同一内容而收到大量的复制实 例。 在 LINQ to SQL 中,DataContext 管理对象标识。只要您从数据库中检索新行,该行就会由其主键记录到标识表中,并且 会创建一个新的对象。只要您检索该行,就会将原始对象实例传递回应用程序。通过这种方式,DataContext 将数据库看到 的标识(即主键)的概念转换成相应语言看到的标识(即实例)的概念。应用程序只看到处于第一次检索时的状态的对 象。新数据如果不同,则会被丢弃。 LINQ to SQL 使用此方法来管理本地对象的完整性,以支持开放式更新。由于在最初创建对象后唯一发生的更改是由应用程 序做出的,因此应用程序的意向是很明确的。如果在中间阶段外部某一方做了更改,则在调用 SubmitChanges() 时会识 别出这些更改。 示例 请参见 注意: 如果查询请求的对象易被识别为已检索到的对象,则不会执行查询。标识表用作以前检索到的所有对象的缓存。 对象缓存示例 1 在此示例中,如果您执行同一查询两次,则您每次都会收到对内存中同一对象的引用。 对象缓存示例 2 在此示例中,如果您执行返回数据库中同一行的不同查询,则您每次都会收到对内存中同一对象的引用。 C# 复制代码 Customer cust1 = (from cust in db.Customers where cust.CustomerID == "BONAP" select cust).First(); Customer cust2 = (from cust in db.Customers where cust.CustomerID == "BONAP" select cust).First(); C# 复制代码 Customer cust1 = (from cust in db.Customers where cust.CustomerID == "BONAP" select cust).First(); Customer cust2 = (from ord in db.Orders where ord.Customer.CustomerID == "BONAP" select ord).First().Customer; 概念 背景信息 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/c788f2f9-65cc-445... 全部折叠 代码:C# LINQ to SQL LINQ to SQL 对象模型 请参见 发送反馈意见 在 LINQ to SQL 中,用开发人员所用的编程语言表示的对象模型映射到关系数据库的数据模型。然后就会按照对象模型来执行对数据的操作。 在这种情况下,您无需向数据库发出数据库命令(例如,INSERT),而是在对象模型中更改值和执行方法。当您需要查询数据库或向其发送更改时,LINQ to SQL 会将您的 请求转换成正确的 SQL 命令,然后将这些命令发送到数据库。 下表概括了 LINQ to SQL 对象模型中最基本的元素及其与关系数据模型中的元素的关系: LINQ to SQL 实体类与数据库表 LINQ to SQL 类成员与数据库列 LINQ to SQL 关联与数据库外键关系 LINQ to SQL 对象模型 关系数据模型 实体类 表 类成员 列 关联 外键关系 方法 存储过程或函数 注意: 以下说明假定您已具备关系数据模型和规则方面的基础知识。 在 LINQ to SQL 中,数据库表由实体类表示。实体类与您可能创建的任何其他类相似,只不过对实体类进行批注的方法是使用将该类与数据库表关联的特殊信息。您需 通过向类声明中添加自定义属性 (TableAttribute) 来进行这种批注,如下面的示例所示: 示例 只有声明为表的类(即实体类)的实例才能保存到数据库中。 有关更多信息,请参见基于属性的映射 (LINQ to SQL) 的“表属性”一节。 C# 复制代码 [Table(Name = "Customers")] public class Customerzz { public string CustomerID; // ... public string City; } 除了将类与表关联以外,您还需指定字段或属性来表示数据库列。为此,LINQ to SQL 定义了 ColumnAttribute 属性,如下面的示例所示: 示例 只有映射到列的字段和属性才能持久保存到数据库中,从数据库中也只能检索这样的字段和属性。那些未声明为列的字段和属性被视为应用程序逻辑的瞬态部分。 ColumnAttribute 属性 (Attribute) 具有各种属性 (Property),您可以使用这些属性 (Property) 来自定义表示列的这些成员(例如,将某一成员指定为表示主键列)。有 关更多信息,请参见基于属性的映射 (LINQ to SQL) 的“列属性”一节。 C# 复制代码 [Table(Name = "Customers")] public class Customer { [Column(IsPrimaryKey = true)] public string CustomerID; [Column] public string City; } 在 LINQ to SQL 中,数据库关联(如外键到主键关系)是通过应用 AssociationAttribute 属性表示的。在下面的代码段中,Order 类包含具有 AssociationAttribute 属性 (Attribute) 的 Customer 属性 (Property)。此属性 (Property) 及其属性 (Attribute) 为 Order 类提供了与 Customer 类的关系。 下面的代码示例显示了 Order 类中的 Customer 属性 (Property)。 示例 C# 复制代码 [Association(Name="FK_Orders_Customers", Storage="_Customer", ThisKey="CustomerID", IsForeignKey=true)] public Customer Customer { get { return this._Customer.Entity; } set { Customer previousValue = this._Customer.Entity; if (((previousValue != value) || (this._Customer.HasLoadedOrAssignedValue == false))) { this.SendPropertyChanging(); if ((previousValue != null)) 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/81dd0c37-e2a4-46... LINQ to SQL 方法与数据库存储过程 请参见 有关更多信息,请参见基于属性的映射 (LINQ to SQL) 的“关联属性”一节。 { this._Customer.Entity = null; previousValue.Orders.Remove(this); } this._Customer.Entity = value; if ((value != null)) { value.Orders.Add(this); this._CustomerID = value.CustomerID; } else { this._CustomerID = default(string); } this.SendPropertyChanged("Customer"); } } } LINQ to SQL 支持存储过程和用户定义的函数。在 LINQ to SQL 中,您应将数据库定义的这些抽象映射到客户端对象,以便您可以从客户端代码中以强类型化方式访问 它们。方法签名与数据库中定义的过程和函数的签名尽可能类似。您可以使用 IntelliSense 来查找这些方法。 通过调用映射的过程返回的结果集为强类型化的集合。 LINQ to SQL 通过使用 FunctionAttribute 和 ParameterAttribute 属性将存储过程和函数映射到方法。表示存储过程的方法与表示用户定义的函数的方法通过 IsComposable 属性加以区分。如果此属性设置为 false(默认值),则此方法表示存储过程。如果它设置为 true,则此方法表示数据库函数。 示例 有关更多信息,请参见基于属性的映射 (LINQ to SQL) 和存储过程 (LINQ to SQL) 中的“函数属性”、“存储过程属性”和“参数属性”各节。 注意: 如果您使用的是 Visual Studio,则可以使用对象关系设计器来创建映射到存储过程和用户定义的函数的方法。 C# 复制代码 // This is an example of a stored procedure in the Northwind // sample database. The IsComposable property defaults to false. [Function(Name="dbo.CustOrderHist")] public ISingleResult CustOrderHist([Parameter(Name="CustomerID", DbType="NChar(5)")] string customerID) { IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), customerID); return ((ISingleResult)(result.ReturnValue)); } 概念 基于属性的映射 (LINQ to SQL) 背景信息 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/81dd0c37-e2a4-46... 全部折叠 代码:C# LINQ to SQL 对象状态与更改跟踪 (LINQ to SQL) 请参见 发送反馈意见 LINQ to SQL 对象始终处于某种状态。例如,当 LINQ to SQL 创建一个新对象时,该对象就处于 Unchanged 状 态。您自己创建的新对象对于 DataContext 而言是未知的,因而处于 Untracked 状态。在成功执行 SubmitChanges 后,LINQ to SQL 已知的所有对象均处于 Unchanged 状态。(唯一的例外是已从数据库中成功删 除的那些对象,它们处于 Deleted 状态,并且在该 DataContext 实例中无法使用。) 对象状态 插入对象 删除对象 下表列出了 LINQ to SQL 对象可能的状态。 状态 说明 Untracked LINQ to SQL 未跟踪的对象。示例包括: z 未通过当前 DataContext 查询的对象(例如新创建的对象)。 z 通过反序列化创建的对象 z 通过其他 DataContext 查询的对象。 Unchanged 通过使用当前 DataContext 检索到的对象,并且尚未获知此对象自创建以来已被修 改。 PossiblyModified 附加到 DataContext 的对象。有关更多信息,请参见 N 层应用程序中的数据检索和 CUD 操作 (LINQ to SQL)。 ToBeInserted 使用当前 DataContext 未检索到的对象。这会导致在 SubmitChanges 期间执行数据 库 INSERT 操作。 ToBeUpdated 符合如下条件的对象:已获知自检索到该对象以来它已被修改。这会导致在 SubmitChanges 期间执行数据库 UPDATE 操作。 ToBeDeleted 标记为删除,从而导致在 SubmitChanges 期间执行数据库 DELETE 操作的对象。 Deleted 已从数据库中删除的对象。此状态为最终状态,不允许再进行其他转换。 您可以通过使用 InsertOnSubmit 显式请求 Inserts。此外,LINQ to SQL 还可以通过查找与必须更新的已知 对象之一相连接的对象来推断 Inserts。例如,如果将 Untracked 对象添加到 EntitySet(TEntity) 或将 EntityRef(TEntity) 设置为 Untracked 对象,则可以通过关系图中的被跟踪对象使 Untracked 对象可访问。 在处理 SubmitChanges 时,LINQ to SQL 遍历被跟踪的对象,并查找未被跟踪的任何可访问的持久性对象。 此类对象是要插入到数据库中的候选对象。 对于继承层次结构中的类,InsertOnSubmit(o) 还会设置指定为鉴别器的成员的值,使之与对象 o 的类型匹 配。对于与默认鉴别器值匹配的类型,此操作会导致鉴别器值被默认值覆盖。有关更多信息,请参见继承支持 (LINQ to SQL)。 重要说明: 添加到 Table 的对象不在标识缓存中。标识缓存仅反映从数据库中检索到的内容。调用 InsertOnSubmit 后,直到 SubmitChanges 成功完成,所添加的实体才会出现在对数据库的查询中。 可以通过对相应的 Table(TEntity) 调用 DeleteOnSubmit(o),将被跟踪的对象 o 标记为删除。LINQ to SQL 将 从 EntitySet(TEntity) 中移除对象视为更新操作,并且将对应的外键值设置为 null。不会将操作的目标 (o) 从 其所在表中删除。例如,当通过将外键 ord.CustomerID 设置为 null 切断 cust 与 ord 之间的关系时, cust.Orders.DeleteOnSubmit(ord) 指示更新操作。它不会导致删除与 ord 对应的行。 将对象从其所在表中删除 (DeleteOnSubmit) 时,LINQ to SQL 执行以下处理: z 调用 SubmitChanges 时,会对该对象执行 DELETE 操作。 z 不论相关对象是否已加载,都不会将此移除操作传播到相关对象。具体而言,不会为更新关系属性而加载 相关对象。 z 在成功执行 SubmitChanges 后,会将这些对象设置为 Deleted 状态。因此,您不能在该 DataContext 中使用此类对象或其 id。由 DataContext 实例维护的内部缓存不会消除检索到的对象或作为新对象添加 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/7a808b00-9c3c-47... 更新对象 请参见 的对象,即使这些对象已从数据库中删除也不例外。 您只能对由 DataContext 跟踪的对象调用 DeleteOnSubmit。对于 Untracked 对象,您必须先调用 Attach, 再调用 DeleteOnSubmit。对 Untracked 对象调用 DeleteOnSubmit 会引发异常。 注意: 从表中移除对象使得 LINQ to SQL 在 SubmitChanges 时生成对应的 SQL DELETE 命令。此操作不会从缓 存中移除该对象或将删除操作传播到相关对象。 若要回收已删除对象的 id,请使用新的 DataContext 实例。若要清理相关对象,可以使用数据库的级联删 除功能,否则需手动删除相关对象。 无需按任何特殊顺序删除相关对象(与在数据库中进行删除不同)。 您可以通过观察有关更改的通知来检测 Updates。通知是通过属性 setter 中的 PropertyChanging 事件提供 的。当 LINQ to SQL 收到有关对象的第一项更改的通知时,它会创建该对象的一个副本,并将该对象视为用 于生成 Update 语句的候选对象。 对于未实现 INotifyPropertyChanging 的对象,LINQ to SQL 保存这些对象在第一次具体化时所具有的值的副 本。调用 SubmitChanges 时,LINQ to SQL 会比较当前值和原始值,以确定对象是否已更改。 对于关系的更新,从子级到父级的引用(即与外键对应的引用)被视为授权。反方向上(即从父级到子级)的 引用是可选的。关系类(EntitySet(TEntity) 和 EntityRef(TEntity))保证双向引用对于一对多和一对一关系是 一致的。如果对象模型不使用 EntitySet(TEntity) 或 EntityRef(TEntity),且存在反向引用,则需要您来确保在 更新关系时反向引用与前向引用一致。 如果您既更新所需的引用,又更新对应的外键,则必须确保它们一致。如果在您调用 SubmitChanges 时这两 者不同步,则会引发 InvalidOperationException 异常。尽管外键值的更改足以影响基础行的更新,但您应更 改相应的引用以维护对象图的连接性和关系的双向一致性。 概念 背景信息 (LINQ to SQL) 插入、更新和删除操作 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/7a808b00-9c3c-47... 全部折叠 代码:C# LINQ to SQL 开放式并发概述 (LINQ to SQL) 请参见 发送反馈意见 LINQ to SQL 支持开放式并发控制。下表介绍 LINQ to SQL 文档中涉及开放式并发的术语: 在 LINQ to SQL 对象模型中,当以下两个条件都得到满足时,就会发生“开放式并发冲突”: z 客户端尝试向数据库提交更改。 z 数据库中的一个或多个更新检查值自客户端上次读取它们以来已得到更新。 此冲突的解决过程包括查明对象的哪些成员发生冲突,然后决定您希望如何进行处理。 示例 冲突检测和解决检查表 术语 说明 并发 两个或更多用户同时尝试更新同一数据库行的情形。 并发冲突 两个或更多用户同时尝试向一行的一列或多列提交冲突值的情形。 并发控制 用于解决并发冲突的技术。 开放式并 发控制 先调查其他事务是否已更改了行中的值,再允许提交更改的技术。 相比之下,保守式并发控制则是通过锁定记录来避免发生并发冲突。 之所以称作开放式控制,是因为它将一个事务干扰另一事务视为不太可能发生。 冲突解决 通过重新查询数据库刷新出现冲突的项,然后协调差异的过程。 刷新对象时,LINQ to SQL 更改跟踪器会保留以下数据: z 最初从数据库获取并用于更新检查的值。 z 通过后续查询获得的新数据库值。 LINQ to SQL 随后会确定相应对象是否发生冲突(即它的一个或多个成员值是否已发生更改)。 如果此对象发生冲突,LINQ to SQL 下一步会确定它的哪些成员发生冲突。 LINQ to SQL 发现的任何成员冲突都会添加到冲突列表中。 注意: 只有映射为 Always 或 WhenChanged 的成员才会参与开放式并发检查。对于标记为 Never 的成员,不执行检 查。有关更多信息,请参见 UpdateCheck。 例如,在下面的情况中,User1 通过查询数据库中的某一行开始准备更新。User1 收到包含 Alfreds、Maria 和 Sales 值的一行。 User1 希望将 Manager 列的值更改为 Alfred,将 Department 列的值更改为 Marketing。在 User2 将更改提 交到数据库后,User1 才能提交这些更改。所以,现在 Assistant 列的值已更改为 Mary,Department 列的值 已更改为 Service。 当 User1 现在尝试提交更改时,提交失败并且引发 ChangeConflictException 异常。出现这种结果是因为 Assistant 列和 Department 列的数据库值并不是他们所预期的那些值。表示 Assistant 和 Department 列的成 员发生了冲突。下表对这种情形作了总结。 您可以用多种不同的方式来解决此类冲突。有关更多信息,请参见如何:管理更改冲突 (LINQ to SQL)。 Manager Assistant Department 原始状态 Alfreds Maria Sales User1 Alfred Marketing User2 Mary Service 您可以检测和解决任意详细等级的冲突。一种极端情况是,您可以用三种方式之一(请参见 RefreshMode)来 解决所有冲突,而不再作其他方面的考虑。另一种极端情况是,您可以为发生冲突的每个成员上的每种冲突指 定特定操作。 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/c2e38512-d0c8-48... 支持冲突发现和解决的 LINQ to SQL 类型 请参见 z 在您的对象模型中指定或修改 UpdateCheck 选项。 有关更多信息,请参见如何:指定测试哪些成员是否发生并发冲突 (LINQ to SQL)。 z 在对 SubmitChanges 的调用的 try/catch 块中,指定您希望在哪个点引发异常。 有关更多信息,请参见如何:指定并发异常的引发时间 (LINQ to SQL)。 z 决定您希望检索的冲突详细信息量,并在 try/catch 块中包括相应的代码。 有关更多信息,请参见如何:检索实体冲突信息 (LINQ to SQL) 和如何:检索成员冲突信息 (LINQ to SQL)。 z 在 try/catch 代码中包括您希望解决您发现的各种冲突的方式。 有关更多信息,请参见如何:通过保留数据库值解决并发冲突 (LINQ to SQL)、如何:通过覆盖数据库值 解决并发冲突 (LINQ to SQL) 和如何:通过与数据库值合并解决并发冲突 (LINQ to SQL)。 LINQ to SQL 中支持解决开放式并发冲突的类和功能包括: z System.Data.Linq.ObjectChangeConflict z System.Data.Linq.MemberChangeConflict z System.Data.Linq.ChangeConflictCollection z System.Data.Linq.ChangeConflictException z DataContext.ChangeConflicts z DataContext.SubmitChanges z DataContext.Refresh z ColumnAttribute.UpdateCheck z System.Data.Linq.Mapping.UpdateCheck z System.Data.Linq.RefreshMode 概念 如何:管理更改冲突 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/c2e38512-d0c8-48... 全部折叠 代码:C# LINQ to SQL LINQ to SQL 中的查询概念 发送反馈意见 本节介绍有关在 LINQ to SQL 中设计 LINQ 查询的一些关键概念。 本节内容 相关章节 LINQ to SQL 查询 参考一般性的 LINQ 主题,并解释特定于 LINQ to SQL 的项。 跨关系查询 (LINQ to SQL) 解释如何在 LINQ to SQL 对象模型中使用关联。 远程查询执行与本地查询执行的比较 (LINQ to SQL) 解释如何指定您希望执行查询的位置。 延迟加载与立即加载的比较 (LINQ to SQL) 介绍如何指定加载相关对象的时间。 编程指南 (LINQ to SQL) 包含指向解释 LINQ to SQL 技术的主题的链接。 对象标识 (LINQ to SQL) 解释 LINQ to SQL 中对象标识的概念。 LINQ 查询简介 提供有关 LINQ 中的查询操作的介绍。 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/9a125749-ccb5-49... 全部折叠 代码:C# LINQ to SQL LINQ to SQL 查询 请参见 发送反馈意见 定义 LINQ to SQL 查询所用的语法与在 LINQ 中使用的语法相同。唯一的差异是您的查询中引用的对象映射到数据 库中的元素。有关更多信息,请参见 LINQ 查询简介。 LINQ to SQL 将您编写的查询转换成等效的 SQL 查询,然后将它们发送至服务器进行处理。更具体地说,您的应用 程序使用 LINQ to SQL API 来请求查询执行。LINQ to SQL 提供程序随后会将查询转换成 SQL 文本,并委托 ADO 提供程序执行。ADO 提供程序将查询结果作为 DataReader 返回。LINQ to SQL 提供程序将 ADO 结果转换成用户 对象的 IQueryable 集合。 下图描绘了此常规流程。 查询执行关系图 下表显示了 LINQ 与 LINQ to SQL 查询项之间的相似和不同之处。 请参见 注意: .NET Framework 内置类型中的大多数方法和运算符都能直接转换成 SQL。LINQ 无法转换的那些方法和运算符 会产生运行时异常。有关更多信息,请参见 SQL-CLR 类型映射 (LINQ to SQL)。 项 LINQ 查询 LINQ to SQL 查询 保存查询的局部变量的 返回类型(对于返回序 列的查询而言) 泛型 IEnumerable 泛型 IQueryable 指定数据源 使用 From (Visual Basic) 或 from (C#) 子 句 相同 筛选 使用 Where/where 子 句 相同 分组 使用 Group… By/groupby 子句 相同 选择(投影) 使用 Select/select 子句 相同 延迟执行与立即执行 请参见 LINQ 查询简介 相同 实现联接 使用 Join/join 子句 可以使用 Join/join 子句,但使用 AssociationAttribute 属性更有效。有关更多信息,请 参见跨关系查询 (LINQ to SQL)。 远程执行与本地执行 有关更多信息,请参见远程查询执行与本地查询执行 的比较 (LINQ to SQL)。 流式查询与缓存查询 在本地内存情况中不适用 概念 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/f4897aaa-7f44-4c2... LINQ 查询简介 基本查询操作 (LINQ) 查询操作中的类型关系 (LINQ) LINQ to SQL 中的查询概念 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/f4897aaa-7f44-4c2... 全部折叠 代码:C# LINQ to SQL 跨关系查询 (LINQ to SQL) 请参见 发送反馈意见 您的类定义中对其他对象或其他对象的集合的直接引用相当于数据库中的外键关系。您可以通过使用点表示法在查 询时利用这些关系来访问关系属性以及从一个对象定位到另一个对象。这些访问操作会转换成用等效的 SQL 表示的 更为复杂的联接或关联子查询。 例如,下面的查询从订单定位到客户,以此方式将结果限制为只包括位于伦敦的客户的订单。 如果关系属性不存在,您必须作为联接手动编写它们,就像您在 SQL 查询中进行编写时一样,如下面的代码所示: 您可以使用关系属性来定义一次这种特殊关系。然后您就可以使用更为方便的点语法。但之所以存在关系属性,更 重要的原因在于特定于域的对象模型通常定义为层次结构或关系图。您要进行编程的那些对象引用其他对象。如果 对象与对象的关系相当于数据库中外键类型的关系,那么这只是一种令人庆幸的巧合而已。在这种情况下,可以很 方便地通过访问属性来编写联接。 就这一点而言,关系属性在查询的结果方面要比作为查询本身的一部分重要。在查询检索到关于特定客户的数据 后,类定义会指示客户下了订单。换言之,您希望特定客户的 Orders 属性是用该客户下的所有订单填充的集合。 这实际上是您通过以此方式定义类声明的协定。即便查询并未请求订单,您也希望在那里看到这些订单。您希望自 己的对象模型保持这样的错觉:它是数据库在内存中的扩展,并且相关对象立即可用。 既然您已经具备了关系,您就可以通过引用您的类中定义的关系属性来编写查询。这些关系引用相当于数据库中的 外键关系。使用这些关系的操作会转换成用等效的 SQL 表示的更为复杂的联接。只要您已经定义关系(使用 AssociationAttribute 属性),您就无需在 LINQ to SQL 中编写显式联接的代码。 为帮助保持这种错觉,LINQ to SQL 实现了一种称作延迟加载的技术。有关更多信息,请参见延迟加载与立即加载的 比较 (LINQ to SQL)。 可以考虑用下面的 SQL 查询来投影 CustomerID-OrderID 对的列表: 下图以图形方式显示了表关系。 若要通过使用 LINQ to SQL 获得相同的结果,您可以使用 Customer 类中已经存在的 Orders 属性引用。Orders C# 复制代码 Northwnd db = new Northwnd(@"northwnd.mdf"); IQueryable londonOrderQuery = from ord in db.Orders where ord.Customer.City == "London" select ord; C# 复制代码 Northwnd db = new Northwnd(@"northwnd.mdf"); IQueryable londonOrderQuery = from cust in db.Customers join ord in db.Orders on cust.CustomerID equals ord.CustomerID where cust.City == "London" select ord; 复制代码 SELECT t0.CustomerID, t1.OrderID FROM Customers AS t0 INNER JOIN Orders AS t1 ON t0.CustomerID = t1.CustomerID WHERE (t0.City = @p0) 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/297878d0-685b-4c... 引用提供了执行查询和投影 CustomerID-OrderID 对所必需的信息,如下面的代码所示: 您也可以反向操作。也就是说,您可以查询 Orders,然后使用其 Customer 关系引用来访问关于关联的 Customer 对象的信息。下面的代码投影与前面相同的 CustomerID-OrderID 对,但这一次采用的方法是查询 Orders 而非 Customers。 请参见 C# 复制代码 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 }; C# 复制代码 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 中的查询概念 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/297878d0-685b-4c... 全部折叠 代码:C# LINQ to SQL 远程查询执行与本地查询执行的比较 (LINQ to SQL) 请参见 发送反馈意见 您可以决定以远程方式(即数据库引擎对数据库执行查询)或在本地(LINQ to SQL 对本地缓存执行查询)执行您的查询。 远程执行 本地执行 比较 请参见 请考虑下面的查询: 如果数据库有数千行订单,则在处理其中很小一部分时您不需要将它们全都检索出来。在 LINQ to SQL 中,EntitySet (TEntity) 类实现了 IQueryable 接口。这种方式确保了可以以远程方式执行此类查询。利用此技术有两大优点: z 不会检索到不需要的数据。 z 由于利用了数据库索引,由数据库引擎执行的查询通常更为高效。 C# 复制代码 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. } 在其他一些情况下,您可能需要在本地缓存中保留完整的相关实体集。为此,EntitySet(TEntity) 提供了 Load 方法, 用于显式加载 EntitySet(TEntity) 的所有成员。 如果 EntitySet(TEntity) 已经加载,则后续查询将在本地执行。这种方式在两个方面起到帮助作用: z 如果此完整集必须在本地使用或使用多次,则您可以避免远程查询和与之相关的延迟。 z 实体可以序列化为完整的实体。 下面的代码段演示了如何实现本地执行: C# 复制代码 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. } } 这两项功能提供了强大的选项组合:大型集合采用以远程方式执行,小型集合或在需要完整集的情况下在本地执行。 您需要通过 IQueryable 进行远程执行,对于本地执行,则需要对内存中的 IEnumerable(T) 集合执行。若要强制在本 地执行(即 IEnumerable(T)),请参见如何:将类型转换为泛型 IEnumerable (LINQ to SQL)。 针对无序集的查询 对于实现 List(T) 的本地集合与提供对关系数据库中无序集执行的远程查询的集合,请注意它们之间的重要差异。List (T) 方法(如使用索引值的那些方法)需要列表语义,列表语义通常无法通过针对无序集的远程查询获得。因此,此类 方法隐式加载 EntitySet(TEntity),以允许本地执行。 概念 LINQ to SQL 中的查询概念 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/ee50e943-9349-4c... 全部折叠 代码:C# LINQ to SQL 延迟加载与立即加载的比较 (LINQ to SQL) 请参见 发送反馈意见 查询某对象时,实际上您只检索请求的对象。不会同时自动获取相关对象。(有关更多信息,请参见跨关系查询 (LINQ to SQL)。) 您无法看到相关对象尚未加载这一事实,原因是尝试访问它们时将产生检索它们的请求。 例如,您可能需要查询一组特定的订单,然后偶而向特定客户发送电子邮件通知。您最初不一定需要检索与每个订 单有关的所有客户数据。您可以使用延迟加载将额外信息的检索操作延迟到您确实需要检索它们时再进行。请看下 面的示例: 反过来也可能是可行的。您的应用程序可能必须同时查看客户数据和订单数据。您了解同时需要这两组数据。您了 解一旦获得结果,您的应用程序就需要每个客户的订单信息。您不希望一个一个地提交对每个客户的订单的查询。 您真正想要的是将订单数据与客户信息一起检索出来。 您还可以在查询中联接客户和订单,方法是构建叉积并将所有相关数据位作为一个大型投影检索出来。但这些结果 并非实体。(有关更多信息,请参见 LINQ to SQL 对象模型)。实体是具有标识且您可以修改的对象,而这些结果 将是无法更改和持久化的投影。更糟的是,您将检索到大量的冗余数据,因为在平展联接输出中,对于每个订单, 每个客户将重复出现。 您真正需要的是同时检索相关对象的集合的方法。此集合是关系图的精确剖面,因此您检索到的数据绝不会比您所 需要的数据多或少。为此,LINQ to SQL 提供了 DataLoadOptions,用以立即加载对象模型的某一区域。方法包括: z LoadWith 方法,用于立即加载与主目标相关的数据。 z AssociateWith 方法,用于筛选为特定关系检索到的对象。 请参见 C# 复制代码 Northwnd db = new Northwnd(@"northwnd.mdf"); IQueryable 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); } } C# 复制代码 Northwnd db = new Northwnd(@"c:\northwnd.mdf"); db.DeferredLoadingEnabled = false; IQueryable 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); } } 概念 LINQ to SQL 中的查询概念 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/d1d7247f-a3b7-46... 全部折叠 代码:C# LINQ to SQL LINQ to SQL 中的安全性 请参见 发送反馈意见 连接到数据库时始终都存在安全风险,特别是在连接字符串中的密码清楚可读时更是如此。 为了尽可能降低此类风险,请使用集成安全性与 SQL Server 建立可信连接。通过使用这种方法,您无需将密码存储 在连接字符串中。有关更多信息,请参见 SQL Server 安全性 (ADO.NET)。 请参见 概念 背景信息 (LINQ to SQL) 常见问题 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/d49787f7-414e-4c7... 全部折叠 代码:C# LINQ to SQL 序列化 (LINQ to SQL) 请参见 发送反馈意见 本主题介绍 LINQ to SQL 的序列化功能。下面几段提供了有关在设计时如何在代码生成期间添加序列化以及 LINQ to SQL 类的运行时序列化行为的信 息。 您可以通过以下任一方法在设计时添加序列化代码: z 在对象关系设计器中,将“Serialization Mode”属性更改为“Unidirectional”。有关更多信息,请参见Object Relational Designer (O/R Designer) 和对 象关系设计器(O/R 设计器). z 在 SQLMetal 命令行中,添加 /serialization 选项。有关更多信息,请参见代码生成工具 (SqlMetal.exe)。 概述 代码示例 由 LINQ to SQL 生成的代码默认情况下提供延迟加载功能。延迟加载对在中间层以透明方式根据需要加载数据而言非常方便。但是,它会给序列 化过程带来问题,原因是不论是否需要进行延迟加载,序列化程序都会触发延迟加载。实际上,对对象进行序列化时,会对其在所有出站延迟加 载引用下的可传递闭包进行序列化。 LINQ to SQL 的序列化功能主要通过以下两种机制来解决此问题: z 用于关闭延迟加载的 DataContext 模式 (ObjectTrackingEnabled)。有关更多信息,请参见 DataContext。 z 用于在生成的实体中生成 System.Runtime.Serialization.DataContractAttribute 和 System.Runtime.Serialization.DataMemberAttribute 属性的 代码生成开关。这方面(包括处于序列化过程中的延迟加载类的行为)是本主题的主要介绍对象。 定义 z DataContract 序列化程序:.NET Framework 3.0 或更高版本的 Windows Communication Framework (WCF) 组件使用的默认序列化程序。 z 单向序列化:只包含单向关联属性(为避免出现循环)的类的已序列化版本。按照约定,主键-外键关系的父级端的属性标记为序列化。双向 关联中的另一端不进行序列化。 单向序列化是 LINQ to SQL 支持的唯一一种序列化。 下面的代码使用 Northwind 示例数据库中的传统 Customer 和 Order 类,并演示了如何用序列化属性修饰这些类。 C# 复制代码 // The class is decorated with the DataContract attribute. [Table(Name="dbo.Customers")] [DataContract()] public partial class Customer : INotifyPropertyChanging, INotifyPropertyChanged { C# 复制代码 // Private fields are not decorated with any attributes, and are // elided. private string _CustomerID; // Public properties are decorated with the DataMember // attribute and the Order property specifying the serial // number. See the Order class later in this topic for // exceptions. public Customer() { this.Initialize(); } [Column(Storage="_CustomerID", DbType="NChar(5) NOT NULL", CanBeNull=false, IsPrimaryKey=true)] [DataMember(Order=1)] public string CustomerID { get { return this._CustomerID; } set { if ((this._CustomerID != value)) { this.OnCustomerIDChanging(value); this.SendPropertyChanging(); this._CustomerID = value; this.SendPropertyChanged("CustomerID"); this.OnCustomerIDChanged(); } } } C# 复制代码 // The following Association property is decorated with // DataMember because it is the parent side of the // relationship. The reverse property in the Order class // does not have a DataMember attribute. This factor // prevents a 'cycle.' [Association(Name="FK_Orders_Customers", Storage="_Orders", OtherKey="CustomerID", DeleteRule="NO ACTION")] [DataMember(Order=13)] public EntitySet Orders { get { return this._Orders; } set 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/a15ae411-8dc2-4ca... 请参见 对于下例中的 Order 类,为简洁起见,只显示与 Customer 类对应的反向关联属性。它没有用来避免出现循环的 DataMember 属性。 如何序列化实体 您可以按如下方式序列化上一节中显示的代码中的实体: 自递归关系 自递归关系遵循相同的模式。与外键对应的关联属性 (Property) 不具有 DataMember 属性 (Attribute),而父属性 (Property) 则具有。 请考虑以下具有两个自递归关系的类:Employee.Manager/Reports 和 Employee.Mentor/Mentees。 { this._Orders.Assign(value); } } C# 复制代码 // The class for the Orders table is also decorated with the // DataContract attribute. [Table(Name="dbo.Orders")] [DataContract()] public partial class Order : INotifyPropertyChanging, INotifyPropertyChanged C# 复制代码 // Private fields for the Orders table are not decorated with // any attributes, and are elided. private int _OrderID; // Public properties are decorated with the DataMember // attribute. // The reverse Association property on the side of the // foreign key does not have the DataMember attribute. [Association(Name = "FK_Orders_Customers", Storage = "_Customer", ThisKey = "CustomerID", IsForeignKey = true)] public Customer Customer C# 复制代码 Northwnd db = new Northwnd(@"c\northwnd.mdf"); Customer cust = db.Customers.Where(c => c.CustomerID == "ALFKI").Single(); DataContractSerializer dcs = new DataContractSerializer(typeof(Customer)); StringBuilder sb = new StringBuilder(); XmlWriter writer = XmlWriter.Create(sb); dcs.WriteObject(writer, cust); writer.Close(); string xml = sb.ToString(); C# 复制代码 // No DataMember attribute. public Employee Manager; [DataMember(Order = 3)] public EntitySet Reports; // No DataMember public Employee Mentor; [DataMember(Order = 5)] public EntitySet Mentees; 概念 背景信息 (LINQ to SQL) 代码生成工具 (SqlMetal.exe) 如何:使实体可序列化 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/a15ae411-8dc2-4ca... 全部折叠 代码:C# LINQ to SQL 存储过程 (LINQ to SQL) 发送反馈意见 LINQ to SQL 在对象模型中使用方法来表示数据库中的存储过程。您可以通过应用 FunctionAttribute 属性和 ParameterAttribute 属性(如果需要)将方法指定为存储过程。有关更多信息,请参见 LINQ to SQL 对象模型。 使用 Visual Studio 的开发人员通常会使用对象关系设计器来映射存储过程。有关更多信息,请参见Object Relational Designer (O/R Designer) 和对象关系设计器(O/R 设计器).本节中的主题说明了在您自行编写代码的情况下,如何 在您的应用程序中构建和调用这些方法。 本节内容 相关章节 如何:使用存储过程返回行集合 (LINQ to SQL) 介绍如何返回数据行并说明如何使用输入参数。 如何:使用带参数的存储过程 (LINQ to SQL) 介绍如何使用输入和输出参数。 如何:使用为多个结果形状映射的存储过程 (LINQ to SQL) 介绍如何实现在同一存储过程中返回多个形状。 如何:使用为顺序结果形状映射的存储过程 (LINQ to SQL) 介绍如何在返回顺序已知的情况下提供多个形状。 使用存储过程来自定义操作 (LINQ to SQL) 介绍如何使用存储过程来实现插入、更新和删除操作。 通过仅使用存储过程来自定义操作 (LINQ to SQL) 介绍如何只使用存储过程来实现插入、更新和删除操作。 编程指南 (LINQ to SQL) 提供有关如何创建和使用 LINQ to SQL 对象模型的信息。 演练:仅使用存储过程 (Visual Basic) (LINQ to SQL) 包含了演示如何在 Visual Basic 中使用存储过程的步骤。 演练:仅使用存储过程 (C#) (LINQ to SQL) 包含了演示如何在 C# 中使用存储过程的步骤。 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/4d23dd7a-a85f-44f... 全部折叠 代码:C# LINQ to SQL 如何:使用存储过程返回行集合 (LINQ to SQL) 示例 请参见 发送反馈意见 此示例从数据库中返回行集合,并包含用于筛选结果的输入参数。 当您执行返回行集合的存储过程时,会用到结果类,它存储从存储过程中返回的结果。有关更多信息,请参见分析 LINQ to SQL 源代码。 示例 请参见 下面的示例表示一个存储过程,该存储过程返回客户行并使用输入参数来仅返回将“London”列为客户城市的那些行。该示例假定有一个可枚举的 CustomersByCityResult 类。 复制代码 CREATE PROCEDURE [dbo].[Customers By City] (@param1 NVARCHAR(20)) AS BEGIN -- SET NOCOUNT ON added to prevent extra result sets from -- interfering with SELECT statements. SET NOCOUNT ON; SELECT CustomerID, ContactName, CompanyName, City from Customers as c where c.City=@param1 END C# 复制代码 [Function(Name="dbo.Customers By City")] public ISingleResult CustomersByCity([Parameter(DbType="NVarChar(20)")] string param1) { IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), param1); return ((ISingleResult)(result.ReturnValue)); } // Call the stored procedure. void ReturnRowset() { Northwnd db = new Northwnd(@"c:\northwnd.mdf"); ISingleResult result = db.CustomersByCity("London"); foreach (CustomersByCityResult cust in result) { Console.WriteLine("CustID={0}; City={1}", cust.CustomerID, cust.City); } } 概念 存储过程 (LINQ to SQL) 下载示例数据库 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/725718f5-da29-48... 全部折叠 代码:C# LINQ to SQL 如何:使用带参数的存储过程 (LINQ to SQL) 示例 请参见 发送反馈意见 LINQ to SQL 将输出参数映射到引用参数,并且对于值类型,它将参数声明为可以为 null。 有关如何在返回行集的查询中使用输入参数的示例,请参见如何:使用存储过程返回行集合 (LINQ to SQL)。 示例 请参见 下面的示例带有单个输入参数(客户 ID)并返回一个输出参数(该客户的总销售额)。 您将按如下方式调用此存储过程: 复制代码 CREATE PROCEDURE [dbo].[CustOrderTotal] @CustomerID nchar(5), @TotalSales money OUTPUT AS SELECT @TotalSales = SUM(OD.UNITPRICE*(1-OD.DISCOUNT) * OD.QUANTITY) FROM ORDERS O, "ORDER DETAILS" OD where O.CUSTOMERID = @CustomerID AND O.ORDERID = OD.ORDERID C# 复制代码 [Function(Name="dbo.CustOrderTotal")] [return: Parameter(DbType="Int")] public int CustOrderTotal([Parameter(Name="CustomerID", DbType="NChar(5)")] string customerID, [Parameter(Name="TotalSales", DbType="Money")] ref System.Nullable totalSales) { IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), customerID, totalSales); totalSales = ((System.Nullable)(result.GetParameterValue(1))); return ((int)(result.ReturnValue)); } C# 复制代码 Northwnd db = new Northwnd(@"c:\northwnd.mdf"); decimal? totalSales = 0; db.CustOrderTotal("alfki", ref totalSales); Console.WriteLine(totalSales); 概念 存储过程 (LINQ to SQL) 下载示例数据库 (LINQ to SQL) 使用可以为 null 的类型(C# 编程指南) 可以为 Null 的值类型 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/b935fd84-cb9c-420... 全部折叠 代码:C# LINQ to SQL 如何:使用为多个结果形状映射的存储过程 (LINQ to SQL) 示例 请参见 发送反馈意见 当存储过程可以返回多个结果形状时,返回类型无法强类型化为单个投影形状。尽管 LINQ to SQL 可以生成所有可能的投影类型,但它无法获知将以何种顺序返回它 们。 请将这种情况与按顺序产生多个结果形状的存储过程作一个对比。有关更多信息,请参见如何:使用为顺序结果形状映射的存储过程 (LINQ to SQL)。 ResultTypeAttribute 属性适用于返回多个结果类型的存储过程,用以指定该过程可以返回的类型的集合。 示例 请参见 在下面的 SQL 代码示例中,结果形状取决于输入(shape =1 或 shape = 2)。您不知道将先返回哪个投影。 您将使用类似于以下内容的代码来执行此存储过程。 复制代码 CREATE PROCEDURE VariableResultShapes(@shape int) AS if(@shape = 1) select CustomerID, ContactTitle, CompanyName from customers else if(@shape = 2) select OrderID, ShipName from orders C# 复制代码 [Function(Name="dbo.VariableResultShapes")] [ResultType(typeof(VariableResultShapesResult1))] [ResultType(typeof(VariableResultShapesResult2))] public IMultipleResults VariableResultShapes([Parameter(DbType="Int")] System.Nullable shape) { IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), shape); return ((IMultipleResults)(result.ReturnValue)); } 注意: 您必须根据您对存储过程的认识,使用 GetResult(TElement) 模式获取正确类型的枚举数。 C# 复制代码 Northwnd db = new Northwnd(@"c:\northwnd.mdf"); // Assign the results of the procedure with an argument // of (1) to local variable 'result'. IMultipleResults result = db.VariableResultShapes(1); // Iterate through the list and write results (the company names) // to the console. foreach(VariableResultShapesResult1 compName in result.GetResult()) { Console.WriteLine(compName.CompanyName); } // Pause to view company names; press Enter to continue. Console.ReadLine(); // Assign the results of the procedure with an argument // of (2) to local variable 'result'. IMultipleResults result2 = db.VariableResultShapes(2); // Iterate through the list and write results (the order IDs) // to the console. foreach (VariableResultShapesResult2 ord in result2.GetResult()) { Console.WriteLine(ord.OrderID); } 概念 存储过程 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/c2b84dfe-7fec-489... 全部折叠 代码:C# LINQ to SQL 如何:使用为顺序结果形状映射的存储过程 (LINQ to SQL) 示例 请参见 发送反馈意见 这种存储过程可以生成多个结果形状,但您知道结果的返回顺序。请将此方案与您不知道返回顺序的方案作一个对比。有关更多信息,请参见如何:使用为 多个结果形状映射的存储过程 (LINQ to SQL)。 示例 请参见 下面是一个按顺序返回多个结果形状的存储过程的 T-SQL。 您将使用类似于以下内容的代码来执行此存储过程。 复制代码 CREATE PROCEDURE MultipleResultTypesSequentially AS select * from products select * from customers C# 复制代码 [Function(Name="dbo.MultipleResultTypesSequentially")] [ResultType(typeof(MultipleResultTypesSequentiallyResult1))] [ResultType(typeof(MultipleResultTypesSequentiallyResult2))] public IMultipleResults MultipleResultTypesSequentially() { IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod()))); return ((IMultipleResults)(result.ReturnValue)); } C# 复制代码 Northwnd db = new Northwnd(@"c:\northwnd.mdf"); IMultipleResults sprocResults = db.MultipleResultTypesSequentially(); // First read products. foreach (Product prod in sprocResults.GetResult()) { Console.WriteLine(prod.ProductID); } // Next read customers. foreach (Customer cust in sprocResults.GetResult()) { Console.WriteLine(cust.CustomerID); } 概念 存储过程 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/a73530de-5a4e-4d... 全部折叠 代码:C# LINQ to SQL 使用存储过程来自定义操作 (LINQ to SQL) 请参见 发送反馈意见 存储过程代表用于重写默认行为的常见方法。本主题中的示例演示了如何将生成的方法包装用于存储过程,以及如 何直接调用存储过程。 如果您使用的是 Visual Studio,您可以使用对象关系设计器分配存储过程以执行插入、更新和删除操作。 示例 示例 注意: 若要读回数据库生成的值,请在存储过程中使用输出参数。如果无法使用输出参数,请编写分部方法实现,而 不是依靠对象关系设计器生成的重写。在成功完成 INSERT 或 UPDATE 操作后,必须将映射到数据库生成的 值的成员设置为适当的值。有关更多信息,请参见开发人员在重写默认行为方面的责任 (LINQ to SQL)。 说明 在下面的示例中,假定 Northwind 类包含两个方法,这两个方法可用来调用要用于派生类中的重写的存储过 程。 代码 C# 复制代码 [Function()] public IEnumerable CustomerOrders( [Parameter(Name = "CustomerID", DbType = "NChar(5)")] string customerID) { IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), customerID); return ((IEnumerable)(result.ReturnValue)); } [Function()] public IEnumerable CustomerById( [Parameter(Name = "CustomerID", DbType = "NChar(5)")] string customerID) { IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), customerID); return (IEnumerable)(result.ReturnValue); } 说明 下面的类将这些方法用于重写。 代码 C# 复制代码 public class NorthwindThroughSprocs : Northwnd { public NorthwindThroughSprocs(string connection) : base(connection) { } // Override loading of Customer.Orders by using method wrapper. private IEnumerable LoadOrders(Customer customer) { return this.CustomerOrders(customer.CustomerID); } // Override loading of Order.Customer by using method wrapper. private Customer LoadCustomer(Order order) { 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/aedbecc1-c33c-4fb... 示例 请参见 return this.CustomerById(order.CustomerID).Single(); } // Override INSERT operation on Customer by calling the // stored procedure directly. private void InsertCustomer(Customer customer) { // Call the INSERT stored procedure directly. this.ExecuteCommand("exec sp_insert_customer …"); } // The UPDATE override works similarly, that is, by // calling the stored procedure directly. private void UpdateCustomer(Customer original, Customer current) { // Call the UPDATE stored procedure by using current // and original values. this.ExecuteCommand("exec sp_update_customer …"); } // The DELETE override works similarly. private void DeleteCustomer(Customer customer) { // Call the DELETE stored procedure directly. this.ExecuteCommand("exec sp_delete_customer …"); } } 说明 您可以完全像使用 Northwnd 一样使用 NorthwindThroughSprocs。 代码 C# 复制代码 NorthwindThroughSprocs db = new NorthwindThroughSprocs(""); var custQuery = from cust in db.Customers where cust.City == "London" select cust; foreach (Customer custObj in custQuery) // deferred loading of cust.Orders uses the override LoadOrders. foreach (Order ord in custObj.Orders) // ... // Make some changes to customers/orders. // Overrides for Customer are called during the execution of the // following: db.SubmitChanges(); 概念 开发人员在重写默认行为方面的责任 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/aedbecc1-c33c-4fb... 全部折叠 代码:C# LINQ to SQL 通过仅使用存储过程来自定义操作 (LINQ to SQL) 请参见 发送反馈意见 通过仅使用存储过程来访问数据是常见的情况。 示例 请参见 说明 在使用存储过程来自定义操作 (LINQ to SQL) 提供的示例中,您甚至可以将第一个查询(导致动态 SQL 执 行)替换为包装了存储过程的方法调用,通过这种方法来修改此示例。 假定 CustomersByCity 就是此方法,如下面的示例所示。 代码 下面的代码不用任何动态 SQL 即可执行。 C# 复制代码 [Function()] public IEnumerable CustomersByCity( [Parameter(Name = "City", DbType = "NVarChar(15)")] string city) { IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), city); return ((IEnumerable)(result.ReturnValue)); } C# 复制代码 NorthwindThroughSprocs db = new NorthwindThroughSprocs("..."); // Use a method call (stored procedure wrapper) instead of // a LINQ query against the database. var custQuery = db.CustomersByCity("London"); foreach (Customer custObj in custQuery) { // Deferred loading of custObj.Orders uses the override // LoadOrders. There is no dynamic SQL. foreach (Order ord in custObj.Orders) { // Make some changes to customers/orders. // Overrides for Customer are called during the execution // of the following. } } db.SubmitChanges(); 概念 开发人员在重写默认行为方面的责任 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/441e8ef3-998c-4d1... 全部折叠 代码:C# LINQ to SQL 事务 (LINQ to SQL) 请参见 发送反馈意见 LINQ to SQL 支持三种不同的事务模型。下文按执行检查的顺序列出了这些模型。 显式本地事务 显式可分发事务 隐式事务 请参见 调用 SubmitChanges 时,如果 Transaction 属性设置为 (IDbTransaction) 事务,则在同一事务的上下文中 执行 SubmitChanges 调用。 成功执行事务后,要由您来提交或回滚事务。与事务对应的连接必须与用于构造 DataContext 的连接匹配。 如果使用其他连接,则会引发异常。 可以在当前 Transaction 的作用域中调用 LINQ to SQL API(包括但不限于 SubmitChanges)。LINQ to SQL 检测到调用是在事务的作用域内,因而不会创建新的事务。在这种情况下,vbtecdlinq 还会 避免关闭连接。您可以在此类事务的上下文中执行查询和 SubmitChanges 操作。 当您调用 SubmitChanges 时,LINQ to SQL 会检查此调用是否在 Transaction 的作用域内或者 Transaction 属性 (IDbTransaction) 是否设置为由用户启动的本地事务。如果这两个事务它均未找到,则 LINQ to SQL 启动本地事务 (IDbTransaction),并使用此事务执行所生成的 SQL 命令。当所有 SQL 命令均已成功执行完 毕时,LINQ to SQL 提交本地事务并返回。 概念 背景信息 (LINQ to SQL) 如何:使用事务封闭数据提交 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/8cceb26e-8d36-43... 全部折叠 代码:C# LINQ to SQL 类型系统不匹配 (LINQ to SQL) 请参见 发送反馈意见 LINQ to SQL 可以自动完成对象模型和 SQL 数据库管理系统 (DBMS) 之间的大量转换。不过,有一些情况会阻碍进 行精确转换。 本主题的目的是总结通用类型系统 (CTS) 类型和数据库类型之间的关键类型系统不匹配情况。有关更多信息,请参 见 SQL-CLR 类型映射 (LINQ to SQL) 和数据类型和函数 (LINQ to SQL)。 类型系统 数据类型 将讨论的两个类型系统如下: z 对象模型的类型系统。 此类型系统由以下两者共同定义:通过公共语言运行库 (CLR) 实现的公共语言规范 (CLS) 语言(如 Visual Basic 或 C#)的一个版本,以及在 .NET Framework 的 System 命名空间中定义的核心类型。前者 包括 Int32、Int64 和 String,后者包括派生类型,如 DateTime、TimeSpan 等。有关更多信息,请参见 通用类型系统概述。 z 由 SQL 实现定义的类型系统。 对于本主题而言,它是 SQL Server 的 T-SQL 实现在 SQL Server 2000 和 SQL Server 2005 中实现的类 型系统。 转换值时必须考虑数据类型之间的基本差异。具体化查询中的投影会将数据库类型的值转换为由查询程序员指 定的相应 CTS 类型。同样,使用参数需要反向的转换。 例如,下面的查询需要进行两次值转换: DateOfBirth 的 T-SQL 值必须转换为相应的 CTS 值(例如从 T-SQL DateTime 转换到 CTS System.DateTime),而 id 的 CTS 值必须转换为相应的 T-SQL 类型参数值(例如从 CTS System.Int32 转换到 T-SQL integer)。 缺少对应项 以下类型没有合理的对应项。 z System.* CTS 类型 z 无符号整数。这些类型通常映射到较大大小的有符号对应项,以避免溢出。文本可以根据值转换为相 同或较小大小的有符号数字。 z Boolean。这些类型可以映射到位、较大的数字或字符串。文本可以映射到计算结果为相同值的表达 式(例如,SQL 中的 1=1 对应于 CLS 中的 True)。 z TimeSpan。此类型表示两个 DateTime 值之间的差异,并且不与 SQL Server 中的 timestamp 相对 应。 z SQL Server 类型 z 固定长度字符类型。T-SQL 对 Unicode 和非 Unicode 类别加以区分,每个类别有三个不同类型:固 定长度的 nchar/char、限制为 8K 字符的可变长度 nvarchar/varchar,以及较大大小(最大 230 -1)的 ntext/text。固定长度字符类型可以映射到 CTS char[] 以检索字符,但在转换和行为上不 能真正对应于同一类型。 z Bit。尽管 bit 域与 Nullable 值的数目相同,但它们是两个不同的类型。Bit 使用值 1 和 0 而不是 true/false,并且不能用作布尔表达式的等效项。 z Timestamp。与 CTS TimeSpan 不同,T-SQL timestamp 表示由数据库生成的 8 字节数字,它对于 每次更新都是唯一的,而不是基于 DateTime 值之间的差异。 z Money 和 SmallMoney。这些类型可以映射到 Decimal,但本质上是不同的类型,并且基于服务器 的函数和转换也将它们视为不同的类型。 多重映射 以下类型具有多重映射。您可以将此问题看作是广义上的缺少对应项问题,因为每个缺少对应项的类型都可以 复制代码 Select DateOfBirth From Customer Where CustomerId = @id 注意: System.Data.SqlTypes 中特定于 SQL Server 的 .NET Framework 类型不包含在比较范围内。 页码,1/7(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/0a90c33f-7ed7-450... 表达式语义 映射到其他类型系统的一个或多个具有不同值和表达式语义的类型。所有值转换都必须考虑所使用的映射以及 对往返过程的潜在需求。所有表达式转换都必须考虑映射域中的类型特定语义。 z CTS 类型 z String。可以映射到六个使用不同编码 (ASCII/Unicode)、大小(8k/更多)和填充(固定/可变长度) 的不同字符类型之一。 z DateTime。可以映射到 DateTime 或 SmallDateTime。 z 数据库类型 z Decimal。可以映射到 Decimal 或 Double,具体取决于小数位数、精度和限制值范围的 CHECK 约 束。 z Integer。Integer 类型可以映射到有符号或无符号的 CTS 整数类型(取决于限制值范围的 CHECK 约束)。无符号类型可以使用相同大小或者低一个级别的大小(例如,int 可以映射到 Int32、 UInt32 或 UInt16)。 精度和小数位数的差异 示例包括: z Decimal。Decimal 最多允许 29 位数,而 T-SQL 小数最多允许 38 位数。若要保留数量级,T-SQL 小数 必须映射到 Double。此类映射会损失精度,因为 Double 不具备相等的精度。另一方面,若不限制 T- SQL 小数的精度,转换到 Decimal 会导致损失数量级,而这是不可接受的。 z DateTime/SmallDateTime。三种类型(T-SQL DateTime、T-SQL SmallDateTime 和 CTS System.DateTime)的值范围和特定性都不同。任何值转换都可能导致错误或损失精度。 z 浮点数。这些类型的语义尤其不同。CTS 浮点数支持正零和负零、正无穷和负无穷,以及 NaN(非数 字)值。SQL real 和 float 则不支持。SQL 还支持可变精度(尾数位数)浮点数,而 CTS 使用预设精 度(尾数)。 非默认映射 有些映射值得注意,因为旧版数据库架构使用与其在 CLS 程序中的预期用法不对应的类型。请参考下面的数 据定义语言 (DDL) 及其在 Visual Basic 和 C# 中的对应类: 在上面的示例中,类 C1 的成员映射到一个完全不同的类型(整数映射到字符串)。它们有明显的值域差异。 当进行读取操作时,大于 231-1 的整数时将导致溢出。如果使用十进制表示法,将无法表示 10 位的负整 数。更值得注意的是,转换表达式时必须考虑映射,因为映射不仅影响投影,还影响 where 子句。 另一个常见示例是将用户定义的枚举映射到一个整型列。对于数据库中不使用用户定义枚举的旧版架构和数 据,此非默认映射需要进行额外的验证。 用户定义的类型 用户定义的(使用 CLS 语言)类型旨在帮助弥合类型系统之间的差异。不过,这些类型也引起了与类型版本 管理有关的值得注意的问题。客户端上的版本更改可能无法与数据库服务器上存储的类型的更改相匹配。任何 此类更改都会导致另一个类型不匹配,体现在其类型语义不匹配,并且版本差距可能变得明显。在后续版本中 重构继承层次结构时,会发生更多的复杂问题。 复制代码 create table T1 ( Col1 nchar(10), Col2 nchar(10) ) C# 复制代码 class C1 { int x; // Map to T1.Col1. int y; // Map to T1.Col2. } 除了 CTS 和数据库类型之间配对的不匹配之外,表达式增加了不匹配的复杂性。必须考虑运算符语义、函数 语义、隐式类型转换和优先级规则方面的不匹配。 以下小节演示了表面相似的表达式之间的不匹配。可以生成在语义上与给定 CLS 表达式相等的 SQL 表达式。 但是对于 CLS 用户来说,表面相似的表达式之间的语义差异是否明显,这一点并不明确。因此,是否需要进 行实现语义等效方面的更改也不明确。要计算出表达式的一组值时,这个问题尤为严重。差异的明显程度可能 取决于数据,因此很难在编码和调试过程中确定。 页码,2/7(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/0a90c33f-7ed7-450... null 语义 SQL 表达式为布尔表达式提供了三值逻辑,结果可以是 true、false 或 null。相比之下,CLS 为涉及 null 值的 比较指定了两值布尔结果。参考下列代码: 出现存在两个值的结果时会发生类似问题。 在上例中,在生成 SQL 方面可能得出等效的行为,但是转换可能不会准确地反映您的意图。 LINQ to SQL 不会将 C# null 或 Visual Basic nothing 比较语义施加在 SQL 上。比较运算符在语义上被转换 为其 SQL 等效项。反映 SQL 语义的语义是由服务器或连接设置定义的。在默认的 SQL Server 设置下,两个 null 值被视为不相等(尽管您可以更改设置以改变语义)。无论如何,LINQ to SQL 在查询转换中不会考虑服 务器设置。 带有文本 null (nothing) 的比较被转换为相应的 SQL 版本(is null 或 is not null)。 排序规则中的 null (nothing) 值是由 SQL Server 定义的;LINQ to SQL 不会更改排序规则。 类型转换和提升 SQL 支持在表达式中使用一组丰富的隐式转换。C# 中的类似表达式则需要显式强制转换。例如: z Nvarchar 和 DateTime 类型在 SQL 中可以不经过任何显式强制转换进行比较,而在 C# 中则需要显式 转换。 z Decimal 在 SQL 中隐式转换为 DateTime。C# 不允许使用隐式转换。 同样,由于基础类型集不同,T-SQL 中的类型优先级与 C# 中的类型优先级不同。实际上,在优先级列表之间 没有明确的子集/父集关系。例如,将 nvarchar 和 varchar 进行比较会导致 varchar 表达式隐式转换为 C# 复制代码 Nullable i = null; Nullable j = null; if (i == j) { // This branch is executed. } 复制代码 -- Assume col1 and col2 are integer columns with null values. -- Assume that ANSI null behavior has not been explicitly -- turned off. Select … From … Where col1 = col2 -- Evaluates to null, not true and the corresponding row is not -- selected. -- To obtain matching behavior (i -> col1, j -> col2) change -- the query to the following: Select … From … Where col1 = col2 or (col1 is null and col2 is null) -- (Visual Basic 'Nothing'.) C# 复制代码 if ((i == j) || (i != j)) // Redundant condition. { // ... } 复制代码 -- Assume col1 and col2 are nullable columns. -- Assume that ANSI null behavior has not been explicitly -- turned off. Select … From … Where col1 = col2 or col1 != col2 -- Visual Basic: col1 <> col2. -- Excludes the case where the boolean expression evaluates -- to null. Therefore the where clause does not always -- evaluate to true. 页码,3/7(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/0a90c33f-7ed7-450... nvarchar。CTS 不提供等效的提升。 在简单的情况下,这些差异会导致 CLS 表达式包含对于相应的 SQL 表达式来说多余的强制转换。更重要的 是,SQL 表达式的中间结果可能隐式提升到在 C# 中没有精确对应项的类型,反之亦然。总之,此类表达式 的测试、调试和验证会给用户增加大量的负担。 排序规则 T-SQL 支持作为字符串类型的批注的显式排序规则。这些排序规则决定了某些比较的有效性。例如,比较两个 使用不同显式排序规则的列将会出错。使用大大简化的 CTS 字符串类型不会导致这种错误。请看下面的示 例: 实际上,排序顺序子句会创建不可替换的受限类型。 同样,各个类型系统的排序顺序会有明显差异。这种差异影响到对结果的排序。Guid 对全部 16 个字节按字 典顺序排序 (IComparable()),而 T-SQL 按下面的顺序比较 GUID:node(10-15)、clock-seq(8-9)、time- high(6-7)、time-mid(4-5)、time-low(0-3)。当 NT 生成的 GUID 具有类似的八位字节顺序时,此排序在 SQL 7.0 中完成。该方法确保在同一节点群集上生成的 GUID 按时间戳的顺序集合。该方法还可用于生成索引(插 入改为追加而不是随机 IO)。出于保密考虑,该顺序稍后在 Windows 中加密,但 SQL 必须维护兼容性。一 个解决方法是使用 SqlGuid 代替 Guid。 运算符和函数差异 本质上可比较的运算符和函数在语义上稍有不同。例如: z C# 基于逻辑运算符 && 和 || 的操作数的词法顺序指定短路语义。另一方面,SQL 面向基于集的查询, 因此在决定执行顺序方面为优化器提供了更大的自由度。由此产生的一些连带影响包括: z SQL 中语义相等的转换将需要“CASE … WHEN … THEN”构造,以避免对操作数执行重新排序。 z 如果 C# 表达式依赖于计算第二个操作数,同时第二个操作数的计算基于第一个操作数的计算结果, 则对 AND / OR 运算符的松散转换可能导致错误。 z 在 SQL 中始终检查溢出,但在 C# 中必须显式指定溢出(在中 Visual Basic 中则无须),以避免头尾回 绕。假设有整数列 C1、C2 和 C3,并且 C1+C2 存储在 C3 中 (Update T Set C3 = C1 + C2)。 复制代码 create table T2 ( Col1 nvarchar(10), Col2 nvarchar(10) collate Latin_general_ci_as ) C# 复制代码 class C { string s1; // Map to T2.Col1. string s2; // Map to T2.Col2. void Compare() { if (s1 == s2) // This is correct. { // ... } } } 复制代码 Select … From … Where Col1 = Col2 -- Error, collation conflict. 复制代码 create table T3 ( Col1 integer, Col2 integer ) insert into T3 (col1, col2) values (2147483647, 5) -- Valid values: max integer value and 5. select * from T3 where col1 + col2 < 0 -- Produces arithmetic overflow error. C# 复制代码 // C# overflow in absence of explicit checks. int i = Int32.MaxValue; 页码,4/7(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/0a90c33f-7ed7-450... z SQL 执行对称算法四舍五入,而 .NET Framework 使用“四舍六入五取偶”。有关更多信息,请参见知识 库文章 196652。 z 默认情况下,对于通用区域设置,字符串比较在 SQL 中不区分大小写。在 Visual Basic 和 C# 中,它们 区分大小写。例如,如果 s 是 food,则 s == "Food"(在 Visual Basic 中为 s = "Food")和 s == "food" 会产生不同的结果。 z Like 运算符基于隐式转换有效地获取自动重载。虽然 Like 运算符被定义为对字符串类型发生作用,但如 果对数字类型或 DateTime 类型进行隐式转换,则 Like 也可以用于这些非字符串类型。在 CTS 中,不 存在相应的隐式转换。因此,需要其他重载。 z 在语义上,SQL 中适用于固定长度字符类型参数的运算符/函数与适用于 CTS 字符串的相同运算符/函数 有明显不同。这也可以看作是关于类型的小节中讨论的缺少对应项问题的延伸。 字符串串联存在类似的问题。 z Round() 函数在 .NET Framework 和 T-SQL 中具有不同的语义。 z 字符串的起始索引在 CTS 中是 0,而在 SQL 中是 1。因此,任何具有索引的函数都需要进行索引转换。 int j = 5; if (i+j < 0) Console.WriteLine("Overflow!"); // This code prints the overflow message. 复制代码 -- Assume default US-English locale (case insensitive). create table T4 ( Col1 nvarchar (256) ) insert into T4 values (‘Food’) insert into T4 values (‘FOOD’) select * from T4 where Col1 = ‘food’ -- Both the rows are returned because of case-insensitive matching. C# 复制代码 // C# equivalent on collections of Strings in place of nvarchars. String[] strings = { "food", "FOOD" }; foreach (String s in strings) { if (s == "food") { Console.WriteLine(s); } } // Only "food" is returned. 注意: 此 Like 运算符行为仅适用于 C#;Visual Basic Like 关键字保持不变。 复制代码 create table T4 ( Col1 nchar(4) ) Insert into T5(Col1) values ('21'); Insert into T5(Col1) values ('1021'); Select * from T5 where Col1 like '%1' -- Only the second row with Col1 = '1021' is returned. -- Not the first row! C# 复制代码 // Assume Like(String, String) method. string s = ""; // map to T4.Col1 if (System.Data.Linq.SqlClient.SqlMethods.Like(s, "%1")) { Console.WriteLine(s); } // Expected to return true for both "21" and "1021" 复制代码 create table T6 ( Col1 nchar(4) Col2 nchar(4) ) Insert into T6 values ('a', 'b'); Select Col1+Col2 from T6 -- Returns concatenation of padded strings "a b " and not "ab". 页码,5/7(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/0a90c33f-7ed7-450... 差异的体现 z CTS 支持用于浮点数的模数 (‘%’) 运算符,但 SQL 不支持。 总之,可能需要对 CLS 表达式进行复杂转换,同时可能需要附加的运算符/函数来公开 SQL 功能。 类型强制转换 在 C# 和 SQL 中,用户可以使用显式类型强制转换(Cast 和 Convert)来重写默认的表达式语义。但是, 跨类型系统边界公开此功能将带来一个难题。提供所需语义的 SQL 强制转换不能方便地转换为相应的 C# 强 制转换。另一方面,由于类型不匹配、缺少对应项和类型优先级层次结构不同,C# 强制转换无法直接转换为 等效的 SQL 强制转换。需要在出现类型系统不匹配与损失表达式强大功能之间进行权衡。 在其他情况下,可能不需要对任何一种验证表达式的情况使用类型强制转换,但可能需要使用它来确保非默认 映射正确地应用到表达式。 复制代码 -- Example from “Non-default Mapping” section extended create table T5 ( Col1 nvarchar(10), Col2 nvarchar(10) ) Insert into T5(col1, col2) values (‘3’, ‘2’); C# 复制代码 class C { int x; // Map to T5.Col1. int y; // Map to T5.Col2. void Casting() { // Intended predicate. if (x + y > 4) { // valid for the data above } } } 复制代码 Select * From T5 Where Col1 + Col2 > 4 -- "Col1 + Col2" expr evaluates to '32' 类型系统差异至少可以通过服务器上的用户定义类型和客户端上的 SqlType 得到部分弥补。但是,如果使用 旧版架构和数据并要使用本机 CTS 类型进行编程,则使用上述类型远非最佳解决方法。因此,对于需要跨类 型系统工作的开发人员而言,这些差异通常是很明显的。这些差异体现为数据丢失、意外结果、映射约束或性 能问题。 一些症状可能依赖于数据,这使问题更为复杂。如果测试未涵盖特定的数据值,则不匹配可能表现为运行时意 外或更糟的情况,如无提示的数据损坏。 损值转换 缺少确切值对应关系时可能发生有损转换。此处是一个常见示例,虽然 System.Double 不会损失数据库小数 的数量级,但是数字从定点型转换为浮点型通常并不方便,并且可能导致精度损失。虽然选择在算法和格式上 相似的 System.Decimal 比较方便,但可能导致灾难性的数量级损失。 查询结果的语义差异 有损转换通常可以通过验证投影和参数的输入/输出转换进行检查。但是,诊断表达式语义上的差异(无论是 由隐式类型转换还是由运算符/函数差异造成的)可能要困难得多。请参考前面小节中列出的以下示例。每个 示例都假设 API 用户需要 CTS 语义,而对象关系转换层正在未经附加语义调整的情况下生成 SQL。 z 布尔表达式缺少“第三个”值(null,或 Visual Basic 中的 Nothing)。 两值 CTS 表达式必须转换为等效的三值表达式。请参见 4.1 节 z 非标准映射 在 SQL 中数字类型和字符串之间的自动来回转换可能导致有效但语义不同的表达式。请参见 4.5 节 z 排序规则差异 根据排序规则默认值和设置进行排序。请参见 4.3 节 页码,6/7(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/0a90c33f-7ed7-450... 请参见 z 有效的 CTS 表达式可能导致运行时错误,因为假设了特定的运算符语义。 例如,如果不使用短路语义将表达式转换到 SQL,合取(逻辑与)表达式中的第二个谓词可能导致错 误。请参见 4.4 节中的示例 1 映射限制 通过限制映射的 CTS 类型集和另外限制受限集中每个给定 CTS 类型的相应类型,可以限制类型不匹配造成的 问题。但是,这会将转换查询的任务推给用户。例如,如果在 4.5 节的示例中,不允许将 nvarchar 映射到 System.String,则查询开发人员必须执行值转换和查询表达式转换才能完成相同的任务。 性能问题 除了上面列出的语义差异之外,一个常见的实际问题是性能的显著降低。示例包括: z 强制对逻辑“与”/逻辑“或”运算符的计算顺序 生成 SQL 对谓词计算执行排序会限制 SQL 优化器的功能。 z 类型转换(无论是由 CLS 编译器引入还是由对象关系查询实现引入)可能限制索引的使用。 例如: 请考虑表达式 (s = SOME_STRING_CONSTANT) 的转换。 对于大型数据集,此类性能问题可能决定应用程序是否可部署。不仅语义差异比较关键,性能问题也是很关键 的。 复制代码 -- Table DDL create table T5 ( Col1 varchar(100) ) C# 复制代码 class C5 { string s; // Map to T5.Col1. } 复制代码 -- Corresponding part of SQL where clause Where … Col1 = SOME_STRING_CONSTANT -- This expression is of the form = . -- Hence SQL introduces a conversion from varchar to nvarchar, -- resulting in Where … Convert(nvarchar(100), Col1) = SOME_STRING_CONSTANT -- Cannot use the index for column Col1 for some implementations. 概念 背景信息 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,7/7(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/0a90c33f-7ed7-450... 全部折叠 代码:C# LINQ to SQL 用户定义的函数 (LINQ to SQL) 发送反馈意见 LINQ to SQL 在您的对象模型中使用方法来表示用户定义的函数。您可以通过应用 FunctionAttribute 属性和 ParameterAttribute 属性(如果需要)将方法指定为函数。有关更多信息,请参见 LINQ to SQL 对象模型。 为避免出现 InvalidOperationException,LINQ to SQL 中用户定义的函数必须采用以下形式之一: z 包装为具有正确映射属性的方法调用的函数。有关更多信息,请参见基于属性的映射 (LINQ to SQL)。 z 特定于 LINQ to SQL 的静态 SQL 方法。 z .NET Framework 方法支持的函数。 本节中的主题说明了在您自行编写代码的情况下,如何在您的应用程序中构建和调用这些方法。使用 Visual Studio 的开发人员通常会使用对象关系设计器来映射用户定义的函数。有关更多信息,请参见Object Relational Designer (O/R Designer) 和对象关系设计器(O/R 设计器). 本节内容 如何:使用用户定义的标量值函数 (LINQ to SQL) 介绍如何实现返回标量值的函数。 如何:使用用户定义的表值函数 (LINQ to SQL) 介绍如何实现返回表值的函数。 如何:内联调用用户定义的函数 (LINQ to SQL) 介绍如何对函数进行内联调用,以及进行内联调用时在执行方面的差异。 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/3304c9b2-5c7a-4a... 全部折叠 代码:C# LINQ to SQL 如何:使用用户定义的标量值函数 (LINQ to SQL) 示例 请参见 发送反馈意见 您可以通过使用 FunctionAttribute 属性将类中定义的客户端方法映射到用户定义的函数。请注意,方法体会构造一 个捕获方法调用意向的表达式,并将该表达式传递给 DataContext 进行转换和执行。 示例 请参见 注意: 只有在查询外部调用此函数时,才会直接执行。有关更多信息,请参见如何:内联调用用户定义的函数 (LINQ to SQL)。 下面的 SQL 代码展示了一个用户定义的标量值函数 ReverseCustName()。 您可以为此代码映射一个客户端方法,例如,可以映射下面这个方法: 复制代码 CREATE FUNCTION ReverseCustName(@string varchar(100)) RETURNS varchar(100) AS BEGIN DECLARE @custName varchar(100) -- Implementation left as exercise for users. RETURN @custName END C# 复制代码 [Function(Name = "dbo.ReverseCustName", IsComposable = true)] [return: Parameter(DbType = "VarChar(100)")] public string ReverseCustName([Parameter(Name = "string", DbType = "VarChar(100)")] string @string) { return ((string)(this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), @string).ReturnValue)); } 概念 用户定义的函数 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/714e252f-c053-4bb... 全部折叠 代码:C# LINQ to SQL 如何:使用用户定义的表值函数 (LINQ to SQL) 示例 请参见 发送反馈意见 表值函数返回单个行集(与存储过程不同,存储过程可返回多个结果形状)。由于表值函数的返回类型为 Table,因此在 SQL 中可以使用表的任何地方均可以使用表值函数。此外,您 还可以完全像处理表那样来处理表值函数。 示例 请参见 下面的 SQL 函数显式声明其返回一个 TABLE。因此,隐式定义了所返回的行集结构。 LINQ to SQL 按如下方式映射此函数: 下面的 SQL 代码说明您可以对此函数返回的表执行联接,以及像处理任何其他表一样处理它: 在 LINQ to SQL 中,此查询的呈现结果如下: 复制代码 CREATE FUNCTION ProductsCostingMoreThan(@cost money) RETURNS TABLE AS RETURN SELECT ProductID, UnitPrice FROM Products WHERE UnitPrice > @cost C# 复制代码 [Function(Name="dbo.ProductsCostingMoreThan", IsComposable=true)] public IQueryable ProductsCostingMoreThan([Parameter(DbType="Money")] System.Nullable cost) { return this.CreateMethodCallQuery(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), cost); } 复制代码 SELECT p2.ProductName, p1.UnitPrice FROM dbo.ProductsCostingMoreThan(80.50) AS p1 INNER JOIN Products AS p2 ON p1.ProductID = p2.ProductID C# 复制代码 var q = from p in db.ProductsCostingMoreThan(80.50m) join s in db.Products on p.ProductID equals s.ProductID select new { p.ProductID, s.UnitPrice }; 概念 用户定义的函数 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/5a4ae2b4-3290-4aa... 全部折叠 代码:C# LINQ to SQL 如何:内联调用用户定义的函数 (LINQ to SQL) 示例 请参见 发送反馈意见 尽管您可以内联调用用户定义的函数,但延迟执行的查询中包含的函数直到此查询执行时才会执行。有关更多信 息,请参见 LINQ 查询简介。 当您在查询外部调用同一函数时,LINQ to SQL 会用方法调用表达式创建一个简单查询。下面是相应的 SQL 语法 (@p0 参数绑定到传入的常量): LINQ to SQL 会创建如下内容: 示例 请参见 复制代码 SELECT dbo.ReverseCustName(@p0) C# 复制代码 string str = db.ReverseCustName("LINQ to SQL"); 在下面的 LINQ to SQL 查询中,您可以看到对生成的用户定义函数方法 ReverseCustName 的内联调用。此函 数不会立即执行,这是因为查询会延迟执行。为此查询生成的 SQL 会转换成对数据库中用户定义函数的调用 (请参见此查询后面的 SQL 代码)。 C# 复制代码 var custQuery = from cust in db.Customers select new {cust.ContactName, Title = db.ReverseCustName(cust.ContactTitle)}; 复制代码 SELECT [t0].[ContactName], dbo.ReverseCustName([t0].[ContactTitle]) AS [Title] FROM [Customers] AS [t0] 概念 用户定义的函数 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/f80d4327-b6a5-4aa... 全部折叠 代码:C# LINQ to SQL 参考 (LINQ to SQL) 发送反馈意见 本节提供针对 LINQ to SQL 开发人员的参考信息。 我们还建议您在 MSDN Library 中搜索特定问题,尤其是参与 LINQ Forum(LINQ 论坛),在这里您可以与专家们 详细讨论更复杂的主题。此外,您还可以学习详细介绍 LINQ to SQL 技术并包含 Visual Basic 和 C# 代码示例的白 皮书。有关更多信息,请参见 LINQ to SQL: .NET Language-Integrated Query for Relational Data(LINQ to SQL:关 系数据的 .NET 语言集成查询)。 本节内容 相关章节 数据类型和函数 (LINQ to SQL) 介绍为何公共语言运行时 (CLR) 构造只在 LINQ to SQL 已在转换引擎中显式提供转换时才具有 SQL 形式的 对应表达式。 基于属性的映射 (LINQ to SQL) 介绍用来将 LINQ to SQL 对象模型映射到 SQL Server 数据库的基于 LINQ to SQL 属性的方法。 LINQ to SQL 中的代码生成 介绍 LINQ to SQL 如何从数据库中获取元信息,然后生成代码文件。 外部映射引用 (LINQ to SQL) 介绍用来将 LINQ to SQL 对象模型映射到 SQL Server 数据库的 LINQ to SQL 外部映射方法。提供用于映射 文件的 XSD 架构定义。 常见问题 (LINQ to SQL) 提供有关 LINQ to SQL 的常见问题解答。 SQL Server Compact 3.5 与 LINQ to SQL 介绍 SQL Server Compact 3.5 与 SQL Server 在 LINQ to SQL 应用程序方面有何不同。 标准查询运算符转换 (LINQ to SQL) 介绍 LINQ to SQL 如何将标准查询运算符转换为 SQL 命令。 LINQ to SQL 提供访问 LINQ to SQL 主题的门户。 语言集成查询 (LINQ) 提供访问 LINQ 主题的门户。 LinqDataSource 技术概述 介绍 LinqDataSource 控件如何通过 ASP.NET 数据源控件架构向 Web 开发人员公开 LINQ。 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/312c3935-a947-42... 全部折叠 代码:C# LINQ to SQL 数据类型和函数 (LINQ to SQL) 请参见 发送反馈意见 公共语言运行库 (CLR) 构造只在 LINQ to SQL 已在转换引擎中显式提供转换时才具有 SQL 形式的对应表达式。换 言之,对于下表所列主题中未说明支持的任何 .NET Framework 类功能,均不支持转换到 SQL。此限制还适用于用 户定义的方法、属性和强制转换。 但是,如果满足以下任一条件,您就可以在查询中使用此类不受支持的方法: z 在进行转换前,相应方法的计算结果可以为可转换值。 换言之,此方法不能依赖于直到查询执行时才绑定的任何 lambda 变量。 z 在从数据库中检索到相应方法后可以将其应用于结果。 换言之,在应用此方法后无法提出检索数据库信息的其他请求。 请参见 主题 说明 SQL-CLR 类型映射 (LINQ to SQL) 提供 CLR 类型和 SQL Server 类型之间的详细映射矩阵。 基本数据类型 (LINQ to SQL) 指向有关有效的内置 SQL Server 2005 转换的 SQL Server 文档。 Boolean 数据类型 (LINQ to SQL) 介绍与预期的可能情况不同的异常。 Null 语义 (LINQ to SQL) 提供指向各种讨论 null 和可以为 null 问题的 LINQ to SQL 主题的链 接。 数值运算符和比较运算符 (LINQ to SQL) 介绍与预期的可能情况不同的异常。 序列运算符 (LINQ to SQL) 介绍 LINQ to SQL 不支持的序列运算符,提供一些示例,并总结与 .NET Framework 的差异。 System.Convert 方法 (LINQ to SQL) 列出 LINQ to SQL 不支持的方法。 System.DateTime 方法 (LINQ to SQL) 列出 LINQ to SQL 不支持的方法,并总结与 .NET Framework 的差异。 System.Math 方法 (LINQ to SQL) 列出 LINQ to SQL 不支持的方法,并总结与 .NET Framework 的差异。 System.Object 方法 (LINQ to SQL) 列出 LINQ to SQL 不支持的方法,并总结与 .NET Framework 的差异。 System.String 方法 (LINQ to SQL) 列出 LINQ to SQL 不支持的方法,并总结与 .NET Framework 的差异。 System.TimeSpan 方法 (LINQ to SQL) 介绍有关使用 TimeSpan 的方法的限制。 不支持的功能 (LINQ to SQL) 介绍 LINQ to SQL 不支持的功能。 概念 类型系统不匹配 (LINQ to SQL) 参考 (LINQ to SQL) Visual Studio 中的 .NET Framework 类库 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/683413c5-0312-4e... 全部折叠 代码:C# LINQ to SQL SQL-CLR 类型映射 (LINQ to SQL) 请参见 发送反馈意见 类型映射就是在对象的字段或属性的公共语言运行库 (CLR) 类型与表字段的 SQL Server 类型之间进行配对。 本主题提供了完整的映射矩阵以及有关以下内容的一些特定信息: z Enum、DateTime 和 XML 映射。 z SQL Server 货币类型与 CLR 之间的转换。 z 浮点型。 z 二进制序列化和字符串序列化。 类型映射运行时行为矩阵 以下关系图显示了顶部 CLR 类型与左侧 SQL Server 类型之间的详细映射矩阵。每个单元格表示从具有对应 CLR 类型的字段或属性中检索到 SQL 类型的数据或者将 SQL 类型的数据保存到这样的字段或属性中时预期的 运行时行为。LINQ to SQL 不支持映射到此矩阵中未指定的任何 CLR 或 SQL 数据类型(除非通过字符串序列 化或二进制序列化实现)。 注意: 边框为红色的单元格显示了每列的 CLR 类型的默认 SQL 类型映射。当您使用 CreateDatabase 生成 SQL Server 数据库时,会使用这些默认映射。 页码,1/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/4ed76327-54a7-41... 枚举映射 DateTime 映射 XML 类型映射 LINQ to SQL 支持使用如下两种方式对 Enum 类型进行映射: z 映射到 SQL 数值类型(TINYINT、SMALLINT、INT、BIGINT) 将 CLR Enum 类型映射到 SQL 数值类型时,此 CLR Enum 的基础整数值会映射到 SQL 数据库字段的 值。SQL 中的字段值是直接作为 Enum 类型的基础整数值检索的。当您更改 Enum 值并且将数据保存到 数据库中后,Enum 的基础整数值会存储到数据库字段中。 z 映射到 SQL 文本类型(CHAR、NCHAR、VARCHAR、NVARCHAR) 将 CLR Enum 类型映射到 SQL 文本类型时,SQL 数据库值会映射到 CLR Enum 成员的名称。例如,如果 一个名为 DaysOfWeek 的 Enum 包含一个名为 Tue 的成员,则此成员将映射到 Tue 的数据库值。您可 以通过对 Enum 类型使用反射来完成此映射。 CLR Enum 类型的默认 SQL 映射是其基础整型的 SQL 等效项。 不论原始 DateTimeKind 信息如何,DateTime 值均不进行 TimeZone 转换,而是按原样保存到数据库中。从 数据库中检索到 DateTime 值时,它们的值按原样加载到 DateTimeKind 为 Unspecified 的 DateTime 中。有 关更多信息,请参见 System.DateTime 方法 (LINQ to SQL)。 页码,2/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/4ed76327-54a7-41... Decimal 和 Money 类型 浮点型 二进制序列化和字符串序列化 您可以将 SQL Server 2005 XML 数据类型映射到 XDocument(默认)、XElement 或 String。如果列中存储 了无法读入 XDocument 或 XElement 中的 XML 片段,则此列必须映射到 String 以免出现运行时错误。必须 映射到 String 的 XML 片段的示例包括: z XML 元素的序列。 z 属性、PI、注释。 SQL Server DECIMAL/MONEY/SMALLMONEY 类型与 CLR Decimal/Double 类型有如下差异: SQL Server z DECIMAL(precision,scale) z 最高 38 位精度。 z 范围(小数点左侧所有位):-1038 + 1 到 1038 – 1。 z 可表示所有可能的 0 到 38 位数字。 z MONEY z 最高 18 到 19 位精度,但小数点右侧始终为 4 位。 z 范围:-263/1000 到 (263 – 1)/1000。 z 可表示所有可能的 0 到 18 位数字,以及部分(而不是全部)19 位数字。 z SMALLMONEY z 最高 5 到 6 位精度,但小数点右侧始终为 4 位。 z 范围:-231/1000 到 (231 – 1)/1000。 z 可表示所有可能的 0 到 5 位数字,以及部分(而不是全部)6 位数字。 CLR z Decimal z 最高 28 到 29 位精度。 z 范围(小数点左侧所有位):-296 + 1 到 296 – 1。 z 可表示所有可能的 0 到 28 位数字,以及部分(而不是全部)29 位数字。 z Double z 范围:±4.94065645841246544E-324 到 1.79769313486231570E+308。 z 与 Decimal 相比,支持的量级要大得多,但精度更小。 z 所有十进制值都可以转换为 Double 而不会发生溢出,但精度可能会损失。 SQL Server 支持指定为 FLOAT(mantissaBits) 的可变大小的浮点型。CLR Single 等效于 REAL(FLOAT (24) 的同义词)。CLR Double 等效于 FLOAT(默认为 FLOAT(53))。LINQ to SQL 将 FLOAT(24) 或精度 更低的 FLOAT 映射到 Single,将精度更高的浮点型映射到 Double。 注意: 从技术上而言,SQL Server 可以在查询中存储 NaN、正/负无穷以及正/负零。这些值的行为并不总是像预 期的那样,甚至在直接的 SQL 查询中也是如此。 LINQ to SQL 支持两种类型的 .NET Framework 类和用户类序列化。 z 字符串序列化 (.Parse()) 如果一个类实现了 .Parse()(例如,带有类似于 DateTime 的签名),则可以将它序列化到任何 SQL 文本字段(CHAR、NCHAR、VARCHAR、NVARCHAR、TEXT、NTEXT、XML)。如果您将对象序列化 成字符串,则从 ToString() 返回的值将保存到数据库中。如果您对存储的字符串进行反序列化,则会对 该字符串调用 .Parse(),以返回所构造的对象。 z 二进制序列化 (ISerializable) 如果一个类实现了 ISerializable,则可以将它序列化到任何 SQL 二进制字段(BINARY、VARBINARY、 IMAGE)。将按照 ISerializable 的标准行为来对对象进行序列化和反序列化。 z IXmlSerializable LINQ to SQL 不支持使用 IXmlSerializable 进行序列化。 页码,3/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/4ed76327-54a7-41... 请参见 概念 数据类型和函数 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,4/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/4ed76327-54a7-41... 全部折叠 代码:C# LINQ to SQL 基本数据类型 (LINQ to SQL) 请参见 发送反馈意见 在 SQL Server 主题 CAST 和 CONVERT (Transact-SQL) 中,可以找到有效的内置 SQL Server 2005 转换矩阵。 LINQ to SQL 支持以下运算符。 z Visual Basic = 和 <>(比较运算符 (Visual Basic));C# 比较运算符 ==(== 运算符(C# 参考))和 !=(! = 运算符(C# 参考))。支持对数值、Boolean、DateTime 和 TimeSpan 类型进行相等和不相等运算。 z Is 运算符 在使用继承映射时,Is (Visual Basic)/is (C#) 运算符具有受支持的转换形式。可以使用该运算符,而不用通过 直接测试鉴别器列来确定对象是否属于特定实体类型,该运算符会转换为对鉴别器列的检查。有关更多信息, 请参见 Is 运算符 (Visual Basic) 和 is(C# 参考)。 z 强制转换 支持从源 CLR 类型到目标 CLR 类型的隐式或显示强制转换,前提是在 SQL Server 中存在类似的有效转换。有 关更多信息,请参见 CType 函数 (Visual Basic) 和 as(C# 参考)。转换后,强制转换会更改对 CLR 表达式执 行的操作的行为,使之与自然映射到目标类型的其他 CLR 表达式的行为匹配。强制转换还可以在继承映射的上 下文中进行。可以将对象强制转换成更具体的实体子类型,以便可以访问其特定于子类型的数据。 请参见 概念 数据类型和函数 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/eca2c472-9548-48... 全部折叠 代码:C# LINQ to SQL Boolean 数据类型 (LINQ to SQL) 请参见 发送反馈意见 布尔运算符在公共语言运行库 (CLR) 中按预期方式工作,但其短路行为不会进行转换。例如,Visual Basic AndAlso 运算符在行为上类似于 And 运算符。C# && 运算符在行为上类似于 & 运算符。 LINQ to SQL 支持以下运算符。 请参见 Visual Basic C# And 运算符 (Visual Basic) & 运算符(C# 参考) AndAlso 运算符 && 运算符(C# 参考) Or 运算符 (Visual Basic) | 运算符(C# 参考) OrElse 运算符 || 运算符(C# 参考) Xor 运算符 (Visual Basic) ^ 运算符(C# 参考) Not 运算符 (Visual Basic) ! 运算符(C# 参考) 概念 数据类型和函数 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/57f7376b-4b11-4b... 全部折叠 代码:C# LINQ to SQL Null 语义 (LINQ to SQL) 请参见 发送反馈意见 下表提供了指向 LINQ to SQL 文档中讨论 null(在 Visual Basic 中为 Nothing)问题的各个部分的链接。 请参见 主题 说明 类型系统不匹配 (LINQ to SQL) 本主题的“Null 语义”部分包括以下方面的讨论:三态 SQL Boolean 与两态公 共语言运行库 (CLR) Boolean 的比较、文本 Nothing (Visual Basic) 与 null (C#) 以及其他类似问题。 标准查询运算符转换 (LINQ to SQL) 本主题的“Null 语义”部分介绍 LINQ to SQL 中的 null 比较语义。 System.String 方法 (LINQ to SQL) 本主题的“与 .NET 的差异”部分说明从 LastIndexOf 返回 0 为何意味着字符 串为 null 或找到的位置为 0。 如何:计算数值序列中的 值之和 (LINQ to SQL) 说明使用 Sum 运算符计算只包含 null 的序列或空序列时,所得结果为何为 null(在 Visual Basic 中为 Nothing)。 概念 数据类型和函数 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/a97017ae-d634-4cf... 全部折叠 代码:C# LINQ to SQL 数值运算符和比较运算符 (LINQ to SQL) 请参见 发送反馈意见 算术运算符和比较运算符在公共语言运行库 (CLR) 中按预期方式工作,但有以下几点例外: z SQL 不支持对浮点数字使用取模运算符。 z SQL 不支持未校验的算法。 z 在无法复制到 SQL 中的表达式中使用增量和减量运算符时会产生副作用,因而不支持此类运算符。 支持的运算符 请参见 LINQ to SQL 支持以下运算符。 z 基本算术运算符: z + z -(减号) z * z / z Visual Basic 整除 (\) z % (Visual Basic Mod) z << z >> z -(一元求反) z 基本比较运算符: z Visual Basic = 和 C# == z Visual Basic <> 和 C# != z Visual Basic Is/IsNot z < z <= z > z >= 概念 数据类型和函数 (LINQ to SQL) C# 运算符 运算符 (Visual Basic) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/25b4a26a-06f2-4f8... 全部折叠 代码:C# LINQ to SQL 序列运算符 (LINQ to SQL) 请参见 发送反馈意见 一般而言,LINQ to SQL 不支持具有以下一种或多种性质的序列运算符。 z 采用带有索引的 lambda 作为参数。 z 依赖于顺序行的属性,如 TakeWhile。 z 依赖于任意 CLR 实现,如 IComparer(T)。 与 .NET 的差异 请参见 不受支持的序列运算符示例 Enumerable.Where(TSource)(IEnumerable(TSource), Func(TSource, Int32, Boolean)) Enumerable.Select(TSource, TResult)(IEnumerable(TSource), Func(TSource, TResult)) Enumerable.Select(TSource, TResult)(IEnumerable(TSource), Func(TSource, TResult)) Enumerable.TakeWhile(TSource)(IEnumerable(TSource), Func(TSource, Boolean)) Enumerable.TakeWhile(TSource)(IEnumerable(TSource), Func(TSource, Int32, Boolean)) Enumerable.SkipWhile(TSource)(IEnumerable(TSource), Func(TSource, Boolean)) Enumerable.SkipWhile(TSource)(IEnumerable(TSource), Func(TSource, Int32, Boolean)) Enumerable.GroupBy(TSource, TKey, TElement)(IEnumerable(TSource), Func(TSource, TKey), Func(TSource, TElement), IEqualityComparer(TKey)) Enumerable.GroupBy(TSource, TKey, TElement, TResult)(IEnumerable(TSource), Func(TSource, TKey), Func(TSource, TElement), Func(TKey, IEnumerable(TElement), TResult), IEqualityComparer(TKey)) Enumerable.Reverse(TSource)(IEnumerable(TSource)) Enumerable.DefaultIfEmpty(TSource)(IEnumerable(TSource), TSource) Enumerable.ElementAt(TSource)(IEnumerable(TSource), Int32) Enumerable.ElementAtOrDefault(TSource)(IEnumerable(TSource), Int32) Enumerable.Range(Int32, Int32) Enumerable.Repeat(TResult)(TResult, Int32) Enumerable.Empty(TResult)() Enumerable.Contains(TSource)(IEnumerable(TSource), TSource) Enumerable.Aggregate(TSource)(IEnumerable(TSource), Func(TSource, TSource, TSource)) Enumerable.Aggregate(TSource, TAccumulate)(IEnumerable(TSource), TAccumulate, Func(TAccumulate, TSource, TAccumulate)) Enumerable.Aggregate(TSource, TAccumulate, TResult)(IEnumerable(TSource), TAccumulate, Func(TAccumulate, TSource, TAccumulate), Func(TAccumulate, TResult)) Enumerable.SequenceEqual 除 Average 外,所有受支持的序列运算符在公共语言运行库 (CLR) 中均按预期方式工作。Average 返回的值的类 型与执行求平均值运算的类型相同,而在 CLR 中,Average 则始终返回 Double 或 Decimal。如果源参数被显式强 制转换为 double/decimal,或者选择器强制转换为 double/decimal,则产生的 SQL 也将进行这种转换,因而结果将 同预期的一样。 概念 数据类型和函数 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/4d332d32-3806-44... 全部折叠 代码:C# LINQ to SQL System.Convert 方法 (LINQ to SQL) 请参见 发送反馈意见 LINQ to SQL 不支持以下 Convert 方法。 z 带有 IFormatProvider 参数的版本。 z 涉及字符数组或字节数组的方法: z FromBase64CharArray z ToBase64CharArray z FromBase64String z ToBase64String z 以下方法: z public static To( value);,其中 Type1 和 Type2 各自是 sbyte、uint、ulong 或 ushort 之一。 z C#: int To(string value, int fromBase), ToString(... value, int toBase) z Visual Basic: Function To(Of [Numeric])(value as String, fromBase As Integer) As [Numeric], ToString( value As …, toBase As Integer) z IsDBNull z GetTypeCode z ChangeType 请参见 概念 数据类型和函数 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/3ca6c5b6-ea5d-4ab... 全部折叠 代码:C# LINQ to SQL System.DateTime 方法 (LINQ to SQL) 请参见 发送反馈意见 LINQ to SQL 不支持以下 DateTime 方法。 与 .NET 的差异 System.DateTime 减法部分模式 请参见 IsDaylightSavingTime IsLeapYear DaysInMonth ToBinary ToFileTime ToFileTimeUtc ToLongDateString ToLongTimeString ToOADate ToShortDateString ToShortTimeString ToUniversalTime FromBinary UtcNow FromFileTime FromFileTimeUtc FromOADate GetDateTimeFormats SQL Server 和公共语言运行库 (CLR) 的 DateTime 类型在范围和计时周期精度上存在差异,如下表中所述。 z CLR DateTime 类型与 SQL Server 类型相比,前者范围更大、精度更高。因此来自 SQL Server 的数据用 CLR 类型表示时,绝不会损失量值或精度。但如果反过来的话,则范围可能会减小,精度可能会降低。 z SQL Server 日期不存在 TimeZone 概念,而它则是在 CLR 中得到充分支持的一个功能。 用户必须决定如何将数据存储在其数据库中(以当地时间、UTC 或固定时间),并在 LINQ to SQL 查询前后 自己执行必要的转换。 类型 最小值 最大值 计时周期 System.DateTime 0001 年 1 月 1 日 9999 年 12 月 31 日 100 毫微秒(0.0000001 秒) T-SQL DateTime 1753 年 1 月 1 日 9999 年 12 月 31 日 3.33… 毫秒 (0.0033333 秒) T-SQL SmallDateTime 1900 年 1 月 1 日 2079 年 6 月 6 日 1 分钟(60 秒) 例如,考虑如下模式。 (dateTime1 – dateTime2).{Days, Hours, Milliseconds, Minutes, Months, Seconds, Years} 识别它时,会将它转换成对 DATEDIFF 的直接调用,如下所示: DATEDIFF({DatePart}, @dateTime1, @dateTime2) 概念 数据类型和函数 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/4f80700c-e83f-4ab... 全部折叠 代码:C# LINQ to SQL System.Math 方法 (LINQ to SQL) 请参见 发送反馈意见 LINQ to SQL 不支持以下 Math 方法。 z Math.DivRem(Int32, Int32, Int32) z Math.DivRem(Int64, Int64, Int64) z Math.IEEERemainder(Double, Double) 与 .NET 的差异 请参见 .NET Framework 在舍入语义方面与 SQL Server 不同。.NET Framework 中的 Round 方法执行的是“四舍六入 五成双”,其中以 .5 结尾的数字舍入为最近的偶数位,而非向前进一位。例如,2.5 舍入为 2,而 3.5 则舍 入为 4。(此方法有助于避免大型数据事务中趋向较高值的系统性偏差。) 而在 SQL 中,ROUND 函数则始终向远离 0 的方向舍入。因此 2.5 舍入为 3,相比之下,它在 .NET Framework 中则舍入为 2。 LINQ to SQL 遵照 SQL ROUND 语义,因而不会尝试执行“四舍六入五成双”。 概念 数据类型和函数 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/0f299521-6f41-472... 全部折叠 代码:C# LINQ to SQL System.Object 方法 (LINQ to SQL) 请参见 发送反馈意见 LINQ to SQL 支持以下 Object 方法。 LINQ to SQL 不支持以下 Object 方法。 与 .NET 的差异 请参见 Object.Equals(Object) Object.Equals(Object, Object) Object.ToString() Object.GetHashCode() Object.ReferenceEquals(Object, Object) Object.MemberwiseClone() Object.GetType() 用于二进制类型(如 BINARY、VARBINARY、IMAGE 和 TIMESTAMP)的 Object.ToString()。 用于双精度类型的 Object.ToString() 的输出对 SQL 使用 SQL CONVERT(NVARCHAR(30), @x, 2)。在这种情 况下 SQL 始终使用 16 位科学记数法(例如,用“0.000000000000000e+000”表示 0)。因此, Object.ToString() 转换与 .NET Framework 中的 Convert.ToString 产生的字符串不同。 概念 数据类型和函数 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/5397fca0-689e-443... 全部折叠 代码:C# LINQ to SQL System.String 方法 (LINQ to SQL) 请参见 发送反馈意见 LINQ to SQL 不支持以下 String 方法。 一般情况下不支持的 System.String 方法 不支持的 System.String 静态方法 不支持的 System.String 非静态方法 与 .NET 的差异 一般情况下不支持的 String 方法: z 区分区域性的重载(带有 CultureInfo/StringComparison/IFormatProvider 的方法)。 z 带有或生成 char 数组的方法。 不支持的 System.String 静态方法 String.Copy(String) String.Compare(String, String, Boolean) String.Compare(String, String, Boolean, CultureInfo) String.Compare(String, Int32, String, Int32, Int32) String.Compare(String, Int32, String, Int32, Int32, Boolean) String.Compare(String, Int32, String, Int32, Int32, Boolean, CultureInfo) String.CompareOrdinal(String, String) String.CompareOrdinal(String, Int32, String, Int32, Int32) String.Format String.Join 不支持的 System.String 非静态方法 String.IndexOfAny(Char[]) String.Split String.ToCharArray() String.ToUpper(CultureInfo) String.TrimEnd(Char[]) String.TrimStart(Char[]) z 查询不考虑可能在服务器上生效的 SQL Server 排序规则,因而默认情况下将提供区分区域性和大小写的 比较。此行为不同于 .NET Framework 默认的区分大小写的语义。 z 当 LastIndexOf 返回 0 时,则说明相应的字符串为 NULL 或找到的位置为 0。 z 对长度固定的字符串(CHAR、NCHAR)执行串联或其他运算时,可能会返回意外结果,原因是这些类型 会自动在数据库中应用空白。 z 由于许多方法(如 Replace、ToLower、ToUpper)和字符索引器未实现对 TEXT 或 NTEXT 列以及 XML 的有效转换,因此,如果对它们进行正常转换,则会发生 SqlExceptions。对这些类型而言,这种 行为被视为可接受。但是,所有字符串运算都必须符合 VARCHAR、NVARCHAR、VARCHAR(max) 和 NVARCHAR(max) 的公共语言运行库 (CLR) 语义。 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/ce307f14-87e6-481... 请参见 概念 数据类型和函数 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/ce307f14-87e6-481... 全部折叠 代码:C# LINQ to SQL System.TimeSpan 方法 (LINQ to SQL) 请参见 发送反馈意见 在 LINQ to SQL 中,您无法将数据库字段映射到 TimeSpan。但是,支持对 TimeSpan 的操作,原因是可以通过 DateTime 减法运算返回 TimeSpan 值或将这些值作为文本或绑定变量引入表达式。 请参见 概念 数据类型和函数 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/9333fee8-1454-437... 全部折叠 代码:C# LINQ to SQL 不支持的功能 (LINQ to SQL) 请参见 发送反馈意见 无法通过转换现有的公共语言运行库 (CLR) 和 .NET Framework 构造公开以下 SQL 功能: z LIKE z STDDEV 支持以下功能,但这些功能存在差异: z DATEDIFF 有关更多信息,请参见 System.DateTime 方法 (LINQ to SQL)。 z ROUND 有关更多信息,请参见 System.Math 方法 (LINQ to SQL)。 请参见 概念 数据类型和函数 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/e480cfb5-697e-42c... 全部折叠 代码:C# LINQ to SQL 基于属性的映射 (LINQ to SQL) 请参见 发送反馈意见 LINQ to SQL 通过应用属性或通过使用外部映射文件将 SQL Server 数据库映射到 LINQ to SQL 对象模型。本主题概 述了基于属性的方法。 LINQ to SQL 最基本的映射形式是将数据库映射到 DataContext,将表映射到类,将列和关系映射到这些类的属性。 您也可以使用属性来将继承层次结构映射到对象模型中。有关更多信息,请参见如何:用 Visual Basic 或 C# 生成 对象模型 (LINQ to SQL)。 使用 Visual Studio 的开发人员通常使用对象关系设计器执行基于属性的映射。有关更多信息,请参见Object Relational Designer (O/R Designer) 和对象关系设计器(O/R 设计器).您还可以使用 SQLMetal 命令行工具,或自行 手动编写这些属性的代码。有关更多信息,请参见如何:用 Visual Basic 或 C# 生成对象模型 (LINQ to SQL)。 以下各节更加详细地介绍了基于属性的映射。有关更多信息,请参见 System.Data.Linq.Mapping 命名空间。 DatabaseAttribute 属性 TableAttribute 属性 ColumnAttribute 属性 注意: 您还可以通过使用外部 XML 文件进行映射。有关更多信息,请参见外部映射引用 (LINQ to SQL)。 使用此属性可指定在连接未提供名称时数据库的默认名称。此属性 (Attribute) 是可选的,但如果使用它,则 必须按照下表中的说明应用 Name 属性 (Property)。 有关更多信息,请参见 DatabaseAttribute。 属性 类型 默认值 说明 Name 字符串 请参见 Name 与其 Name 属性一起使用,用于指定数据库的名称。 使用此属性可将类指定为与数据库表或视图关联的实体类。LINQ to SQL 将具有此属性的类视为持久性类。下 表介绍了 Name 属性。 有关更多信息,请参见 TableAttribute。 属性 类型 默认值 说明 Name 字符串 与类名相同的字符串 将类指定为与数据库表关联的实体类。 使用此属性可指定实体类的某个成员表示数据库表中的列。您可以将此属性 (Attribute) 应用于任何字段或属 性 (Property)。 当 LINQ to SQL 保存对数据库所做的更改时,只会检索并持久保存您标识为列的那些成员。不具有此属性的 成员被假定为非持久的,且不会被提交以进行插入或更新。 下表介绍了此属性 (Attribute) 的属性 (Property)。 属性 类型 默认值 说明 AutoSync AutoSync Never 指示公共语言运行库 (CLR) 在执行 插入或更新操作后检索值。 可供选择的值:Always、Never、 OnUpdate、OnInsert。 CanBeNull 布尔值 true 指示列可以包含 null 值。 DbType 字符串 推断出的数据库列类型 使用数据库类型和修饰符来指定数 据库列的类型。 Expression 字符串 空 定义数据库中计算所得的列。 页码,1/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/6dd89999-f415-4d... AssociationAttribute 属性 InheritanceMappingAttribute 属性 FunctionAttribute 属性 有关更多信息,请参见 ColumnAttribute。 IsDbGenerated 布尔值 false 指示列包含数据库自动生成的值。 IsDiscriminator 布尔值 false 指示列包含 LINQ to SQL 继承层次 结构的鉴别器值。 IsPrimaryKey 布尔值 false 指定此类成员表示作为表主键或表 主键一部分的列。 IsVersion 布尔值 false 将成员的列类型标识为数据库时间 戳或版本号。 UpdateCheck UpdateCheck 除非对某个成员而言 IsVersion 为 true,否则为 Always 指定 LINQ to SQL 如何实现开放式 并发冲突的检测。 使用此属性 (Attribute) 可指定属性 (Property) 表示数据库中的关联,如外键对主键关系。有关关系的更多信 息,请参见 如何:映射数据库关系 (LINQ to SQL)。 下表介绍了此属性 (Attribute) 的属性 (Property)。 有关更多信息,请参见 AssociationAttribute。 属性 类型 默认值 说明 DeleteOnNull 布尔 值 false 当放置在其外键成员均不可以为 null 的关联上时,如果该关联设 置为 null,则删除对象。 DeleteRule 字符 串 无 向关联添加删除行为。 IsForeignKey 布尔 值 false 如果为 true,则将成员指定为表示数据库关系的关联中的外键。 IsUnique 布尔 值 false 如果为 true,则指示对外键的唯一性约束。 OtherKey 字符 串 相关类的 ID 将目标实体类的一个或多个成员指定为关联的另一端上的键值。 ThisKey 字符 串 包含类的 ID 指定此实体类的成员表示关联的此端上的键值。 使用此属性可映射继承层次结构。 下表介绍了此属性 (Attribute) 的属性 (Property)。 有关更多信息,请参见 InheritanceMappingAttribute。 属性 类型 默认值 说明 Code 字符 串 无。必须提供 值。 指定鉴别器的代码值。 IsDefault 布尔 值 false 如果为 true,则在存储区中没有与指定值中的任何一个值匹配的 鉴别器值时实例化此类型的对象。 Type 类型 无。必须提供 值。 指定层次结构中的类的类型。 使用此属性可指定方法表示数据库中的存储过程或用户定义函数。 页码,2/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/6dd89999-f415-4d... ParameterAttribute 属性 ResultTypeAttribute 属性 DataAttribute 属性 请参见 下表介绍了此属性 (Attribute) 的属性 (Property)。 有关更多信息,请参见 FunctionAttribute。 属性 类型 默认值 说明 IsComposable 布尔 值 false 如果为 false,则指示映射到存储过程。如果为 true,则指示映射到用户定义的函数。 Name 字符 串 与数据库中的名称相 同的字符串 指定存储过程或用户定义函数的名称。 使用此属性可映射存储过程方法中的输入参数。 下表介绍了此属性 (Attribute) 的属性 (Property)。 有关更多信息,请参见 ParameterAttribute。 属性 类型 默认值 说明 DbType 字符串 无 指定数据库类型。 Name 字符串 与数据库中的参数名相同的字符串 指定参数的名称。 使用此属性可指定结果类型。 下表介绍了此属性 (Attribute) 的属性 (Property)。 有关更多信息,请参见 ResultTypeAttribute。 属性 类型 默认值 说明 Type 类型 (无) 用于映射到返回 IMultipleResults 的存储过程的方法。为存储过程声明有效 的或预期的类型映射。 使用此属性可指定名称和私有存储字段。 下表介绍了此属性 (Attribute) 的属性 (Property)。 有关更多信息,请参见 DataAttribute。 属性 类型 默认值 说明 Name 字符串 与数据库中的名称相同 指定表、列等的名称。 Storage 字符串 公共访问器 指定基础存储字段的名称。 概念 参考 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,3/3(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/6dd89999-f415-4d... 全部折叠 代码:C# LINQ to SQL LINQ to SQL 中的代码生成 请参见 发送反馈意见 可以使用对象关系设计器或 SQLMetal 命令行工具生成表示数据库的代码。在任一情况下,端到端代码生成都分成三个阶段: 1. DBML 提取器从数据库中提取架构信息,然后将信息重新组合到 XML 格式的 DBML 文件中。 2. DBML 验证程序对 DBML 文件进行扫描,检查是否有错误。 3. 如果未显示验证错误,文件将传递到代码生成器。 有关更多信息,请参见代码生成工具 (SqlMetal.exe)。使用 Visual Studio 的开发人员还可以使用对象关系设计器生成代码。有关更多信息,请参见Object Relational Designer (O/R Designer) 和对象关系设计器(O/R 设计器). DBML 提取器 代码生成器 XML 架构定义文件 DBML 提取器是一个 LINQ to SQL 组件,它采用数据库元数据作为输入,生成 DBML 文件作为输出。下图显示了操作顺序。 代码生成器是一个 LINQ to SQL 组件,该组件将 DBML 文件转换为 Visual Basic、C# 或 XML 映射文件。下图显示了操作顺序。 DBML 文件必须对下面的 XSD 架构定义文件有效。 请将此架构定义文件与用于验证外部映射文件的架构定义文件区分开来。有关更多信息,请参见 外部映射引用 (LINQ to SQL)。 注意: Visual Studio 用户还会发现此 XSD 文件在“XML 架构”对话框中名为“DbmlSchema.xsd”。若要正确使用此 XSD 文件来验证 DBML 文件,请参见如何:验证 DBML 和外部映射文件 (LINQ to SQL)。 复制代码 ? 页码,1/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/ddcbdaa1-e7fa-4d8... DBML 文件示例 请参见 下面的代码摘自从 Northwind 示例数据库创建的 DBML 文件。可以使用 SQLMetal 通过 /xml 选项生成整个文件。有关更多信息,请参见代码生成工具 (SqlMetal.exe)。 复制代码
概念 背景信息 (LINQ to SQL) 外部映射引用 (LINQ to SQL) 如何:将对象模型生成为外部文件 (LINQ to SQL) 下载示例数据库 (LINQ to SQL) 参考 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,2/2(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/ddcbdaa1-e7fa-4d8... 全部折叠 代码:C# LINQ to SQL 外部映射引用 (LINQ to SQL) 请参见 发送反馈意见 LINQ to SQL 支持外部映射,借助于该过程,可以使用单独的 XML 文件指定数据库的数据模型与对象模型之间的映射。使用外部映射文件具有以下优点: z 可以将映射代码放在应用程序代码外部。此方法可以降低应用程序代码的混乱程度。 z 可以将外部映射文件视为类似于配置文件的某种东西。例如,在发布二进制文件后,只需交换出外部映射文件,就可以更新应用程序的工作方式。 要求 XML 架构定义文件 请参见 映射文件必须为 XML 文件,并且该文件必须能够通过 LINQ to SQL 架构定义 (.xsd) 文件的验证。 适用以下规则: z 映射文件必须为 XML 文件。 z XML 映射文件必须能够通过 XML 架构定义文件的验证。有关更多信息,请参见如何:验证 DBML 和外部映射文件 (LINQ to SQL)。 z 外部映射会重写基于属性的映射。换句话说,在使用外部映射源创建 DataContext 时,DataContext 会忽略已在类上创建的所有映射属性。无论类是否包含在外部映射文件中,都会发生这种情况。 z LINQ to SQL 不支持混合使用两种映射方式(基于属性的映射和外部映射)。 z 外部映射可以特定于数据库提供程序。通过将单独的外部映射用于不同的提供程序,可以映射同一个类。这是基于属性的映射所不支持的功能。 LINQ to SQL 中的外部映射必须能够通过以下 XML 架构定义的验证。 请将此架构定义文件与用于验证 DBML 文件的架构定义文件区分开来。有关更多信息,请参见 LINQ to SQL 中的代码生成。 注意: Visual Studio 用户还会发现此 XSD 文件在“XML 架构”对话框中名为“LinqToSqlMapping.xsd”。若要正确使用此文件来验证外部映射文件,请参见如何:验证 DBML 和外部映射文件 (LINQ to SQL)。 复制代码 ? 概念 LINQ to SQL 中的代码生成 参考 (LINQ to SQL) 如何:将对象模型生成为外部文件 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/076606b8-d889-4b... 全部折叠 代码:C# LINQ to SQL 常见问题 (LINQ to SQL) 请参见 发送反馈意见 以下各节解答了您在实现 LINQ 时可能遇到的一些常见问题。 其他问题在疑难解答 (LINQ to SQL) 中进行了解答。 无法连接 对数据库的更改丢失 数据库连接:可以打开多长时间? 在不进行查询的情况下更新 意外的查询结果 意外的存储过程结果 问:我无法连接到数据库。 答:请确保您的连接字符串是正确的,以及您的 SQL Server 实例正在运行。另请注意,LINQ to SQL 要求启 用命名管道协议。有关更多信息,请参见通过演练学习 (LINQ to SQL)。 问:我对数据库中的数据进行了更改,但是在重新运行应用程序时更改已丢失。 答:请确保您调用了 SubmitChanges 来将结果保存到数据库。 问:我的数据库连接可以保持打开状态多长时间? 答:一个连接通常会一直保持打开状态,直至您使用完查询结果为止。如果您需要花时间处理所有结果并且愿 意对结果进行缓存,请将 ToList(TSource) 应用于查询。在每个对象仅处理一次的常见方案中,流模型比 DataReader 和 LINQ to SQL 都更为适合。 连接使用的准确详细信息与以下内容有关: z 使用连接对象构造 DataContext 时的连接状态。 z 连接字符串设置(例如,启用多活动结果集 (MARS))。有关更多信息,请参见多活动结果集 (MARS)。 问:是否可以在不先查询数据库的情况下更新表数据? 答:虽然 LINQ to SQL 没有基于集的更新命令,但是可以使用以下方法之一进行更新而无需先进行查询: z 使用 ExecuteCommand 发送 SQL 代码。 z 创建对象的新实例,并初始化所有影响更新的当前值(字段)。然后使用 Attach 将对象附加到 DataContext 并修改您要更改的字段。 问:我的查询返回了意外的结果。如何检查所发生的情况? 答:LINQ to SQL 提供了几种工具用于检查其生成的 SQL 代码。其中最重要的工具就是 Log。有关更多信 息,请参见调试支持 (LINQ to SQL)。 问:我有一个存储过程,其返回值由 MAX() 进行计算。在将该存储过程拖动到 O/R 设计器图面时,返回值 不正确。 答:LINQ to SQL 提供了两种方法用于通过存储过程返回数据库生成的值: z 通过命名输出结果。 z 通过显式指定输出参数。 下面是错误输出的示例。因为 LINQ to SQL 无法映射结果,所以始终返回 0: create procedure proc2 as begin select max(i) from t where name like 'hello' 页码,1/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/252ed666-0679-4e... 序列化错误 多个 DBML 文件 避免在插入或更新时显式设置数据库生成的值 多个 DataLoadOptions 使用 SQL Compact 3.5 时的错误 继承关系中的错误 end 下面是使用输出参数获得正确输出的示例: create procedure proc2 @result int OUTPUT as select @result = MAX(i) from t where name like 'hello' go 下面是通过命名输出结果获得正确输出的示例: create procedure proc2 as begin select nax(i) AS MaxResult from t where name like 'hello' end 有关更多信息,请参见使用存储过程来自定义操作 (LINQ to SQL)。 问:在我试图进行序列化时收到下面的错误消息:“...类型 ‘System.Data.Linq.ChangeTracker+StandardChangeTracker’未标记为可序列化。” 答:LINQ to SQL 中的代码生成支持 DataContractSerializer 序列化,而不支持 XmlObjectSerializer 或 BinaryFormatter。有关更多信息,请参见序列化 (LINQ to SQL)。 问:如果我有多个 DBML 文件共享一些公用的表,我会收到一个编译器错误消息。 答:在对象关系设计器中将每个 DBML 文件对应的“上下文命名空间”和“实体命名空间”属性设置为不同 的值。此方法可以避免名称/命名空间冲突。 问:我的一个数据库表具有一个默认为 SQL Getdate() 的 DateCreated 列。在我试图使用 LINQ to SQL 插 入新记录时,该值会设置为 NULL。我希望其设置为数据库默认值。 答:LINQ to SQL 会自动为标识(自动增加)和 rowguidcol(数据库生成的 GUID)以及时间戳列处理这种情 况。在其他情况下,您应手动设置 IsDbGenerated=true 和 AutoSync=Always/OnInsert/OnUpdate 属性。 问:是否可以指定其他加载选项而不覆盖原先的选项? 答:可以。不会覆盖原先的选项,如下面的示例中所示: C# 复制代码 DataLoadOptions dlo = new DataLoadOptions(); dlo.LoadWith(o => o.Customer); dlo.LoadWith(o => o.OrderDetails); 问:在将表拖出 SQL Server Compact 3.5 数据库时收到错误消息。 答:虽然 SQL Server Compact 3.5 受 LINQ to SQL 运行时支持,但它在对象关系设计器中不受支持。在这种 情况下,必须创建您自己的实体类并添加合适的属性。 页码,2/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/252ed666-0679-4e... 提供程序模型 SQL 注入式攻击 更改 DBML 文件中的只读标志 APTCA 映射来自多个表的数据 连接池 第二个 DataContext 未更新 问:我使用对象关系设计器中的工具箱继承形状连接两个实体,但是收到错误消息。 答:仅创建关系是不够的。还必须提供其他信息,例如鉴别器列、基类鉴别器值和派生类鉴别器值。 问:是否有公共提供程序模型可用? 答:没有任何公共提供程序模型可用。目前,LINQ to SQL 仅支持 SQL Server 和 SQL Server Compact 3.5。 问:LINQ to SQL 如何防范 SQL 注入式攻击? 答:对于通过串联用户输入而组成的传统 SQL 查询,SQL 注入已成为重大风险。LINQ to SQL 通过在查询中 使用 SqlParameter 来避免这种注入。用户输入会转换为参数值。此方法可防止通过客户输入使用恶意命令。 问:如何在从 DBML 文件创建对象模型时消除某些属性中的 setter? 答:对于此高级方案,请执行以下步骤: 1. 在 .dbml 文件中,通过将 IsReadOnly 标志更改为 True 来修改属性。 2. 添加一个分部类。为只读成员创建一个带参数的构造函数。 3. 检查默认的 UpdateCheck 值 (Never) 以确定该值对于您的应用程序是否正确。 警告: 如果使用 Visual Studio 中的对象关系设计器,则可能会覆盖您的更改。 问:System.Data.Linq 是否标记为供部分受信任的代码使用? 答:是,System.Data.Linq.dll 程序集包括在那些使用 AllowPartiallyTrustedCallersAttribute 属性标记的 .NET Framework 程序集之中。如果没有此标记,则 .NET Framework 中的程序集将仅供完全受信任的代码使用。 在 LINQ to SQL 中,允许部分受信任的调用方使用的主要方案是允许从 Web 应用程序(其中的 trust 配置为 Medium)访问 LINQ to SQL 程序集。 有关更多信息,请参见ASP.NET Code Access Security 和ASP.NET 代码访问安全性. 问:我的实体中的数据来自多个表。如果映射这些数据? 答:您可以在数据库中创建一个视图并将实体映射到该视图。LINQ to SQL 会为视图生成 SQL,与它为表生成 SQL 相同。 注意: 这种情况下的视图用法有一些限制。当基础视图支持在 Table(TEntity) 上执行的操作时,此方法最为安 全。只有您知道要执行的操作。例如,大多数应用程序是只读的,还有相当多的应用程序仅通过对视图使 用存储过程来执行 Create/Update/Delete 操作。 问:是否具有对 DataContext 池有所帮助的构造? 答:请不要试图重用 DataContext 的实例。每个 DataContext 都会保持对应一个特定编辑/查询会话的状态 (包括标识缓存)。若要获取基于数据库当前状态的新实例,请使用新的 DataContext。 仍然可以使用基础 ADO.NET 连接池。有关更多信息,请参见 SQL Server 连接池 (ADO.NET)。 问:我使用 DataContext 的一个实例存储数据库中的值。但是,相同数据库上的另一个 DataContext 未反映 更新的值。第二个 DataContext 实例似乎返回缓存的值。 页码,3/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/252ed666-0679-4e... 无法在只读模式下调用 SubmitChanges 请参见 答:此行为是有意安排的。LINQ to SQL 会继续返回您在第一个实例中看到的相同实例/值。在进行更新时使用 开放式并发。原始数据用于检查当前数据库状态,以确定该数据实际上仍未更改。如果该数据已更改,则会发 生冲突,您的应用程序必须解决该冲突。您的应用程序可以选择将原始状态重置为当前数据库状态并尝试再次 更新。有关更多信息,请参见如何:管理更改冲突 (LINQ to SQL)。 您也可以将 ObjectTrackingEnabled 设置为 false,这样可以关闭缓存和更改跟踪。然后便可以在每次查询时 检索最新值。 问:当我试图在只读模式下调用 SubmitChanges 时收到错误消息。 答:只读模式关闭了上下文跟踪更改的功能。 概念 参考 (LINQ to SQL) 疑难解答 (LINQ to SQL) LINQ to SQL 中的安全性 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,4/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/252ed666-0679-4e... 全部折叠 代码:C# LINQ to SQL SQL Server Compact 3.5 与 LINQ to SQL 请参见 发送反馈意见 SQL Server Compact 3.5 是随 Visual Studio 2008 安装的默认数据库。有关更多信息,请参见Using SQL Server Compact 3.5 (Visual Studio) 和使用 SQL Server Compact 3.5 (Visual Studio). 本主题概括了在用法、配置、功能集和 LINQ to SQL 支持范围上的主要差异。 SQL Server Compact 3.5 相对于 LINQ to SQL 的特性 功能集 请参见 默认情况下,所有 Visual Studio 版本均安装了 SQL Server Compact 3.5,因此在开发计算机上它可以与 LINQ to SQL 一起使用。但使用 SQL Server Compact 3.5 和 LINQ to SQL 进行的应用程序部署与 SQL Server 应用程序的部署存在差异。SQL Server Compact 3.5 不是 .NET Framework 的一部分,因此必须与应用程序一 起打包或单独从 Microsoft 网站下载。 请注意下列特性: z SQL Server Compact 3.5 打包成可直接对数据库文件(扩展名为 .sdf)使用的 DLL。 z SQL Server Compact 3.5 与客户端应用程序在同一进程中运行。因此与 SQL Server Compact 3.5 通信的 效率要大大高于与 SQL Server 通信的效率。另一方面,SQL Server Compact 3.5 确实需要托管代码和非 托管代码之间互相交互,而这种交互也会耗费一定的开销。 z SQL Server Compact 3.5 DLL 的大小很小。这一特征减小了应用程序的总大小。 z LINQ to SQL 运行时和 SQLMetal 命令行工具支持 SQL Server Compact 3.5。 z 对象关系设计器不支持 SQL Server Compact 3.5。 SQL Server Compact 3.5 功能集与 SQL Server 的功能集相比要简单得多,具体体现在可影响 LINQ to SQL 应 用程序的以下方面: z SQL Server Compact 3.5 不支持存储过程或视图。 z SQL Server Compact 3.5 仅支持部分数据类型和 SQL 函数。 z SQL Server Compact 3.5 仅支持部分 SQL 构造。 z SQL Server Compact 3.5 仅提供具有最基本功能的优化器。有些查询可能会超时。 z SQL Server Compact 3.5 不支持部分信任。 概念 参考 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/59022359-a5a2-4c... 全部折叠 代码:C# LINQ to SQL 标准查询运算符转换 (LINQ to SQL) 请参见 发送反馈意见 LINQ to SQL 将标准查询运算符转换为 SQL 命令。数据库的查询处理器决定了 SQL 转换的执行语义。 标准查询运算符是针对序列定义的。序列是有序的并依赖于该序列每个元素的引用标识。有关更多信息,请参见标 准查询运算符概述。 SQL 主要处理无序值集。排序通常是显式声明的后续处理操作,应用于查询的最终结果而不是中间结果。标识由值 定义。因此,将 SQL 查询理解为处理多重集(包)而非集合。 以下各段介绍了标准查询运算符与其针对用于 LINQ to SQL 的 SQL Server 提供程序的 SQL 转换结果之间的差异。 运算符支持 Concat Concat(TSource) 方法是为有序多重集定义的,其中接收方的顺序与参数的顺序相同。Concat(TSource) 的作用 等效于对多重集执行 UNION ALL,紧接着再执行常见排序。 在产生结果前,最后一步是在 SQL 中排序。Concat(TSource) 不保留其参数的顺序。为确保顺序合适,您必须 显式对 Concat(TSource) 的结果进行排序。 Intersect、Except、Union Intersect 和 Except 方法仅对集合而言是定义完善的。针对多重集的语义尚未定义。 Union 方法是为多重集定义的,定义为多重集的无序串联(实际上是 SQL 中的 UNION ALL 子句的执行结 果)。 Take、Skip Take(TSource) 和 Skip(TSource) 方法仅对有序集而言是定义完善的。未定义针对无序集或多重集的语义。 由于 SQL 中的排序存在限制,因此 LINQ to SQL 会设法将这些方法的参数的排序操作移到相应方法的结果中 进行。例如,请考虑下面这个 LINQ to SQL 查询: 为此代码生成的 SQL 会将排序移到结尾,如下所示: 显而易见,当 Take(TSource) 和 Skip(TSource) 链接在一起时,所有指定的排序必须一致。否则,结果将是不 确定的。 对于非负的、基于标准查询运算符规范的整型常量参数,Take(TSource) 和 Skip(TSource) 都是定义完善的。 注意: Take(TSource) 和 Skip(TSource) 用在针对 SQL Server 2000 的查询中时存在一定的限制。有关更多信 息,请参见疑难解答 (LINQ to SQL) 中的“SQL Server 2000 中的 Skip 和 Take 异常”项。 C# 复制代码 var custQuery = (from cust in db.Customers where cust.City == "London" orderby cust.CustomerID select cust).Skip(1).Take(1); 复制代码 SELECT TOP 1 [t0].[CustomerID], [t0].[CompanyName], FROM [Customers] AS [t0] WHERE (NOT (EXISTS( SELECT NULL AS [EMPTY] FROM ( SELECT TOP 1 [t1].[CustomerID] FROM [Customers] AS [t1] WHERE [t1].[City] = @p0 ORDER BY [t1].[CustomerID] ) AS [t2] WHERE [t0].[CustomerID] = [t2].[CustomerID] ))) AND ([t0].[City] = @p1) ORDER BY [t0].[CustomerID] 页码,1/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/a60c30fa-1e68-45f... 表达式转换 不进行转换的运算符 LINQ to SQL 不对以下方法进行转换。最常见的原因是无序多重集与序列之间存在差异。 运算符 阐释 TakeWhile, SkipWhile SQL 查询是对多重集执行的,而不是对序列执行的。ORDER BY 必 须是最后一个应用于结果的子句。因此,不存在适用于这两个方法的 通用转换。 Reverse(TSource) 此方法的转换对于有序集而言是可行的,但目前 LINQ to SQL 未对它 进行转换。 Last, LastOrDefault 这些方法的转换对于有序集而言是可行的,但目前 LINQ to SQL 未对 它们进行转换。 ElementAt(TSource), ElementAtOrDefault(TSource) SQL 查询是对多重集执行的,而不是对可建立索引的序列执行的。 DefaultIfEmpty(带默认参数的重 载) 一般而言,无法为任意元组指定默认值。在某些情况下,可以通过外 部联接为元组指定 Null 值。 Null 语义 LINQ to SQL 不会将 null 比较语义施加在 SQL 上。比较运算符在语法上被转换为其 SQL 等效项。因此,该语 义反映了由服务器或连接设置定义的 SQL 语义。例如,在默认的 SQL Server 设置下,两个 null 值被视为不 相等,但您可以更改这些设置以更改语义。LINQ to SQL 在转换查询时不考虑服务器设置。 对文本型 null 的比较被转换为相应的 SQL 版本(is null 或 is not null)。 排序规则中的 null 值是由 SQL Server 定义的。LINQ to SQL 不会更改排序规则。 聚合 使用标准查询运算符的聚合方法 Sum 计算空序列或只包含 null 的序列时,所得结果为零。在 LINQ to SQL 中,SQL 的语义保持不变,并且使用 Sum 计算空序列或只包含 null 的序列时,所得结果为 null 而非零。 针对中间结果的 SQL 限制适用于 LINQ to SQL 中的聚合。32 位整型量的 Sum 不是使用 64 位结果计算的。 在 LINQ to SQL 转换 Sum 时,可能会发生溢出,即使对于内存中的对应序列,标准查询运算符的实现不会造 成溢出,仍存在这种可能性。 同样,使用经 LINQ to SQL 转换后的 Average 计算整数值时,所得结果的数据类型为 integer,而非 double。 实体参数 LINQ to SQL 允许在 GroupBy 和 OrderBy 方法中使用实体类型。在这些运算符的转换过程中,使用一种类型 的参数被视为等效于指定该类型的所有成员。例如,下面的代码是等效的: 可相等/可比较参数 在以下方法的实现中,要求参数相等: Contains Skip(TSource) Union Intersect Except LINQ to SQL 支持对平参数执行相等和比较运算,但对作为序列或包含序列的参数则不支持这两种运算。平参 数是一种能映射到 SQL 行的类型。可以静态方式确定不包含序列的一个或多个实体类型的投影被视为平参 数。 C# 复制代码 db.Customers.GroupBy(c => c); db.Customers.GroupBy(c => new { c.CustomerID, c.ContactName }); 页码,2/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/a60c30fa-1e68-45f... 继承支持 SQL Server 2005 支持 SQL Server 2000 支持 以下是平参数的一些示例: 以下是非平(分层)参数的一些示例。 Visual Basic 函数转换 Visual Basic 编译器使用的以下 Helper 函数转换为对应的 SQL 运算符和函数: CompareString DateTime.Compare Decimal.Compare IIf (in Microsoft.VisualBasic.Interaction) 转换方法: C# 复制代码 db.Customers.Select(c => c); db.Customers.Select(c => new { c.CustomerID, c.City }); db.Orders.Select(o => new { o.OrderID, o.Customer.City }); db.Orders.Select(o => new { o.OrderID, o.Customer }); C# 复制代码 // In the following line, c.Orders is a sequence. db.Customers.Select(c => new { c.CustomerID, c.Orders }); // In the following line, the result has a sequence. db.Customers.GroupBy(c => c.City); ToBoolean ToSByte ToByte ToChar ToCharArrayRankOne ToDate ToDecimal ToDouble ToInteger ToUInteger ToLong ToULong ToShort ToUShort ToSingle ToString 继承映射限制 有关更多信息,请参见如何:映射继承层次结构 (LINQ to SQL)。 查询中的继承 仅支持在投影中使用 C# 强制转换。在其他地方使用的强制转换不会进行转换且会被忽略。除 SQL 函数名以 外,SQL 确实仅执行公共语言运行库 (CLR) Convert 的等效项。也就是说,SQL 可以将一种类型的值更改为另 一类型。CLR 强制转换不存在等效项,这是因为不存在这样的概念:重新解释与另一类型的位相同的位。正因 如此,C# 强制转换只能在本地使用。它无法以远程方式使用。 运算符 is 和 as 以及 GetType 方法并不限于 Select 运算符。它们还可以用在其他查询运算符中。 LINQ to SQL 不支持以下 SQL Server 2005 功能: z 为 SQL CLR 编写的存储过程。 z 用户定义的类型。 z XML 查询功能。 以下 SQL Server 2000 局限性(与 Microsoft SQL Server 2005 相比)会影响 LINQ to SQL 支持。 Cross Apply 和 Outer Apply 运算符 这些运算符在 SQL Server 2000 中不可用。LINQ to SQL 设法通过一系列的重写来将它们替换为相应的联接。 页码,3/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/a60c30fa-1e68-45f... 对象具体化 请参见 Cross Apply 和 Outer Apply 是为关系导航生成的。可以进行这种重写的查询集定义不完善。因此,SQL Server 2000 支持的最小查询集是不涉及关系导航的集合。 text/ntext 在 Microsoft SQL Server 2005 支持的针对 varchar(max)/nvarchar(max) 的某些查询操作中,不能使用 text/ntext 数据类型。 不存在解决此限制的方法。具体而言,您不能对包含映射到 text 或 ntext 列的成员的任何结果使用 Distinct()。 由嵌套查询触发的行为 SQL Server 2000(一直到 SP4)联编程序具有由嵌套查询触发的一些特性。触发这些特性的 SQL 查询集定义 不完善。因此,您不能定义可能会引发 SQL Server 异常的 LINQ to SQL 查询集。 Skip 和 Take 运算符 Take(TSource) 和 Skip(TSource) 用在针对 SQL Server 2000 的查询中时存在一定的限制。有关更多信息,请 参见疑难解答 (LINQ to SQL) 中的“SQL Server 2000 中的 Skip 和 Take 异常”项。 具体化过程用一个或多个 SQL 查询返回的行创建 CLR 对象。 z 以下调用作为具体化过程的一部分在本地执行: z 构造函数 z 投影中的 ToString 方法 z 投影中的类型强制转换 z 紧跟 AsEnumerable(TSource) 方法之后的方法在本地执行。此方法不会导致直接执行。 z 您可以将 struct 用作查询结果的返回类型或结果类型的成员。实体需要变成类。匿名类型具体化为类的 实例,但命名结构(非实体)可在投影中使用。 z 查询结果的返回类型的成员可以为 IQueryable(T) 类型。它具体化为本地集合。 z 以下方法导致直接具体化应用这些方法的序列: z ToList(TSource) z ToDictionary z ToArray(TSource) 概念 参考 (LINQ to SQL) 如何:返回或跳过序列中的元素 (LINQ to SQL) 如何:串联两个序列 (LINQ to SQL) 如何:返回两个序列之间的差集 (LINQ to SQL) 如何:返回两个序列的交集 (LINQ to SQL) 如何:返回两个序列的并集 (LINQ to SQL) 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,4/4(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/a60c30fa-1e68-45f... 全部折叠 代码:C# LINQ to SQL 示例 (LINQ to SQL) 请参见 发送反馈意见 本主题提供指向包含 LINQ to SQL 示例代码的 Visual Basic 和 C# 解决方案的链接。 本节内容 请参见 SampleQueries 解决方案的 Visual Basic 版本 示例查询 (Visual Basic) SampleQueries 解决方案的 C# 版本 示例查询 请按以下这些步骤查找 LINQ to SQL 代码和应用程序的其他示例: z 在 MSDN Library 中搜索特定问题。 z 参与 LINQ Forum(LINQ 论坛),在这里您可以与专家们详细讨论更复杂的主题。 z 学习详细介绍 LINQ to SQL 技术并包含 Visual Basic 和 C# 代码示例的白皮书。有关更多信息,请参见 LINQ to SQL: .NET Language-Integrated Query for Relational Data(LINQ to SQL:关系数据的 .NET 语言 集成查询)。 概念 LINQ to SQL LINQ to SQL 演练 发送反馈意见,就此主题向 Microsoft 发送反馈意见。 页码,1/1(W)w 2008/3/18ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_linqsql/html/3fcc0c4a-b952-4cf...

下载文档,方便阅读与编辑

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 20 金币 [ 分享文档获得金币 ] 0 人已下载

下载文档

相关文档