承接前文,本篇深入探討 Entity Framework Code First 的資料庫遷移策略,並完整呈現班級模組的核心功能:使用者登入驗證(含 MD5 雜湊)、Cookie 記住我、班級建立與圖片上傳、AJAX 動態新增學校。
@model AlumniBook.Models.LoginViewModel
@using (Html.BeginForm("ToLogin", "Log"))
{
@Html.AntiForgeryToken()
@Html.ValidationSummary(true)
<div class="login-form">
@Html.TextBoxFor(m => m.UserName, new { @class = "form-control" })
@Html.PasswordFor(m => m.Password, new { @class = "form-control" })
@Html.CheckBoxFor(m => m.RememberMe) @Html.LabelFor(m => m.RememberMe)
<button type="submit">登入</button>
@if (!string.IsNullOrEmpty(ViewBag.Error))
{ <span class="error">@ViewBag.Error</span> }
</div>
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ToLogin(LoginViewModel model)
{
using (var db = new AlumniBookContext())
{
string hashedPwd = CryptoHelper.GetMD5(model.Password);
var user = db.Users.FirstOrDefault(u =>
u.UserName == model.UserName && u.Password == hashedPwd);
if (user != null)
{
// 登入成功,處理 Cookie
if (model.RememberMe)
{
var cookie = new HttpCookie("UserName", user.UserName)
{
Expires = DateTime.Now.AddDays(30)
};
Response.Cookies.Add(cookie);
}
else
{
// 移除舊 Cookie
if (Request.Cookies["UserName"] != null)
{
var c = new HttpCookie("UserName") { Expires = DateTime.Now.AddDays(-1) };
Response.Cookies.Add(c);
}
}
FormsAuthentication.SetAuthCookie(user.UserName, model.RememberMe);
return RedirectToAction("Index", "Home");
}
}
ViewBag.Error = "帳號或密碼錯誤,請重試";
return View("Login");
}
關鍵點:
[HttpPost]
public ActionResult ToCreate(CreateClassViewModel model)
{
using (var db = new AlumniBookContext())
{
// 檢查班級名稱唯一性
if (db.Classes.Any(c => c.ClassName == model.ClassName))
{
ViewBag.Title = "錯誤";
ViewBag.Message = "班級名稱已存在";
return View("Error");
}
var newClass = new Class
{
ClassName = model.ClassName,
SchoolName = model.SchoolName,
CreatorId = User.Identity.Name,
CreateTime = DateTime.Now
};
// 處理封面圖片上傳
HttpPostedFileBase file = Request.Files[0];
if (file != null && file.ContentLength > 0)
{
newClass.ClassCoverPath = FileHelper.SaveFile(file);
}
db.Classes.Add(newClass);
db.SaveChanges();
return RedirectToAction("Index", new { className = newClass.ClassName });
}
}
<div id="newSchoolBox" title="新增學校" style="display:none;">
<input type="text" id="newSchoolName" placeholder="學校名稱" />
</div>
<script>
$("#createNewSchool").button().click(function() {
$("#newSchoolBox").dialog("open");
});
$("#newSchoolBox").dialog({
autoOpen: false,
width: 400,
height: 180,
modal: true,
buttons: {
"確定": function() {
$.ajax({
type: "POST",
url: "/Class/ToCreateNewSchool",
data: { newSchoolName: $("#newSchoolName").val() },
dataType: "json",
success: function(data) {
if (data.result === true) {
alert("學校新增成功");
location.reload(); // 重新載入以更新下拉選單
} else if (data.result === false) {
alert("學校已存在,請直接選擇");
$("#newSchoolBox").dialog("close");
} else {
alert(data.result);
}
}
});
},
"取消": function() { $(this).dialog("close"); }
}
});
</script>
[HttpPost]
public JsonResult ToCreateNewSchool(string newSchoolName)
{
using (var db = new AlumniBookContext())
{
if (db.Schools.Any(s => s.Name == newSchoolName))
{
return Json(new { result = false });
}
db.Schools.Add(new School { Name = newSchoolName });
db.SaveChanges();
return Json(new { result = true });
}
}
專案中遇到模型變更需更新資料庫時,採用以下流程:
遷移歷程會記錄在 `__MigrationHistory` 資料表,團隊協作時請將遷移檔案納入版控。
本篇完整示範從使用者認證、Cookie 管理、檔案上傳到 AJAX 非同步互動的核心模組實作。下一篇將介紹權限過濾器與 `web.config` 關鍵設定。