无私分享:从入门到精通ASP.NET MVC | 从0开始,一起搭框架、做项目(14)附数据库、发布项目
ASP.NET MVC 是微软推出的基于 Model-View-Controller(模型-视图-控制器)架构的 Web 开发框架,以其松耦合、可测试性强、易于扩展等特性,成为 .NET 生态中构建企业级 Web 应用的首选技术之一。无论你是刚接触 Web 开发的新手,还是有一定经验想系统学习 MVC 的开发者,本文都将带你从 0 开始,逐步掌握 ASP.NET MVC 的核心概念、框架搭建、数据库集成、项目实战及部署全流程。
本文是系列教程的第 14 篇,将聚焦数据库设计与集成和项目发布两大核心环节,结合一个实际案例(简易博客系统),带你完成从代码编写到上线部署的全流程。我们会使用 Entity Framework (EF) Code-First 进行数据库开发,采用 IIS 或 Azure 进行部署,并穿插最佳实践与避坑指南。
目录#
- 环境准备与项目回顾
- 数据库设计与 Entity Framework 集成
- 2.1 数据模型设计
- 2.2 EF Code-First 配置
- 2.3 数据库迁移与初始化
- 核心业务功能实现(CRUD 操作)
- 3.1 控制器与视图生成(Scaffolding)
- 3.2 数据验证与错误处理
- 3.3 业务逻辑封装(Service 层)
- 身份认证与授权
- 4.1 ASP.NET Identity 集成
- 4.2 角色与权限控制
- 项目测试与优化
- 5.1 单元测试(xUnit)
- 5.2 性能优化(缓存、异步操作)
- 项目发布与部署
- 6.1 发布到本地 IIS
- 6.2 发布到 Azure App Service
- 6.3 数据库部署与连接字符串配置
- 总结与最佳实践
- 参考资料
1. 环境准备与项目回顾#
1.1 开发环境#
确保已安装以下工具:
- Visual Studio 2022(社区版免费,推荐安装 "ASP.NET 和 Web 开发" 工作负载)
- .NET Framework 4.8 或 .NET 6/7(本文以 .NET 6 为例)
- SQL Server LocalDB(Visual Studio 默认集成,用于本地开发)
- Git(可选,用于版本控制)
1.2 项目回顾#
在之前的教程中,我们已创建了一个名为 BlogSystem 的 ASP.NET MVC 项目,初步搭建了目录结构(Models、Views、Controllers 等),并实现了基础页面路由。本文将在此基础上集成数据库,实现博客文章的发布、编辑、删除等功能,并最终部署上线。
2. 数据库设计与 Entity Framework 集成#
2.1 数据模型设计#
首先,我们需要设计核心数据模型。以博客系统为例,至少包含以下实体:
- Post(文章):存储博客内容,包含标题、正文、发布时间、作者等字段。
- Category(分类):文章分类,如 "技术"、"生活" 等。
- Comment(评论):用户对文章的评论。
示例模型代码(Models/Post.cs):
using System.ComponentModel.DataAnnotations;
namespace BlogSystem.Models
{
public class Post
{
// 主键
public int Id { get; set; }
[Required(ErrorMessage = "标题不能为空")]
[StringLength(100, ErrorMessage = "标题长度不能超过 100 字符")]
public string Title { get; set; }
[Required(ErrorMessage = "内容不能为空")]
[DataType(DataType.MultilineText)] // 多行文本框
public string Content { get; set; }
[Display(Name = "发布时间")]
[DataType(DataType.DateTime)]
public DateTime PublishTime { get; set; } = DateTime.Now; // 默认当前时间
// 外键:关联分类
public int CategoryId { get; set; }
public Category Category { get; set; } // 导航属性
// 外键:关联作者(后续集成 Identity 后使用)
public string AuthorId { get; set; }
}
}Category 模型(Models/Category.cs):
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace BlogSystem.Models
{
public class Category
{
public int Id { get; set; }
[Required(ErrorMessage = "分类名称不能为空")]
[StringLength(50, ErrorMessage = "分类名称长度不能超过 50 字符")]
public string Name { get; set; }
// 导航属性:一个分类包含多篇文章
public ICollection<Post> Posts { get; set; } = new List<Post>();
}
}2.2 EF Code-First 配置#
Entity Framework (EF) 是 .NET 中常用的 ORM(对象关系映射)工具,支持 Code-First(代码优先)模式,即通过代码定义模型,自动生成数据库表结构。
步骤 1:安装 EF 包#
在 Visual Studio 的 NuGet 包管理器控制台中执行以下命令:
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Tools步骤 2:创建 DbContext#
DbContext 是 EF 的核心类,负责管理数据库连接和实体操作。创建 Data/BlogContext.cs:
using Microsoft.EntityFrameworkCore;
using BlogSystem.Models;
namespace BlogSystem.Data
{
public class BlogContext : DbContext
{
public BlogContext(DbContextOptions<BlogContext> options) : base(options)
{
}
// 定义实体集合(对应数据库表)
public DbSet<Post> Posts { get; set; }
public DbSet<Category> Categories { get; set; }
// 可选:配置模型关系(也可通过数据注解实现)
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// 配置 Post 与 Category 的一对多关系
modelBuilder.Entity<Post>()
.HasOne(p => p.Category)
.WithMany(c => c.Posts)
.HasForeignKey(p => p.CategoryId);
}
}
}步骤 3:配置数据库连接字符串#
在 appsettings.json 中添加数据库连接字符串(使用 LocalDB):
{
"ConnectionStrings": {
"BlogContext": "Server=(localdb)\\mssqllocaldb;Database=BlogSystemDB;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}在 Program.cs 中注册 BlogContext:
var builder = WebApplication.CreateBuilder(args);
// 添加数据库上下文
builder.Services.AddDbContext<BlogContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("BlogContext")));
// 其他服务注册(MVC、Identity 等)
builder.Services.AddControllersWithViews();
var app = builder.Build();
// ... 中间件配置 ...2.3 数据库迁移与初始化#
步骤 1:创建迁移#
迁移(Migration)是 EF 用于同步模型与数据库结构的工具。在 NuGet 包管理器控制台执行:
Add-Migration InitialCreate这会在项目中生成一个迁移文件(如 Migrations/20231001000000_InitialCreate.cs),包含创建表的 SQL 逻辑。
步骤 2:应用迁移(生成数据库)#
执行以下命令将迁移应用到数据库:
Update-DatabaseEF 会自动创建数据库(BlogSystemDB)和表(Posts、Categories)。
步骤 3:初始化测试数据(可选)#
为方便开发,可通过 SeedData 类初始化测试数据。创建 Data/SeedData.cs:
using BlogSystem.Models;
namespace BlogSystem.Data
{
public static class SeedData
{
public static void Initialize(IServiceProvider serviceProvider)
{
var context = new BlogContext(
serviceProvider.GetRequiredService<DbContextOptions<BlogContext>>());
// 如果数据库已存在数据,则不初始化
if (context.Categories.Any())
{
return;
}
// 添加测试分类
context.Categories.AddRange(
new Category { Name = "技术" },
new Category { Name = "生活" },
new Category { Name = "学习" }
);
// 添加测试文章
context.Posts.AddRange(
new Post
{
Title = "ASP.NET MVC 入门教程",
Content = "本文介绍 ASP.NET MVC 的基本概念...",
CategoryId = 1,
PublishTime = DateTime.Now.AddDays(-10)
},
new Post
{
Title = "Entity Framework Code-First 实践",
Content = "Code-First 模式让数据库设计更灵活...",
CategoryId = 1,
PublishTime = DateTime.Now.AddDays(-5)
}
);
context.SaveChanges();
}
}
}在 Program.cs 中调用初始化方法:
var app = builder.Build();
// 初始化测试数据
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
SeedData.Initialize(services);
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "初始化数据时出错");
}
}
// ... 其他中间件 ...3. 核心业务功能实现(CRUD 操作)#
3.1 控制器与视图生成(Scaffolding)#
ASP.NET MVC 提供了脚手架(Scaffolding) 工具,可快速生成 CRUD 操作的控制器和视图。
步骤 1:生成 Post 控制器#
- 在项目的
Controllers文件夹上右键 → 添加 → 控制器 → 选择 "MVC 5 控制器 - 使用 Entity Framework"。 - 模型类 选择
Post (BlogSystem.Models),数据上下文类 选择BlogContext (BlogSystem.Data),点击 添加。
生成的 PostsController 包含 Index(列表)、Details(详情)、Create(创建)、Edit(编辑)、Delete(删除)等 Action,以及对应的视图。
步骤 2:修改视图(以 Index 为例)#
默认生成的视图可能不够美观,可通过 Bootstrap 优化。例如 Views/Posts/Index.cshtml:
@model IEnumerable<BlogSystem.Models.Post>
@{
ViewData["Title"] = "文章列表";
}
<h2>文章列表</h2>
<p>
<a asp-action="Create" class="btn btn-primary">发布新文章</a>
</p>
<table class="table">
<thead>
<tr>
<th>标题</th>
<th>分类</th>
<th>发布时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>@Html.DisplayFor(modelItem => item.Title)</td>
<td>@Html.DisplayFor(modelItem => item.Category.Name)</td>
<td>@Html.DisplayFor(modelItem => item.PublishTime)</td>
<td>
<a asp-action="Details" asp-route-id="@item.Id" class="btn btn-info btn-sm">查看</a>
<a asp-action="Edit" asp-route-id="@item.Id" class="btn btn-warning btn-sm">编辑</a>
<a asp-action="Delete" asp-route-id="@item.Id" class="btn btn-danger btn-sm">删除</a>
</td>
</tr>
}
</tbody>
</table>3.2 数据验证与错误处理#
利用 DataAnnotations 实现模型验证(如 [Required]、[StringLength]),并在视图中显示错误信息。
在 Create 视图中添加验证提示(Views/Posts/Create.cshtml):
<div class="form-group">
@Html.LabelFor(model => model.Title, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Title, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Title, "", new { @class = "text-danger" })
</div>
</div>控制器中处理验证(PostsController/Create):
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Title,Content,CategoryId")] Post post)
{
if (ModelState.IsValid)
{
post.PublishTime = DateTime.Now; // 覆盖默认时间(确保是提交时的时间)
_context.Add(post);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["CategoryId"] = new SelectList(_context.Categories, "Id", "Name", post.CategoryId);
return View(post);
}3.3 业务逻辑封装(Service 层)#
为避免控制器臃肿,建议将业务逻辑封装到 Service 层。例如创建 Services/IPostService.cs 和 Services/PostService.cs:
// IPostService.cs
using BlogSystem.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace BlogSystem.Services
{
public interface IPostService
{
Task<IEnumerable<Post>> GetAllPostsAsync();
Task<Post> GetPostByIdAsync(int id);
Task CreatePostAsync(Post post);
Task UpdatePostAsync(Post post);
Task DeletePostAsync(int id);
}
}
// PostService.cs
using BlogSystem.Data;
using BlogSystem.Models;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace BlogSystem.Services
{
public class PostService : IPostService
{
private readonly BlogContext _context;
public PostService(BlogContext context)
{
_context = context;
}
public async Task<IEnumerable<Post>> GetAllPostsAsync()
{
return await _context.Posts.Include(p => p.Category).ToListAsync();
}
// 其他方法实现...
}
}在 Program.cs 中注册服务:
builder.Services.AddScoped<IPostService, PostService>();控制器中通过依赖注入使用 Service:
private readonly IPostService _postService;
public PostsController(IPostService postService)
{
_postService = postService;
}
public async Task<IActionResult> Index()
{
return View(await _postService.GetAllPostsAsync());
}4. 身份认证与授权#
4.1 ASP.NET Identity 集成#
ASP.NET Identity 是用于用户认证和授权的框架,支持注册、登录、角色管理等功能。
步骤 1:安装 Identity 包#
Install-Package Microsoft.AspNetCore.Identity.EntityFrameworkCore
Install-Package Microsoft.AspNetCore.Identity.UI步骤 2:创建用户模型与配置#
修改 BlogContext 继承 IdentityDbContext<IdentityUser>:
public class BlogContext : IdentityDbContext<IdentityUser>
{
public BlogContext(DbContextOptions<BlogContext> options) : base(options) { }
public DbSet<Post> Posts { get; set; }
public DbSet<Category> Categories { get; set; }
}在 Program.cs 中添加 Identity 服务:
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<BlogContext>();添加认证中间件(在 app.UseAuthorization() 之前):
app.UseAuthentication();步骤 3:生成 Identity 视图#
右键项目 → 添加 → 新建脚手架项 → 选择 "Identity" → 勾选需要的页面(如 Register、Login),选择 BlogContext,点击 添加。
4.2 角色与权限控制#
通过 [Authorize] 特性限制控制器或 Action 的访问权限:
[Authorize(Roles = "Admin")] // 仅管理员可访问
public class AdminController : Controller
{
// ...
}
[Authorize] // 需登录才能访问
public async Task<IActionResult> Create()
{
ViewData["CategoryId"] = new SelectList(_context.Categories, "Id", "Name");
return View();
}5. 项目测试与优化#
5.1 单元测试(xUnit)#
创建测试项目,测试 Service 层逻辑:
using BlogSystem.Data;
using BlogSystem.Models;
using BlogSystem.Services;
using Microsoft.EntityFrameworkCore;
using Xunit;
namespace BlogSystem.Tests
{
public class PostServiceTests
{
[Fact]
public async Task GetAllPostsAsync_ReturnsAllPosts()
{
// Arrange
var options = new DbContextOptionsBuilder<BlogContext>()
.UseInMemoryDatabase(databaseName: "TestDB")
.Options;
using (var context = new BlogContext(options))
{
context.Posts.AddRange(
new Post { Title = "Test 1", Content = "Content 1", CategoryId = 1 },
new Post { Title = "Test 2", Content = "Content 2", CategoryId = 1 }
);
context.SaveChanges();
}
// Act
using (var context = new BlogContext(options))
{
var service = new PostService(context);
var result = await service.GetAllPostsAsync();
// Assert
Assert.Equal(2, result.Count());
}
}
}
}5.2 性能优化#
- 异步操作:控制器和 Service 层使用
async/await避免阻塞线程。 - 缓存:对分类列表等不频繁变化的数据使用
IDistributedCache。 - 延迟加载:EF 中使用
Include显式加载关联数据,避免 N+1 查询问题。
6. 项目发布与部署#
6.1 发布到本地 IIS#
步骤 1:安装 IIS#
在 控制面板 → 程序 → 启用或关闭 Windows 功能 中勾选 Internet Information Services 及子项(如 ASP.NET 4.8)。
步骤 2:发布项目#
- 在 Visual Studio 中右键项目 → 发布 → 选择 "文件夹" → 指定发布路径(如
C:\inetpub\wwwroot\BlogSystem)。 - 点击 发布,等待生成完成。
步骤 3:配置 IIS 站点#
- 打开 IIS 管理器 → 网站 → 添加网站。
- 站点名称:
BlogSystem,物理路径:发布路径,端口:8080(避免与默认站点冲突)。 - 应用程序池设置为 .NET CLR 版本 v4.0.30319,托管管道模式为 集成。
6.2 发布到 Azure App Service#
- 在 Azure 门户创建 App Service(选择 .NET 6 运行时)。
- Visual Studio 发布目标选择 "Azure" → "Azure App Service (Windows)" → 选择创建的 App Service,点击 发布。
6.3 数据库部署与连接字符串配置#
- 本地 IIS:修改
web.config中的连接字符串,指向生产数据库(如 SQL Server 实例):<connectionStrings> <add name="BlogContext" connectionString="Server=myServer;Database=BlogSystemDB;User Id=myUser;Password=myPwd;" providerName="System.Data.SqlClient" /> </connectionStrings> - Azure:在 App Service 的 配置 → 连接字符串 中添加,Azure 会自动替换配置文件中的值。
7. 总结与最佳实践#
核心总结#
本文从数据库设计、EF 集成、CRUD 实现、身份认证到项目部署,完整覆盖了 ASP.NET MVC 项目开发的核心流程。关键知识点包括:
- EF Code-First 模式:通过代码定义模型,自动生成数据库。
- 分层架构:Service 层封装业务逻辑,解耦控制器与数据访问。
- 身份认证:ASP.NET Identity 实现用户管理与权限控制。
- 部署流程:本地 IIS 与 Azure 部署的关键步骤。
最佳实践#
- 模型验证:始终使用
DataAnnotations或 Fluent API 进行数据验证,避免脏数据。 - 依赖注入:通过 DI 容器管理服务(如
BlogContext、IPostService),提高可测试性。 - 异常处理:全局异常过滤器(
IExceptionFilter)统一处理错误,避免向用户暴露敏感信息。 - 安全防护:使用
[ValidateAntiForgeryToken]防止 CSRF 攻击,对用户输入进行转义(MVC 默认已处理)。 - 版本控制:使用 Git 管理代码,避免直接修改生产环境代码。
8. 参考资料#
通过本文的学习,你已掌握 ASP.NET MVC 从框架搭建到项目上线的全流程。后续可深入学习高级主题,如缓存策略、API 开发、微服务集成等。祝你的 ASP.NET MVC 之旅顺利!