杰瑞科技汇

asp.net 数据库教程

  1. 准备工作:搭建开发环境和数据库。
  2. 核心概念:了解 ADO.NET 和 EF Core 这两种主流的数据访问方式。
  3. 实战演练:通过一个完整的“待办事项列表”项目,分别演示使用 ADO.NET 和 Entity Framework Core (EF Core) 进行数据库操作。
  4. 进阶主题:介绍一些高级概念和最佳实践。

第 1 部分:准备工作

在开始之前,请确保您已安装以下软件:

asp.net 数据库教程-图1
(图片来源网络,侵删)
  1. .NET SDK:推荐安装最新的长期支持版本,.NET 6, 7, 8。
  2. IDE (集成开发环境)
    • Visual Studio 2025 (推荐,社区版免费): 提供了强大的 ASP.NET 开发支持。
    • Visual Studio Code: 轻量级编辑器,配合 C# Dev Kit 扩展也能很好地进行开发。
  3. 数据库管理系统
    • SQL Server Express LocalDB:Visual Studio 安装时会自动包含一个轻量级的 SQL Server 版本,非常适合初学者。
    • SQL Server:完整的数据库服务器。
    • SQLite:一个轻量级的、文件式的数据库,无需安装服务器,非常适合学习和测试。
    • PostgreSQL / MySQL:其他流行的开源数据库选择。

第 2 部分:核心概念 - 两种数据访问方式

在 ASP.NET 中,与数据库交互主要有两种主流方式:ADO.NETEntity Framework Core (EF Core)

1 ADO.NET (传统方式)

ADO.NET 是 .NET 中用于与数据源(如数据库)进行交互的低级 API,它提供了对数据库连接、命令、数据读取器等的直接控制。

  • 特点

    • 性能高:直接操作数据库,开销小。
    • 控制力强:开发者可以精确控制 SQL 语句和数据库操作。
    • 代码繁琐:需要编写大量样板代码来连接、打开、关闭连接,并手动处理数据映射。
    • SQL 注入风险:如果直接拼接 SQL 字符串,容易受到 SQL 注入攻击(通常使用参数化查询来避免)。
  • 核心组件

    asp.net 数据库教程-图2
    (图片来源网络,侵删)
    • SqlConnection:建立与 SQL Server 的连接。
    • SqlCommand:执行 SQL 命令(查询、插入、更新、删除)。
    • SqlDataReader:只进、只读的数据流,用于读取查询结果。
    • DataSet / DataTable:内存中的数据缓存,用于离线操作和复杂关系处理。

2 Entity Framework Core (现代方式)

Entity Framework Core (EF Core) 是一个现代的对象关系映射器,它允许您使用 .NET 对象(称为“实体”)来操作数据库,而无需编写大量的 SQL 代码。

  • 特点

    • 开发效率高:将数据库表映射为 C# 类(实体),通过操作这些类来完成数据操作。
    • 代码简洁:减少了大量的样板代码。
    • 数据库无关性:可以通过更换数据库提供程序,轻松地将应用从一种数据库(如 SQL Server)迁移到另一种(如 PostgreSQL)。
    • 功能强大:支持 LINQ 查询、数据迁移、变更追踪等高级功能。
    • 轻微性能开销:相比原生 ADO.NET,存在一定的抽象层开销,但在大多数应用中,这个开销可以忽略不计。
  • 核心概念

    • DbContext (上下文):这是 EF Core 的核心,它负责与数据库的交互,并跟踪实体的状态。
    • Entity (实体):一个普通的 C# 类,通常对应数据库中的一张表。
    • DbSet (数据集):在 DbContext 中定义的 DbSet<T> 属性,代表数据库中的一张表。
    • Migration (迁移):一种机制,用于将您的实体模型变更(如添加新表、新字段)同步到数据库结构。

第 3 部分:实战演练 - 创建一个待办事项应用

