微软的sqlhelper Data Access Application Block for .NET

发布日期: 6/18/2004 | 更新日期: 6/18/2004

Chris Brooks、Graeme Malcolm、Alex Mackman、Edward Jezierski、Jason Hogg、Diego Gonzalez (Lagash)、Pablo Cibraro (Lagash) 和 Julian Cantore (Lagash)

Microsoft Corporation

摘要:数据访问应用程序块 (Data Access Application Block) 是一个 .NET 组件,它包含经过优化的数据访问代码,可以帮助用户调用存储过程以及向 SQL Server 数据库发出 SQL 文本命令。它返回 SqlDataReader、DataSet 和 XmlReader 对象。您可以在自己的基于 .NET 的应用程序中将其作为构造块来使用,以减少需要创建、测试和维护的自定义代码的数量。您可以下载完整的 Visual C# 和 Visual Basic .NET 源代码以及综合文档。

注意,本文档引用了一些仅在数据访问应用程序块的 2.0 版本中提供的功能。

*

简介

您是否正在从事基于 .NET 的应用程序数据访问代码的设计和开发工作?您是否曾经有过这种感觉 — 总是觉得自己在反复编写相同的数据访问代码?您是否曾经将数据访问代码包装在 Helper 函数中,以便能够在一行中调用存储过程?如果是,那么 Microsoft? Data Access Application Block for .NET 正是您所需要的。

数据访问应用程序块将有关访问 Microsoft SQL Server 数据库的性能和资源管理方面的最佳做法封装在一起。您可以很方便地在自己的基于 .NET 的应用程序中将其作为构造块使用,从而减少需要创建、测试和维护的自定义代码的数量。

特别是,数据访问应用程序块可以帮助您:

调用存储过程或 SQL 文本命令。

指定参数详细信息。

返回 SqlDataReaderDataSetXmlReader 对象。

使用强类型的 DataSet

例如,在引用了数据访问应用程序块的应用程序中,您可以在一行代码中调用存储过程并生成 DataSet,如下所示:

[Visual Basic]
Dim ds As DataSet = SqlHelper.ExecuteDataset( _
      connectionString, _
      CommandType.StoredProcedure, _
      "getProductsByCategory", _
      new SqlParameter("@CategoryID", categoryID))
 
