ASP.NET MVC学习笔记|代码与日常的交织
一点开场
写这篇文章的时候,我其实心情挺复杂的。上个学期学过 ASP.NET 的基础知识,那时候更多是“了解”,也就是会写点 C# 的 WebForm,能连个数据库,能写个小表单。但这学期开始,老师说我们要系统学习 ASP.NET MVC架构,我突然觉得“噢,这下要上正菜了”。
MVC 这个概念,之前也听过,只是模模糊糊知道它是“分层、解耦、让代码更清晰”的一种模式。而这次一上来,老师就顺带抛出一个更“文艺”的词:MVVM(Model - View - ViewModel)。当时我愣了半天,心想:我是不是走错片场了?明明是 MVC 课,结果学的却是 MVVM?
不过也挺好。带着一点懵,反而让我更认真地去琢磨:为什么要分这些层?为什么写代码的时候要有这么多角色?是不是光一个控制器就能解决问题?
答案很简单:当你项目越写越大,你就会知道“结构”有多重要。
课堂上的小插曲
当然,所谓“认真学习”,也得打个引号。因为我上课时经常会——跑神。 比如老师在讲 LINQ 的时候,我打开 Visual Studio,开始写一个查询:
var result = from s in students
where s.Score > 80
select s.Name;
写到一半,我突然想: “欸,中午要不要去楼下吃个炒饭?” 🤔 “要不要把昨天那个 C# 小工具优化一下?” “还有那首歌的旋律怎么一直在脑子里绕来绕去?”
等我回过神来,光标还在闪,代码还没写完,我完全忘了自己要实现的逻辑……只能尴尬地傻笑。
这种事发生得太频繁了,甚至让我养成了一个习惯:写代码前最好先写个草稿,把逻辑写在纸上。这样即使走神了,回来一看纸上的流程图,也能重新找回轨道。要不然,代码写着写着就变成“散文诗”了。
MVC 与 MVVM 的第一印象
既然是学习笔记,那还是得回到主题。
MVC:
- Model: 模型层,管数据和业务逻辑。
- View:视图层,管页面展示。
- Controller:控制器,负责调度、把 Model 和 View 联系起来。
而 MVVM 在此基础上,多了一个 ViewModel,本质上就是让数据和视图可以双向绑定,更适合那种交互很多的场景。
我一开始不太能理解,后来写了几个小例子才明白:
- 如果用 MVC,我要在控制器里拼命写逻辑,最后再传数据给视图。
- 如果用 MVVM,我直接让 ViewModel 帮我把数据和页面绑定起来,省事。
就像炒菜一样,MVC 是“你要自己切菜、炒菜、上桌”,MVVM 是“有人帮你切好菜,甚至把盘子都端上来了”,你只管享用。
LINQ 的世界
接着讲讲 LINQ。这个东西我在学数据库和 C# 的时候就接触过。并且,之前使用 WPF 桌面应用程序开发扫雷程序时,使用过数据库,所以对此也不算太陌生,只是这次算是“系统复习”。
LINQ(Language Integrated Query)让我感受到 C# 的优雅。(哈哈哈哈,其实这个优雅最开始学习 C# 时,课本上就将其作为一个特点描述,刚开始很疑惑,但是 C# 代码写多了之后就知道为什么了,尤其是写 LINQ)它不像传统 SQL 那样生硬,反而是把查询写进了语言本身,就像写英文句子一样自然。
示例:
- 查询学生分数大于 80 的人
var result = from s in students where s.Score > 80 select s.Name;
- 排序
var result = from s in students orderby s.Score descending select s;
- 分组
var result = from s in students group s by s.Class into g select new { Class = g.Key, Count = g.Count() };
这些语法在第一次见时,会觉得很神奇:“原来 C# 还能这样查数据?”
后来我才发现,这就是抽象的力量。LINQ 让我们不必关心底层是数据库、集合还是 XML 文件,只要写一样的语法,就能查数据。
不过说实话,刚开始写的时候,我经常被 Lambda 表达式绕晕。
Lambda 表达式与扩展方法
Lambda 的写法简直就是“箭头流”。一开始看着很懵:
students.Where(s => s.Score > 80).Select(s => s.Name);
其实意思和 LINQ 查询语法差不多,但写法更紧凑。 老师说:“这个就是函数式编程的一种体现。” 我当时在心里默默吐槽:“函数式、面向对象、事件驱动……感觉编程就是个名词收集器。”
不过慢慢写多了,发现 Lambda 真的很好用,特别是配合扩展方法。
自定义扩展方法示例
public static class StringExtensions
{
public static bool IsLongerThan(this string str, int length)
{
return str.Length > length;
}
}
// 使用
string text = "Hello World";
bool result = text.IsLongerThan(5); // true
这种写法让我很有成就感:仿佛自己在给 .NET 库“偷偷加功能”。
又一次走神
写到扩展方法的时候,我又想到一个趣事。 有次写扩展方法,我把方法名起得特别奇怪,比如:
text.IsSuperDuperAwesomeLongerThan(10);
写完自己忍不住笑出声,结果同桌投来一个“你在干嘛”的眼神。 那一瞬间我才意识到,编程不仅仅是技术,还是一种“表达欲”。你写的代码,其实就是你在和未来的自己对话。
有时候,我会故意在注释里写点无关紧要的废话,(其实经常在网络上刷到过关于程序员为何写这么多“废话”,哈哈哈哈,现在知道了)比如:
// 这行代码可能以后会救你一命(别删!!!)
结果等到几周后真的看不懂自己写的逻辑时,那句注释还真成了救命稻草。
学习 MVC 的小结
总的来说,这一周的学习让我慢慢体会到 MVC 的美妙:
- 它让代码更清晰,不会一团乱麻。
- 它让逻辑和展示分开,不至于一个文件里堆满了 SQL、HTML、C#。
- 它让我明白,软件架构其实就是“把复杂的事拆开,让人脑能看懂”。
而在学习的过程中,走神、跑偏、甚至写出奇怪代码,反而成了让我记忆更深的点。也许这就是所谓的“日常与学习的交织”。
写在最后
写完这篇笔记,其实我也没觉得自己把 MVC 学得多透彻,更多的是一种“边走边看”的感觉。就像上课的时候,我常常写着写着代码就发呆,想着午饭要吃啥🍜,想着昨天的 bug 还没改,想着生活里的乱七八糟。等回过神来,屏幕上的光标还在闪,我才发现:噢,原来学习也不一定要很正经,它可以夹杂着一点小走神、一点碎碎念。
所以这篇文章,算不上什么专业的教程,更像是我的一份学习日记。希望几年后再回头看,还能记得自己当初一边敲代码、一边开小差的样子。那应该会挺好笑,挺怀念的吧。
附录:SQL 数据库内容
/* ===== 0) 可选:创建并切换到数据库 ===== */
IF DB_ID(N'Demo') IS NULL
CREATE DATABASE Demo;
GO
USE Demo;
GO
/* 为了可重复执行,先安全删除旧表(有外键的顺序要注意) */
IF OBJECT_ID(N'dbo.sc', N'U') IS NOT NULL DROP TABLE dbo.sc;
IF OBJECT_ID(N'dbo.course', N'U') IS NOT NULL DROP TABLE dbo.course;
IF OBJECT_ID(N'dbo.student', N'U') IS NOT NULL DROP TABLE dbo.student;
GO
/* ===== 1) student 表 =====
字段:sno(PK), sname, sex, age, dept */
CREATE TABLE dbo.student
(
sno INT NOT NULL, -- 学号
sname NVARCHAR(20) NOT NULL, -- 姓名(支持中文)
sex NCHAR(1) NULL, -- 性别:可存 '男' / '女'
age TINYINT NULL, -- 年龄:0~255;见下方 CHECK
dept NVARCHAR(20) NULL, -- 部门/院系
CONSTRAINT PK_student PRIMARY KEY (sno),
CONSTRAINT CK_student_age CHECK (age IS NULL OR age BETWEEN 0 AND 120),
CONSTRAINT CK_student_sex CHECK (sex IS NULL OR sex IN (N'男', N'女'))
);
GO
/* ===== 2) course 表 =====
字段:cno(PK), cname, tname, credit */
CREATE TABLE dbo.course
(
cno INT NOT NULL, -- 课程号
cname NVARCHAR(20) NOT NULL, -- 课程名
tname NVARCHAR(20) NOT NULL, -- 教师姓名
credit TINYINT NOT NULL, -- 学分(无符号整数语义)
CONSTRAINT PK_course PRIMARY KEY (cno),
CONSTRAINT CK_course_credit CHECK (credit BETWEEN 0 AND 30) -- 视校规调整
);
GO
/* ===== 3) sc 表(选课/成绩)=====
字段:sno(FK→student), cno(FK→course), grade
常见做法:以 (sno, cno) 作为复合主键 */
CREATE TABLE dbo.sc
(
sno INT NOT NULL, -- 学号 → student.sno
cno INT NOT NULL, -- 课号 → course.cno
grade INT NOT NULL, -- 成绩(如需小数可改 DECIMAL(5,2))
CONSTRAINT PK_sc PRIMARY KEY (sno, cno),
CONSTRAINT FK_sc_student FOREIGN KEY (sno) REFERENCES dbo.student(sno)
ON UPDATE NO ACTION ON DELETE CASCADE, -- 学生删除时级联删除选课记录(按需)
CONSTRAINT FK_sc_course FOREIGN KEY (cno) REFERENCES dbo.course(cno)
ON UPDATE NO ACTION ON DELETE CASCADE,
CONSTRAINT CK_sc_grade CHECK (grade BETWEEN 0 AND 100) -- 视评分制调整
);
GO
INSERT dbo.student(sno, sname, sex, age, dept) VALUES
(10001, N'张三', N'男', 19, N'计算机系'),
(10002, N'李四', N'女', 20, N'软件学院');
INSERT dbo.course(cno, cname, tname, credit) VALUES
(1, N'数据库', N'王老师', 3),
(2, N'操作系统', N'赵老师', 4);
INSERT dbo.sc(sno, cno, grade) VALUES
(10001, 1, 86),
(10001, 2, 90),
(10002, 1, 88);
/* ===== 4)(可选)示例数据,验证外键与约束 ===== */
/* 学生表 */
INSERT dbo.student(sno, sname, sex, age, dept) VALUES
(10003, N'王五', N'男', 21, N'计算机系'),
(10004, N'赵六', N'女', 18, N'信息管理系'),
(10005, N'钱七', N'男', 22, N'软件学院'),
(10006, N'孙八', N'女', 20, N'通信工程系');
/* 课程表 */
INSERT dbo.course(cno, cname, tname, credit) VALUES
(3, N'数据结构', N'刘老师', 3),
(4, N'C语言程序设计', N'陈老师', 4),
(5, N'计算机网络', N'周老师', 3);
/* 选课表 */
INSERT dbo.sc(sno, cno, grade) VALUES
(10003, 3, 75),
(10003, 4, 82),
(10004, 2, 91),
(10004, 5, 87),
(10005, 1, 79),
(10005, 3, 85),
(10006, 4, 92),
(10006, 5, 88);
实训报告代码
下面的代码来自《基于 LINQ 数据模型的学生管理系统》实验报告,主要功能包括学生的增删改查。和课堂笔记里的内容互相呼应,算是一次课后的完整实战记录。
#region 按性别查询
string xingbie = "女";
var stu3 = dataClasses1DataContext1.students
.Where(s => s.sex == xingbie)
.Select(s => s.sname.ToUpper());
Console.WriteLine("女学生:");
foreach (var e in stu3)
{
Console.WriteLine(e);
}
#endregion
#region 按年龄查询
int nianling = 20;
var stu2 = dataClasses1DataContext1.students
.Where(s => s.age >= nianling)
.Select(s => s.sname.ToUpper());
Console.WriteLine("年龄大于20岁的学生:");
foreach (var e in stu2)
{
Console.WriteLine(e);
}
#endregion
#region 插入学生
student stu = new student
{
sno = 10007,
sname = "小明",
sex = "男",
age = 19,
dept = "软件学院"
};
dataClasses1DataContext1.students.InsertOnSubmit(stu);
dataClasses1DataContext1.SubmitChanges();
Console.WriteLine("插入学生成功!");
#endregion
#region 删除学生
var deleteStu = dataClasses1DataContext1.students
.FirstOrDefault(s => s.sno == 10007);
if (deleteStu != null)
{
dataClasses1DataContext1.students.DeleteOnSubmit(deleteStu);
dataClasses1DataContext1.SubmitChanges();
Console.WriteLine("删除学生成功!");
}
#endregion
#region 修改学生信息
var updateStu = dataClasses1DataContext1.students
.FirstOrDefault(s => s.sno == 10001);
if (updateStu != null)
{
updateStu.sname = "张三丰";
dataClasses1DataContext1.SubmitChanges();
Console.WriteLine("修改学生信息成功!");
}
#endregion