我们将创建一个简单的 ASP.NET Core MVC 应用,实现“添加”、“查看”、“编辑”和“删除”待办事项的功能。

步骤 1:创建项目

  1. 打开 Visual Studio 2025。
  2. 选择“创建新项目”。
  3. 搜索并选择 “ASP.NET Core Web 应用” 模板。
  4. 点击“下一步”,命名为 TodoApp
  5. 在“其他信息”页面中:
    • 框架:选择最新的 .NET 版本 (如 .NET 8.0)。
    • 身份验证类型:选择“无”。
    • 配置为 API:取消勾选。
    • 勾选“为 HTTPS 配置”
  6. 点击“创建”。

步骤 2:选择数据访问方式并实现

我们分别用 ADO.NET 和 EF Core 来实现数据访问层。


方案 A:使用 ADO.NET

这种方式更接近“裸”的数据库操作,能让你理解底层发生了什么。

创建数据库

  • 打开 SQL Server Object Explorer (在 Visual Studio 的 "视图" -> "SQL Server Object Explorer")。
  • 连接到 LocalDB (通常是 (localdb)\MSSQLLocalDB)。
  • 右键点击 "数据库",选择 "添加新数据库",命名为 TodoDb
  • TodoDb 上右键,选择 "新建查询",然后执行以下 SQL 语句来创建 TodoItems 表:
CREATE TABLE TodoItems (
    Id INT PRIMARY KEY IDENTITY(1,1),NVARCHAR(100) NOT NULL,
    IsDone BIT NOT NULL DEFAULT 0,
    DueDate DATETIME NULL
);

创建数据模型

Models 文件夹中,创建 TodoItem.cs 文件:

// Models/TodoItem.cs
namespace TodoApp.Models
{
    public class TodoItem
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public bool IsDone { get; set; }
        public DateTime? DueDate { get; set; }
    }
}

创建数据库服务

为了管理数据库连接,我们创建一个服务类。

  • Services 文件夹(如果没有就创建一个)中,创建 TodoDbService.cs
// Services/TodoDbService.cs
using System.Data;
using System.Data.SqlClient;
using Microsoft.Extensions.Configuration;
using TodoApp.Models;
namespace TodoApp.Services
{
    public class TodoDbService
    {
        private readonly IConfiguration _configuration;
        private readonly string _connectionString;
        public TodoDbService(IConfiguration configuration)
        {
            _configuration = configuration;
            // 从 appsettings.json 读取连接字符串
            _connectionString = _configuration.GetConnectionString("DefaultConnection");
        }
        // 获取所有待办事项
        public List<TodoItem> GetAll()
        {
            var todos = new List<TodoItem>();
            using (var connection = new SqlConnection(_connectionString))
            {
                var command = new SqlCommand("SELECT Id, Title, IsDone, DueDate FROM TodoItems", connection);
                connection.Open();
                using (var reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        todos.Add(new TodoItem
                        {
                            Id = reader.GetInt32(0),
                            Title = reader.GetString(1),
                            IsDone = reader.GetBoolean(2),
                            DueDate = reader.IsDBNull(3) ? (DateTime?)null : reader.GetDateTime(3)
                        });
                    }
                }
            }
            return todos;
        }
        // 添加一个新的待办事项
        public void Add(TodoItem item)
        {
            using (var connection = new SqlConnection(_connectionString))
            {
                var command = new SqlCommand("INSERT INTO TodoItems (Title, IsDone, DueDate) VALUES (@Title, @IsDone, @DueDate)", connection);
                command.Parameters.AddWithValue("@Title", item.Title);
                command.Parameters.AddWithValue("@IsDone", item.IsDone);
                command.Parameters.AddWithValue("@DueDate", (object)item.DueDate ?? DBNull.Value);
                connection.Open();
                command.ExecuteNonQuery();
            }
        }
        // 更新待办事项
        public void Update(TodoItem item)
        {
            using (var connection = new SqlConnection(_connectionString))
            {
                var command = new SqlCommand("UPDATE TodoItems SET Title = @Title, IsDone = @IsDone, DueDate = @DueDate WHERE Id = @Id", connection);
                command.Parameters.AddWithValue("@Id", item.Id);
                command.Parameters.AddWithValue("@Title", item.Title);
                command.Parameters.AddWithValue("@IsDone", item.IsDone);
                command.Parameters.AddWithValue("@DueDate", (object)item.DueDate ?? DBNull.Value);
                connection.Open();
                command.ExecuteNonQuery();
            }
        }
        // 删除待办事项
        public void Delete(int id)
        {
            using (var connection = new SqlConnection(_connectionString))
            {
                var command = new SqlCommand("DELETE FROM TodoItems WHERE Id = @Id", connection);
                command.Parameters.AddWithValue("@Id", id);
                connection.Open();
                command.ExecuteNonQuery();
            }
        }
    }
}

