ASP.NET 连接数据库的核心方式是使用 ADO.NET 及其提供程序模型。 这涉及到创建连接字符串、实例化连接对象(如 SqlConnection)、打开连接、执行命令(使用 SqlCommand)处理结果(使用 SqlDataReader 或 DataSet/DataTable),并妥善关闭连接,对于现代开发,Entity Framework Core (EF Core) 作为对象关系映射器 (ORM) 提供了更高级别的抽象和便利性,但底层仍基于 ADO.NET。

ADO.NET:基础且强大的连接方式
ADO.NET 是 .NET Framework 和 .NET Core/.NET 5+ 中访问数据的基石库,它采用断开式设计理念,特别适合 Web 应用场景,连接不同数据库(SQL Server, MySQL, PostgreSQL, Oracle 等)需要使用特定的 .NET Data Provider。
-
选择并引用数据提供程序:
- SQL Server: 内置
System.Data.SqlClient(经典) 或性能更优的Microsoft.Data.SqlClient(推荐),无需额外安装 NuGet 包(通常已包含)。 - MySQL: 安装 NuGet 包
MySql.Data(Oracle 官方) 或MySqlConnector(社区驱动,通常性能更好)。 - PostgreSQL: 安装 NuGet 包
Npgsql。 - SQLite: 安装 NuGet 包
System.Data.SQLite或Microsoft.Data.Sqlite(与 EF Core 集成更好)。 - Oracle: 安装 NuGet 包
Oracle.ManagedDataAccess.Core(推荐,无需本地客户端)。
- SQL Server: 内置
-
构建连接字符串 (Connection String):
连接字符串是一个包含键值对的字符串,告诉提供程序如何连接到特定的数据库实例,关键信息通常包括:Server/Data Source: 数据库服务器地址或实例名 (e.g.,localhost,localhostSQLEXPRESS,yourdb.mydomain.com).Database/Initial Catalog: 要连接的具体数据库名称。User ID/Uid: 数据库用户名 (如果使用 SQL 身份验证)。Password/Pwd: 数据库密码 (如果使用 SQL 身份验证)。Integrated Security/Trusted_Connection: 设置为true或SSPI表示使用当前 Windows 用户身份验证 (推荐用于内网应用)。- 其他参数:如连接超时 (
Connect Timeout)、加密 (Encrypt)、信任服务器证书 (TrustServerCertificate) 等。
安全建议:
- 绝对不要将连接字符串硬编码在源代码中。
- 使用配置源: 存储在
appsettings.json(ASP.NET Core) 或Web.config(ASP.NET Framework) 中。 - 使用机密管理器: 开发时,使用
dotnet user-secrets保护敏感信息。 - 使用 Azure Key Vault / 类似服务: 生产环境中管理高度敏感的凭据。
- 考虑连接字符串构建器:
SqlConnectionStringBuilder等类可以帮助以编程方式安全地构建连接字符串。
appsettings.json 示例 (ASP.NET Core):
{ "ConnectionStrings": { "DefaultConnection": "Server=localhost\SQLEXPRESS;Database=MyAppDb;Trusted_Connection=True;Encrypt=True;TrustServerCertificate=True;" } } -
建立连接并执行操作 (核心流程):
基本模式遵循 连接(Connection) -> 命令(Command) -> 读取器(Reader)/适配器(Adapter) 的结构。// ASP.NET Core 示例 (在 Controller 或 Service 层) using Microsoft.Data.SqlClient; // 或对应的提供程序命名空间 using Microsoft.Extensions.Configuration; public class ProductService { private readonly IConfiguration _configuration; public ProductService(IConfiguration configuration) { _configuration = configuration; } public List<Product> GetProducts() { var products = new List<Product>(); // 1. 从配置获取连接字符串 string connectionString = _configuration.GetConnectionString("DefaultConnection"); // 2. 创建并打开连接 (using 确保连接关闭和释放) using (SqlConnection connection = new SqlConnection(connectionString)) { // 3. 创建 SQL 命令 string sql = "SELECT ProductId, Name, Price FROM Products"; using (SqlCommand command = new SqlCommand(sql, connection)) { connection.Open(); // 显式打开连接 // 4. 执行查询并获取 DataReader using (SqlDataReader reader = command.ExecuteReader()) { // 5. 遍历结果集 while (reader.Read()) { products.Add(new Product { ProductId = reader.GetInt32(0), Name = reader.GetString(1), Price = reader.GetDecimal(2) }); } } // DataReader 自动关闭 } } // Connection 自动关闭和释放 return products; } }关键对象说明:
SqlConnection: 管理与数据库的物理连接。Open()打开连接,Close()/Dispose()(通常通过using) 关闭连接并释放资源。连接池 (Connection Pooling) 由提供程序自动管理,重用连接提升性能。SqlCommand: 表示要对数据库执行的 SQL 语句或存储过程,设置CommandText(SQL 文本或存储过程名) 和CommandType(Text 或 StoredProcedure),可添加参数 (SqlParameter) 防止 SQL 注入。SqlDataReader: 提供一种快速、只进、只读的方式从数据库检索数据流,效率最高,适合顺序处理大量数据。SqlDataAdapter&DataSet/DataTable: 用于在内存中缓存数据的断开式模型。DataAdapter填充DataSet/DataTable并可将更改更新回数据库,适用于需要离线操作数据或绑定到复杂 UI 控件的场景,但内存开销相对较大。
-
参数化查询 (防止 SQL 注入):
永远不要 通过拼接字符串来构造包含用户输入的 SQL 语句!使用参数化查询是防止 SQL 注入攻击的黄金法则。string sql = "SELECT FROM Users WHERE Username = @Username AND PasswordHash = @PasswordHash"; using (SqlCommand command = new SqlCommand(sql, connection)) { command.Parameters.AddWithValue("@Username", usernameInput); command.Parameters.AddWithValue("@PasswordHash", hashedPassword); // ... 执行命令 ... }使用
AddWithValue或更精确的类型化方法Add(new SqlParameter("@ParamName", SqlDbType.VarChar) { Value = inputValue }),提供程序会将参数值安全传递,与 SQL 指令分离。
Entity Framework Core (EF Core):现代 ORM 方案
EF Core 是微软官方推荐的 ORM 框架,它将数据库表映射到 .NET 对象(实体),将数据库操作(增删改查)转化为对对象集合的操作,极大简化了数据访问层代码。
-
核心概念:
- DbContext: 代表与数据库的会话,是查询和保存实体的主要入口点,包含
DbSet<T>属性。 - DbSet 代表数据库中特定表的所有实体集合。
T是实体类。 - Entity: 映射到数据库表的普通 C# 类 (POCOs – Plain Old CLR Objects)。
- 配置 (Fluent API / Data Annotations): 定义实体如何映射到数据库(表名、列名、主键、关系、约束等)。
- DbContext: 代表与数据库的会话,是查询和保存实体的主要入口点,包含
-
连接数据库步骤:
-
安装 NuGet 包: 核心包
Microsoft.EntityFrameworkCore+ 数据库提供程序包 (e.g.,Microsoft.EntityFrameworkCore.SqlServer,Pomelo.EntityFrameworkCore.MySql,Npgsql.EntityFrameworkCore.PostgreSQL,Microsoft.EntityFrameworkCore.Sqlite)。 -
定义实体类:
public class Product { public int ProductId { get; set; } // 约定 Id 或 [Class]Id 为主键 public string Name { get; set; } public decimal Price { get; set; } } -
创建 DbContext 派生类:
using Microsoft.EntityFrameworkCore; public class AppDbContext : DbContext { public DbSet<Product> Products { get; set; } // 映射到数据库 Products 表 public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { } // 可选的:使用 Fluent API 进行更精细的配置 (替代或补充 Data Annotations) protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Product>().ToTable("Products"); // 显式指定表名 modelBuilder.Entity<Product>().Property(p => p.Name).IsRequired().HasMaxLength(100); // ... 其他配置 ... } } -
配置连接字符串 (ASP.NET Core): 在
Startup.cs或Program.cs中注册 DbContext 服务并指定连接字符串。// Program.cs builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); // 对于其他数据库:UseMySql(), UseNpgsql(), UseSqlite() 等 -
使用 DbContext (依赖注入): 在控制器或服务中通过构造函数注入
AppDbContext。public class ProductController : Controller { private readonly AppDbContext _context; public ProductController(AppDbContext context) { _context = context; } public IActionResult Index() { var products = _context.Products.ToList(); // 查询所有产品 return View(products); } [HttpPost] public IActionResult Create(Product product) { if (ModelState.IsValid) { _context.Products.Add(product); // 添加实体 _context.SaveChanges(); // 将更改保存到数据库 return RedirectToAction("Index"); } return View(product); } // ... 其他 CRUD 操作 ... }
-
-
EF Core 优势:

- 开发效率高: 自动生成大部分数据访问代码(CRUD)。
- 强类型: 使用 LINQ (Language Integrated Query) 进行编译时检查的查询。
- 数据库无关性: 通过更换提供程序,代码可轻松切换底层数据库(需注意不同数据库的 SQL 方言差异)。
- 变更跟踪: 自动跟踪加载的实体的状态变化,简化更新操作。
- 迁移 (Migrations): 强大的工具,根据模型变化生成数据库架构更新脚本,管理数据库演进。
-
EF Core 与 Dapper 的选择:
- EF Core: 适合需要快速开发、模型复杂、需要变更跟踪、迁移、使用 LINQ 的场景,抽象层次高。
- Dapper: 一个轻量级的 Micro-ORM,扩展了
IDbConnection接口,提供简单的方法将查询结果映射到对象。性能通常接近原生 ADO.NET,需要手写 SQL,控制更精细,适合高性能场景、简单查询、对 SQL 有精确控制要求、或遗留项目集成,它通常与 ADO.NET 配合使用(使用SqlConnection等)。
关键实践与最佳方案
-
连接管理:
- 打开晚,关闭早: 使用
using语句 (IDisposable) 确保连接及时关闭释放回连接池。 - 连接池: 提供程序默认启用连接池,避免频繁开关连接,保持连接字符串一致以利用池化,注意
Max Pool Size/Min Pool Size等参数的调优(通常默认即可)。 - 异步操作: 使用
OpenAsync(),ExecuteReaderAsync(),ToListAsync()(EF Core) 等方法避免阻塞线程,提高应用吞吐量和响应性,尤其在 Web 应用中。
- 打开晚,关闭早: 使用
-
错误处理:
- 使用
try-catch块捕获数据库操作异常(SqlException,DbUpdateException等)。 - 记录异常详细信息(考虑结构化日志如 Serilog)。
- 向用户返回友好的错误信息,切勿泄露数据库结构或敏感错误细节。
- 实现重试逻辑(特别是瞬时错误,如网络闪断、连接池耗尽),可使用 Polly 等库。
- 使用
-
性能优化:
- EF Core: 使用
AsNoTracking()查询只读数据;使用Select投影仅加载需要的字段;谨慎使用Include/ThenInclude避免过度加载;批量操作考虑AddRange/RemoveRange和SaveChanges的批处理;评估使用原生 SQL (FromSqlRaw/ExecuteSqlRaw) 处理复杂查询。 - ADO.NET/Dapper: 使用参数化查询;选择合适的数据读取器 (
DataReader最快);考虑存储过程处理复杂逻辑。 - 通用: 优化数据库设计(索引!);监控慢查询;使用缓存(内存缓存、分布式缓存如 Redis)减少数据库访问。
- EF Core: 使用
ASP.NET 连接数据库的核心路径清晰明确:基础且灵活的 ADO.NET 提供程序模型 和 现代高效的 Entity Framework Core ORM,选择取决于项目需求、团队熟悉度和性能考量。
- 追求极致控制、性能或简单查询: 深入理解并使用 ADO.NET(结合 Dapper 提升映射效率)。
- 追求开发速度、强类型 LINQ、数据库抽象、模型驱动开发: EF Core 是首选方案。
- 无论哪种方式:
- 安全第一: 参数化查询、安全存储连接字符串。
- 资源管理: 妥善管理连接(
using)、考虑异步。 - 性能意识: 优化查询、利用连接池、考虑缓存。
掌握这两种核心方法及其适用场景,是构建健壮、高效、安全的 ASP.NET 数据访问层的关键。
您在项目中更倾向于使用 ADO.NET/Dapper 还是 EF Core?或者在什么样的场景下会混合使用两者?欢迎分享您的实战经验和遇到的挑战!
原创文章,作者:世雄 - 原生数据库架构专家,如若转载,请注明出处:https://idctop.com/article/24172.html