[C#]
DataSet ds = SqlHelper.ExecuteDataset( 
      connectionString,
      CommandType.StoredProcedure,
      "getProductsByCategory",
      new SqlParameter("@CategoryID", categoryID)); 

:.NET 的应用程序块是在对成功的基于 .NET 的应用程序进行详细研究的基础上设计的。它以源代码的形式提供,您可以原样使用,也可以针对自己的应用程序进行自定义。该应用程序块并不代表未来 Microsoft ADO.NET 程序库的发展方向。Microsoft ADO.NET 程序库是为在各种使用情况下实现对数据访问行为的精确控制而生成的。将来的 ADO.NET 版本可能会使用不同的模型来实现这个方案。

本概述的其余部分包括以下内容:

本页内容
数据访问应用程序块包括哪些内容?数据访问应用程序块包括哪些内容?
下载和安装数据访问应用程序块下载和安装数据访问应用程序块
使用数据访问应用程序块使用数据访问应用程序块
内部设计内部设计
常见问题常见问题
反馈和支持反馈和支持
更多信息更多信息
合作者合作者

数据访问应用程序块包括哪些内容?

提供了数据访问应用程序块的源代码以及快速入门示例应用程序,您可以使用这些应用程序测试其功能。数据访问应用程序块还包括综合文档,可以帮助您使用和了解所提供的代码。

Visual Studio .NET 项目

提供了数据访问应用程序块的 Microsoft Visual Basic? .NET 和 Microsoft Visual C#? 源代码,以及每种语言的快速入门示例客户端应用程序,您可以使用这些应用程序测试通用的方案。这有助于加深您对数据访问应用程序块的工作原理的了解。您还可以自定义源代码以满足自己的需要。

您可以编译 Visual Basic 和 C# Microsoft.ApplicationBlocks.Data 项目,以生成一个名为 Microsoft.ApplicationBlocks.Data.dll 的程序集。该程序集包括一个名为 SqlHelper 的类(其中包含用于执行数据库命令的核心功能)和一个名为 SqlhelperParameterCache 的类(它提供参数发现和缓存功能)。

文档

数据访问应用程序块的文档主要包括以下内容:

使用数据访问应用程序块开发应用程序。本部分包含一些快速入门示例,涵盖了许多常见的用例。这些示例可以帮助您快速而方便地开始使用数据访问应用程序块。

数据访问应用程序块的设计与实现。本部分包括背景设计原理信息,以便用户深入了解数据访问应用程序块的设计与实现。

部署和运行。本部分包括安装信息,其中包含部署和更新选项以及与安全性有关的信息。

参考。本部分包含完整的 API 参考资料,其中介绍了构成数据访问应用程序块的类和接口。

系统要求

要运行 Data Access Application Block 版本 2.0,您需要满足以下条件:

Microsoft Windows?2000、Windows XP Professional 或 Windows 2003 操作系统

Microsoft .NET 框架软件开发工具包 (SDK) 版本 1.1、版本 1.1

Microsoft Visual Studio?2003 开发系统

运行 SQL Server 7.0 或更高版本的数据库服务器

要运行 Data Access Application Block 版本 1.0,您需要满足以下条件:

Microsoft Windows 2000 或 Windows XP Professional

.NET 框架 SDK 的 RTM 版本

Visual Studio .NET 的 RTM 版本(推荐,但不必需)

运行 SQL Server 7.0 或更高版本的数据库服务器

下载和安装数据访问应用程序块

您可以获取一个包含已签名的数据访问应用程序块程序集和综合文档的 Windows 安装程序文件。

安装进程将在您的 Programs 菜单中创建一个 Microsoft Application Blocks for .NET 子菜单。在 Microsoft Application Blocks for .NET 子菜单上,有一个 Data Access 子菜单,它包含用于启动文档和启动 Data Access Application Block Visual Studio.NET 解决方案的选项。

您可以从 Microsoft Download Center 下载 Data Access Application Block 版本 1.0

您可以从 Microsoft Download Center 下载 Data Access Application Block 版本 2.0

使用数据访问应用程序块

本节讨论如何使用数据访问应用程序块来执行数据库命令和管理参数。图 1 显示了数据访问应用程序块的主要元素。


1. 数据访问应用程序块

SqlHelper 类提供了一组静态方法,可以用来对 SQL Server 数据库执行多种不同类型的命令。

SqlHelperParameterCache 类提供命令参数缓存功能,可以用来提高性能。该类由许多 Execute 方法(尤其是那些只执行存储过程的重载方法)在内部使用。数据访问客户端也可以直接使用它来缓存特定命令的特定参数集。

使用 SqlHelper 类执行命令

SqlHelper 类提供了 13 个 Shared (Visual Basic) 或 static (C#) 方法,如上图所示。实现的每种方法都提供一组一致的重载。这提供了一种很好的使用 SqlHelper 类来执行命令的定义模式,同时为开发人员选择访问数据的方式提供了必要的灵活性。每种方法的重载都支持不同的方法参数,因此开发人员可以确定传递连接、事务和参数信息的方式。类中实现的所有方法都支持以下重载:

[Visual Basic]
Execute* (ByVal connection As SqlConnection, _
          ByVal commandType As CommandType, _
          ByVal CommandText As String)

Execute* (ByVal connection As SqlConnection, _
          ByVal commandType As CommandType, _
          ByVal commandText As String, _
          ByVal ParamArray commandParameters() As SqlParameter)

Execute* (ByVal connection As SqlConnection, ByVal spName As String, _
          ByVal ParamArray parameterValues() As Object)

Execute* (ByVal transaction As SqlTransaction, _
          ByVal commandType As CommandType, _
          ByVal commandText As String)

Execute* (ByVal transaction As SqlTransaction, _
          ByVal commandType As CommandType, _
          ByVal commandText As String, _
          ByVal ParamArray commandParameters() As SqlParameter)

Execute* (ByVal transaction As SqlTransaction, _
          ByVal spName As String, _
          ByVal ParamArray parameterValues() As Object)

[C#]
Execute* (SqlConnection connection, CommandType commandType, 
          string commandText)

Execute* (SqlConnection connection, CommandType commandType,
          string commandText, params SqlParameter[] commandParameters)

Execute* (SqlConnection connection, string spName, 
          params object[] parameterValues)

Execute* (SqlConnection connection, 
          CommandType commandType, string commandText)

Execute* (SqlConnection connection,
          CommandType commandType, string commandText, 
          params SqlParameter[] commandParameters)

Execute* (SqlConnection connection,
          string spName, params object[] parameterValues)

除了这些重载以外,所有方法(ExecuteXmlReaderUpdateDatasetCreateCommand 除外)都提供了重载,以允许连接信息作为连接字符串而不是作为连接对象传递,如下面的方法签名所示:

[Visual Basic]
Execute* (ByVal connectionString As String, _
          ByVal commandType As CommandType, _
          ByVal commandText As String)

Execute* (ByVal connectionString As String, _
          ByVal commandType As CommandType, _
          ByVal commandText As String, _
          ByVal ParamArray commandParameters() As SqlParameter)

Execute* (ByVal connectionString As String, ByVal spName As String, _
          ByVal ParamArray parameterValues() As Object)

[C#]
Execute* (string connectionString, CommandType commandType, 
          string commandText)

Execute* (string connectionString, CommandType commandType, 
          string commandText, 
          params SqlParameter[] commandParameters)

Execute* (string connectionString, string spName, 
          params object[] parameterValues)
  

ExecuteXmlReader 不支持连接字符串,这是因为与 SqlDataReader 对象不同,在关闭 XmlReader 时,XmlReader 对象不提供自动关闭连接的方法。如果客户端传递了连接字符串,那么当客户端完成对 XmlReader 的操作后,将无法关闭与 XmlReader 相关联的连接对象。UpdateDataset 使用已有的连接,而 CreateCommand 使用 SqlConnection 对象。

通过引用数据访问应用程序块程序集并导入 Microsoft.ApplicationBlocks.Data 命名空间,您可以编写使用任何一种 SqlHelper 类方法的代码,如下面的代码示例所示:

[Visual Basic]
Imports Microsoft.ApplicationBlocks.Data

[C#]
using Microsoft.ApplicationBlocks.Data;

导入该命名空间后,您可以调用任何 Execute* 方法,如下面的代码示例所示:

[Visual Basic]
Dim ds As DataSet = SqlHelper.ExecuteDataset( _
      "SERVER=(local);DATABASE=Northwind;INTEGRATED SECURITY=True;", _    
      CommandType.Text, "SELECT * FROM Products")

[C#]
DataSet ds = SqlHelper.ExecuteDataset( 
  "SERVER=DataServer;DATABASE=Northwind;INTEGRATED SECURITY=sspi;", 
  CommandType.Text, "SELECT * FROM Products");

使用 SqlHelperParameterCache 类管理参数

SqlHelperParameterCache 类提供了三个可以用来管理参数的公共共享方法。它们是:

CacheParameterSet。用于将 SqlParameters 数组存储到缓存中。

GetCachedParameterSet。用于检索缓存的参数数组的副本。

GetSpParameterSet。一种重载方法,用于检索指定存储过程的相应参数(首先查询一次数据库,然后缓存结果以便将来查询)。

缓存和检索参数

通过使用 CacheParameterSet 方法,可以缓存 SqlParameter 对象数组。此方法通过将连接字符串和命令文本连接起来创建一个键,然后将参数数组存储在 Hashtable 中。

为了从缓存中检索参数,使用了 GetCachedParameterSet 方法。此方法返回一个 SqlParameter 对象数组,这些对象已使用特定缓存(该缓存与传递给该方法的连接字符串和命令文本相对应)中参数的名称、方向和数据类型等进行了初始化。

用作参数集的键的连接字符串通过简单的字符串比较进行匹配。用于从 GetCachedParameterSet 中检索参数的连接字符串必须与用来通过 CacheParameterSet 来存储这些参数的连接字符串完全相同。语法不同的连接字符串即使语义相同,也不会被认为是匹配的。

以下代码显示了如何使用 SqlHelperParameterCache 类来缓存和检索 Transact-SQL 语句的参数。

[Visual Basic]
'Initialize the connection string and command text
'These will form the key used to store and retrieve the parameters
Const CONN_STRING As String = _
  "SERVER=(local); DATABASE=Northwind; INTEGRATED SECURITY=True;"
Dim sql As String = _
         "SELECT ProductName FROM Products WHERE CategoryID=@Cat " + _
         "AND SupplierID = @Sup"

'Cache the parameters
Dim paramsToStore(1) As SqlParameter
paramsToStore(0) = New SqlParameter("@Cat", SqlDbType.Int)
paramsToStore(1) = New SqlParameter("@Sup", SqlDbType.Int)
SqlHelperParameterCache.CacheParameterSet(CONN_STRING, sql, _
                                          paramsToStore)

'Retrieve the parameters from the cache
Dim storedParams(1) As SqlParameter
storedParams = SqlHelperParameterCache.GetCachedParameterSet(
                                             CONN_STRING, sql)
storedParams(0).Value = 2
storedParams(1).Value = 3

'Use the parameters in a command
Dim ds As DataSet
ds = SqlHelper.ExecuteDataset(CONN_STRING, CommandType.Text, sql, _
                              storedParams)

[C#]
// Initialize the connection string and command text
// These will form the key used to store and retrieve the parameters
const string CONN_STRING =
  "SERVER=(local); DATABASE=Northwind; INTEGRATED SECURITY=True;";
string spName = 
"SELECT ProductName FROM Products WHERE CategoryID=@Cat " + 
                                  "AND SupplierID = @Sup";

//Cache the parameters
SqlParameter[] paramsToStore = new SqlParameter[2];
paramsToStore[0] = New SqlParameter("@Cat", SqlDbType.Int);
paramsToStore[1] = New SqlParameter("@Sup", SqlDbType.Int);
SqlHelperParameterCache.CacheParameterSet(CONN_STRING, sql, 
                                          paramsToStore);

//Retrieve the parameters from the cache
SqlParameter storedParams = new SqlParameter[2];
storedParams = SqlHelperParameterCache.GetCachedParameterSet(
                                             CONN_STRING, sql);
storedParams(0).Value = 2;
storedParams(1).Value = 3;

//Use the parameters in a command
DataSet ds;
ds = SqlHelper.ExecuteDataset(CONN_STRING, 
                              CommandType.StoredProcedure,
                              sql, storedParams);

检索存储过程参数

SqlHelperParameterCache 还提供了针对特定存储过程检索参数数组的方法。一个名为 GetSpParameterSet 的重载方法提供了此功能,它包含两种实现。该方法尝试从缓存中检索特定存储过程的参数。如果这些参数尚未被缓存,则使用 .NET SqlCommandBuilder 类从内部检索,并将它们添加到缓存中以用于后续的请求。然后,为每个参数指定相应的参数设置,最后将这些参数以数组形式返回给客户端。以下代码显示了如何检索 Northwind 数据库中 SalesByCategory 存储过程的参数。

[Visual Basic]
'Initialize the connection string and command text
'These will form the key used to store and retrieve the parameters
Const CONN_STRING As String = _
  "SERVER=(local); DATABASE=Northwind; INTEGRATED SECURITY=True;"
Dim spName As String = "SalesByCategory"

'Retrieve the parameters
Dim storedParams(1) As SqlParameter
storedParams = SqlHelperParameterCache.GetSpParameterSet(CONN_STRING, spName)
storedParams(0).Value = "Beverages"
storedParams(1).Value = "1997"

'Use the parameters in a command
Dim ds As DataSet
ds = SqlHelper.ExecuteDataset(CONN_STRING, _
                              CommandType.StoredProcedure, _
                              spName, storedParams)

[C#]
// Initialize the connection string and command text
/ These will form the key used to store and retrieve the parameters
const string CONN_STRING = 
  "SERVER=(local); DATABASE=Northwind; INTEGRATED SECURITY=True;";
string spName = "SalesByCategory";

// Retrieve the parameters
SqlParameter storedParams = new SqlParameter[2];
storedParams = SqlHelperParameterCache.GetSpParameterSet(CONN_STRING, 
                                                         spName);
storedParams[0].Value = "Beverages";
storedParams[1].Value = "1997";

//Use the parameters in a command
DataSet ds;
ds = SqlHelper.ExecuteDataset(CONN_STRING, CommandType.StoredProcedure,
                              spName, storedParams);

内部设计

数据访问应用程序块包含了完整的源代码和有关其设计的综合指南。本节介绍有关主要实现的详细信息。

SqlHelper 类实现详细信息

SqlHelper 类用于通过一组静态方法来封装数据访问功能。该类不能被继承或实例化,因此将其声明为包含专用构造函数的不可继承类。

SqlHelper 类中实现的每种方法都提供了一组一致的重载。这提供了一种很好的使用 SqlHelper 类来执行命令的模式,同时为开发人员选择访问数据的方式提供了必要的灵活性。每种方法的重载都支持不同的方法参数,因此开发人员可以确定传递连接、事务和参数信息的方式。在 SqlHelper 类中实现的方法包括:

ExecuteNonQuery。此方法用于执行不返回任何行或值的命令。这些命令通常用于执行数据库更新,但也可用于返回存储过程的输出参数。

ExecuteReader。此方法用于返回 SqlDataReader 对象,该对象包含由某一命令返回的结果集。

ExecuteDataset。此方法返回 DataSet 对象,该对象包含由某一命令返回的结果集。

ExecuteScalar。此方法返回一个值。该值始终是该命令返回的第一行的第一列。

ExecuteXmlReader。此方法返回 FOR XML 查询的 XML 片段。

FillDataset。此方法类似于 ExecuteDataset,不同之处在于可以传入预先存在的 DataSet,从而允许添加附加表。

UpdateDataset。此方法使用现有的连接和用户指定的更新命令来更新 DataSet。它通常与 CreateCommand 命令结合使用。

CreateCommand。此方法允许提供存储过程和可选参数,从而简化了 SQL 命令对象的创建过程。此方法通常与 UpdateDataset 结合使用。

ExecuteNonQueryTypedParams。此方法使用数据行而不是参数来执行非查询操作。

ExecuteDatasetTypedParams。此方法使用数据行而不是参数来执行 DataSet 创建操作。

ExecuteReaderTypedParams。此方法使用数据行而不是参数来返回数据读取器。

ExecuteScalarTypedParams。此方法使用数据行而不是参数来返回标量。

ExecuteXmlReaderTypedParams。此方法使用数据行而不是参数来执行 XmlReader

除了这些公共方法以外,SqlHelper 类还包含一些专用函数,用于管理参数和准备要执行的命令。不管客户端调用什么样的方法实现,所有命令都通过使用 SqlCommand 对象来执行。在执行此 SqlCommand 对象之前,必须将所有参数添加到它的 Parameters 集合中,并且必须相应地设置 ConnectionCommandTypeCommandTextTransaction 属性。SqlHelper 类中的专用函数主要用于提供一种一致的方式,以便对 SQL Server 数据库执行命令,而不考虑客户端应用程序所调用的重载方法实现。SqlHelper 类中的专用实用工具函数包括:

AttachParameters。该函数用于将任何需要的 SqlParameter 对象附加到要执行的 SqlCommand

AssignParameterValues。该函数用于向 SqlParameter 对象赋值。

PrepareCommand。该函数用于对命令的属性(如连接、事务上下文等)进行初始化。

ExecuteReaderExecuteReader 的这一专用实现用于打开 SqlDataReader 对象,并使用相应的 CommandBehavior 以最有效的方式来管理与该读取器关联的连接的生命期。

SqlHelperParameterCache 类实现详细信息

参数数组缓存在专用的 Hashtable 中。从缓存中检索的参数在内部进行复制,这样客户端应用程序能够更改参数值以及进行其他操作,而不会影响缓存的参数数组。专用共享函数 CloneParameters 用于实现此目的。

常见问题

此版本包含哪些新增功能?

Data Access Application Block 的 2.0 版本包含以下新增功能:

使用 FillDataset 方法支持强类型的 DataSet

支持将对 DataSet 的更新提交回数据库

提供了附加 Helper 方法,并支持 DataRow 类型参数

只须修复小的错误

与 Data Access Application Block Beta 2.0 版本相比,该 RTM 版本包含以下新增功能和变化:

SqlHelper 类方法的事务型重载不再需要 SqlConnection 参数。在此版本中,连接信息从 SqlTransaction 对象中派生,因此不必在方法签名中包含 SqlConnection 对象参数。

GetSpParameterSet 方法现在使用 ADO.NET CommandBuilder 类的 DeriveParameters 方法来确定存储过程所需的参数。这比 Beta 2.0 版本中直接通过查询数据库来检索信息效率更高。

可以使用 XCOPY 部署方法来部署数据访问应用程序块程序集吗?

可以。编译后,可以使用 xcopy 来部署 Microsoft.ApplicationBlocks.Data.dll 程序集。

什么时候应该使用 ExecuteDataset 方法,什么时候应该使用 ExecuteReader 方法?

这个问题实际上是什么时候应该在 DataSet 对象中返回多行数据,什么时候应该使用 DataReader。答案取决于您的应用程序的特定需要,以及您在灵活性和原始性能之间的取舍。DataSet 可以为数据提供一种灵活的、断开的关系视图,而 DataReader 在这方面则提供了一种具有极高性能、只读功能和只进的游标。

如果您使用参数数组来返回输出值,请记住您必须在关闭 SqlDataReader 对象后提取这些值(使用 SqlParameter 值 属性)。

有关 DataSetsDataReaders 的全面比较,请参阅《数据访问结构指南》,网址为:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/daag.asp

什么时候应该使用 Execute*TypedParams 方法?

这些方法旨在利用对强类型 DataSet 的支持。使用它们可以将整个类型化的数据行作为存储过程的参数进行传递,而不是作为通常映射到表中所有域的所有参数的数组进行传递。

如何使用 ExecuteDataset 返回包含多个表的数据集?

通过创建一个可以返回多个行集的存储过程(通过执行多个 SELECT 语句,或者通过对其他存储过程进行嵌套调用),并使用 ExecuteDataset 方法执行该过程,您可以检索包含多个表的 DataSet

例如,假设您的数据库包含以下存储过程。

CREATE PROCEDURE GetCategories
AS
SELECT * FROM Categories
GO
CREATE PROCEDURE GetProducts
AS
SELECT * FROM Products
  

您可以创建一个主存储过程来对这些过程进行嵌套调用,如下面的代码示例所示。

CREATE PROCEDURE GetCategoriesAndProducts
AS
BEGIN
  EXEC GetCategories
  EXEC GetProducts
END

使用 ExecuteDataset 方法执行此主存储过程,会返回包含两个表的单个 DataSet,一个表包含类别数据,另一个表包含产品数据。

ExecuteDataset 方法没有提供将自定义名称赋给返回的表的方法。第一个表的编号始终为 0,并且名为 Table,第二个表的编号始终为 1,并且名为 Table1,依此类推。

还有其他的应用程序块吗?

数据访问应用程序块是即将发布的几个应用程序块之一。这些应用程序块可以解决开发人员在不同项目中遇到的共同问题。它们可以快速而方便地插入到基于 .NET 的应用程序中。

反馈和支持

.NET 的应用程序块用以推动 .NET 分布式应用程序的开发。示例代码和文档都按原样提供。通过 Microsoft 产品支持可以获得收费支持。

要了解有关 .NET 最佳实践的详细信息,请访问 patterns & practices 网页。

要参与有关此主题的联机协作开发环境,请加入 GotDotNet 工作区:Microsoft Patterns & Practices Data Access for .NET Workspace。在该工作区的社区内,能满足您有关数据访问块的问题、建议和自定义方面的共享。

您是否有任何问题、意见和建议?要对本文内容进行反馈,请发送电子邮件至:devfdbck@microsoft.com 与我们联系。

更多信息

数据访问应用程序块是基于《.NET 数据访问体系结构指南》(网址为 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/daag.asp)中讨论的最佳实践和一般性设计原则设计和开发的。请阅读该指南,以了解有关数据访问的详细信息。

合作者

非常感谢下列投稿人和审阅者:Susan Warren、Brad Abrams、Andy Dunn、Michael Day、Mark Ashton、Gregory Leake、Steve Busby、Kenny Jones、David Schleifer、Pablo Castro、Michael Pizzo、Paul Andrew、Cihan Biyikoglu、Eugenio Pace、Roger Lamb、Nick Takos、Andrew Roubin (Vorsite Corp.)、Jeffrey Richter (Wintellect)、Bernard Chen (Sapient) 和 Matt Drucker (Turner Broadcasting)。

同时还要感谢内容工作组成员:Tina Burden (Entirenet)、Shylender Ramamurthy (Infosys Technologies Ltd)、Filiberto Selvas Patino、Roberta Leibovitz 和 Colin Campbell (Modeled Computation LLC)。

转到原英文页面

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值