配置连接字符串

打开 appsettings.json 文件,添加连接字符串:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=TodoDb;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Logging": { ... },
  "AllowedHosts": "*"
}

注册服务和控制器

  • 注册服务:在 Program.cs 中,将我们的服务注册到依赖注入容器中。
// Program.cs
using TodoApp.Services;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
// 注册 TodoDbService
builder.Services.AddScoped<TodoDbService>();
var app = builder.Build();
// ... 其余代码不变
  • 创建控制器:在 Controllers 文件夹中,右键添加 -> "控制器" -> "MVC 控制器 - 空",命名为 TodoController
// Controllers/TodoController.cs
using Microsoft.AspNetCore.Mvc;
using TodoApp.Models;
using TodoApp.Services;
namespace TodoApp.Controllers
{
    public class TodoController : Controller
    {
        private readonly TodoDbService _todoService;
        public TodoController(TodoDbService todoService)
        {
            _todoService = todoService;
        }
        // GET: Todo
        public IActionResult Index()
        {
            var todos = _todoService.GetAll();
            return View(todos);
        }
        // GET: Todo/Create
        public IActionResult Create()
        {
            return View();
        }
        // POST: Todo/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Create(TodoItem item)
        {
            if (ModelState.IsValid)
            {
                _todoService.Add(item);
                return RedirectToAction(nameof(Index));
            }
            return View(item);
        }
        // GET: Todo/Edit/5
        public IActionResult Edit(int id)
        {
            var item = _todoService.GetAll().FirstOrDefault(t => t.Id == id);
            if (item == null)
            {
                return NotFound();
            }
            return View(item);
        }
        // POST: Todo/Edit/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Edit(int id, TodoItem item)
        {
            if (id != item.Id)
            {
                return NotFound();
            }
            if (ModelState.IsValid)
            {
                _todoService.Update(item);
                return RedirectToAction(nameof(Index));
            }
            return View(item);
        }
        // GET: Todo/Delete/5
        public IActionResult Delete(int id)
        {
            var item = _todoService.GetAll().FirstOrDefault(t => t.Id == id);
            if (item == null)
            {
                return NotFound();
            }
            return View(item);
        }
        // POST: Todo/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public IActionResult DeleteConfirmed(int id)
        {
            _todoService.Delete(id);
            return RedirectToAction(nameof(Index));
        }
    }
}

创建视图

  • TodoController 的每个 View() 方法上右键,选择“添加视图”。
  • 视图类型:选择 List (Index), Create, Edit, Delete
  • 模型类:选择 TodoApp.Models.TodoItem
  • 数据上下文类:留空。
  • 勾选“使用布局页面”。
  • 生成所有视图后,运行项目,访问 /Todo 即可看到效果。

方案 B:使用 Entity Framework Core (推荐)

这是现代 .NET 开发的标准做法,效率更高,代码更优雅。

安装 EF Core 包

在 Visual Studio 的“解决方案资源管理器”中,右键点击项目 -> “管理 NuGet 程序包”。 搜索并安装以下两个包:

  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools

创建数据模型

与 ADO.NET 方案中的 Models/TodoItem.cs 完全相同

创建 DbContext

DbContext 是 EF Core 的核心。

  • Data 文件夹(如果没有就创建一个)中,创建 TodoDbContext.cs
// Data/TodoDbContext.cs
using Microsoft.EntityFrameworkCore;
using TodoApp.Models;
namespace TodoApp.Data
{
    public class TodoDbContext : DbContext
    {
        public TodoDbContext(DbContextOptions<TodoDbContext> options) : base(options)
        {
        }
        // DbSet 代表数据库中的一张表
        public DbSet<TodoItem> TodoItems { get; set; }
    }
}

配置 DbContext 和连接字符串

  • 连接字符串:与 ADO.NET 方案中 appsettings.json 的配置 完全相同

  • 注册 DbContext:在 Program.cs 中,将 DbContext 注册到依赖注入容器中。

// Program.cs
using Microsoft.EntityFrameworkCore;
using TodoApp.Data;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
// 配置和注册 TodoDbContext
builder.Services.AddDbContext<TodoDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
var app = builder.Build();
// ... 其余代码不变

创建数据库迁移

这是 EF Core 的强大功能,它会根据你的模型自动创建或更新数据库结构。

  1. 在 Visual Studio 的“程序包管理器控制台”(Package Manager Console) 中 (工具 -> NuGet 包管理器 -> 程序包管理器控制台)。

  2. 执行以下命令:

    Add-Migration InitialCreate
    • InitialCreate 是迁移名称,你可以自定义。
    • 此命令会创建一个包含数据库创建脚本的 Migrations 文件夹。
  3. 再次执行以下命令,将迁移应用到数据库:

    Update-Database
    • 你的 TodoDb 数据库中会自动出现 TodoItems 表,甚至包含一个 __EFMigrationsHistory 表来记录迁移历史。

创建仓储模式 (Repository Pattern - 可选但推荐)

为了解耦控制器和数据访问逻辑,我们创建一个仓储。

  • Repositories 文件夹中,创建 ITodoRepository.cs (接口) 和 TodoRepository.cs (实现)。
// Repositories/ITodoRepository.cs
using TodoApp.Models;
namespace TodoApp.Repositories
{
    public interface ITodoRepository
    {
        IEnumerable<TodoItem> GetAll();
        TodoItem GetById(int id);
        void Add(TodoItem item);
        void Update(TodoItem item);
        void Delete(int id);
    }
}
// Repositories/TodoRepository.cs
using Microsoft.EntityFrameworkCore;
using TodoApp.Data;
using TodoApp.Models;
namespace TodoApp.Repositories
{
    public class TodoRepository : ITodoRepository
    {
        private readonly TodoDbContext _context;
        public TodoRepository(TodoDbContext context)
        {
            _context = context;
        }
        public void Add(TodoItem item)
        {
            _context.TodoItems.Add(item);
            _context.SaveChanges();
        }
        public void Delete(int id)
        {
            var item = _context.TodoItems.Find(id);
            if (item != null)
            {
                _context.TodoItems.Remove(item);
                _context.SaveChanges();
            }
        }
        public IEnumerable<TodoItem> GetAll()
        {
            return _context.TodoItems.ToList();
        }
        public TodoItem GetById(int id)
        {
            return _context.TodoItems.Find(id);
        }
        public void Update(TodoItem item)
        {
            _context.Entry(item).State = EntityState.Modified;
            _context.SaveChanges();
        }
    }
}

注册仓储和控制器

  • 注册仓储:在 Program.cs 中注册仓储接口和实现。
// Program.cs
// ... 在 AddDbContext 之后添加
builder.Services.AddScoped<ITodoRepository, TodoRepository>();
  • 创建控制器:创建一个新的 TodoController (或者修改旧的),这次使用 ITodoRepository
// Controllers/TodoController.cs (EF Core 版本)
using Microsoft.AspNetCore.Mvc;
using TodoApp.Models;
using TodoApp.Repositories;
namespace TodoApp.Controllers
{
    public class TodoController : Controller
    {
        private readonly ITodoRepository _repository;
        public TodoController(ITodoRepository repository)
        {
            _repository = repository;
        }
        public IActionResult Index()
        {
            var todos = _repository.GetAll();
            return View(todos);
        }
        public IActionResult Create()
        {
            return View();
        }
        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Create(TodoItem item)
        {
            if (ModelState.IsValid)
            {
                _repository.Add(item);
                return RedirectToAction(nameof(Index));
            }
            return View(item);
        }
        public IActionResult Edit(int id)
        {
            var item = _repository.GetById(id);
            if (item == null)
            {
                return NotFound();
            }
            return View(item);
        }
        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Edit(int id, TodoItem item)
        {
            if (id != item.Id)
            {
                return NotFound();
            }
            if (ModelState.IsValid)
            {
                _repository.Update(item);
                return RedirectToAction(nameof(Index));
            }
            return View(item);
        }
        public IActionResult Delete(int id)
        {
            var item = _repository.GetById(id);
            if (item == null)
            {
                return NotFound();
            }
            return View(item);
        }
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public IActionResult DeleteConfirmed(int id)
        {
            _repository.Delete(id);
            return RedirectToAction(nameof(Index));
        }
    }
}

创建视图

视图的代码与 ADO.NET 方案中的视图 完全相同!因为它们操作的都是 TodoItem 模型。

运行项目,你会发现功能与 ADO.NET 版本一模一样,但代码量更少,结构更清晰。


第 4 部分:进阶主题与最佳实践

  1. 异步操作:在 Web 应用中,所有 I/O 操作(如数据库访问)都应该是异步的,以避免阻塞线程,提高并发性能。

    • 在 EF Core 中,将 SaveChanges() 改为 SaveChangesAsync()
    • 在 ADO.NET 中,使用 ExecuteReaderAsync(), ExecuteNonQueryAsync() 等。
    • 在控制器中,将 public IActionResult Index() 改为 public async Task<IActionResult> Index()
  2. 依赖注入:我们已经使用了依赖注入,这是现代 .NET 应用的基石,它使代码更易于测试和维护。

  3. 仓储模式 vs. 直接在 Controller 中使用 DbContext

    • 对于小型应用,可以直接在 Controller 中注入 DbContext
    • 对于中大型应用,仓储模式可以更好地解耦业务逻辑和数据访问逻辑,使代码结构更清晰。
  4. DTO (Data Transfer Objects):不要直接将你的 Entity 模型暴露给 API 或视图,创建专门的 DTO 类,只包含需要传输的字段,这可以防止意外数据泄露,并允许你灵活地定义 API 契约。

  5. 单元测试:使用仓储模式和依赖注入,可以轻松地为你的服务层和控制器编写单元测试,而无需连接真实的数据库。


特性 ADO.NET Entity Framework Core
抽象级别 低级,直接控制 高级,ORM 封装
性能 最高,无额外开销 略低,但通常可接受
开发效率 较低,代码繁琐 非常高,代码简洁
控制力 完全控制 SQL 和连接 通过 LINQ 和配置控制
数据库迁移 手动编写和管理 自动化,非常方便
适用场景 性能要求极高的场景、复杂存储过程、遗留系统维护 绝大多数现代 Web 应用、快速开发、跨数据库应用

给初学者的建议直接从 Entity Framework Core 开始学习,它能让你更专注于业务逻辑的实现,而不是底层数据库连接的细节,当你对 .NET 和数据库操作有了更深入的理解后,再去了解 ADO.NET 会更有帮助。

分享:
扫描分享到社交APP
上一篇
下一篇