本篇記錄實際開發中根據需求調整的實體模型,展示 Entity Framework Code First 的實體類別設計、資料註解驗證屬性應用,以及 ViewModel 與 Entity 的區分策略。
public class AlumniBookContext : DbContext
{
public AlumniBookContext() : base("ABConnection") { }
public DbSet<Admin> Admins { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<UserInfo> UserInfos { get; set; }
public DbSet<Class> Classes { get; set; }
public DbSet<School> Schools { get; set; }
public DbSet<Album> Albums { get; set; }
public DbSet<Photo> Photos { get; set; }
public DbSet<Message> Messages { get; set; }
public DbSet<Comment> Comments { get; set; }
}
[Table("Users")]
public class User
{
[Key]
[StringLength(50)]
public string UserName { get; set; } // 主鍵,天然唯一
[Required]
[StringLength(100)]
public string Password { get; set; } // 儲存 MD5 雜湊值
public bool IsAdmin { get; set; }
public DateTime CreateTime { get; set; }
// 導航屬性
public virtual UserInfo UserInfo { get; set; }
public virtual ICollection<Class> JoinedClasses { get; set; }
}
[Table("UserInfos")]
public class UserInfo
{
[Key]
[StringLength(50)]
public string UserName { get; set; } // 與 User 共用主鍵,一對一
[Required]
[StringLength(20)]
public string NickName { get; set; }
[DataType(DataType.Date)]
public DateTime? Birthday { get; set; } // 可為 null
[Required]
public bool Sex { get; set; } // true: 男, false: 女
[Required]
[EmailAddress]
[StringLength(100)]
public string Email { get; set; }
[StringLength(200)]
public string AvatarPath { get; set; }
// 導航屬性
public virtual User User { get; set; }
}
設計考量: 將密碼置於 `User` 表,登入驗證時只需查詢單表;`UserInfo` 置於獨立表,支援延遲載入,減少列表查詢負載。
[Table("Classes")]
public class Class
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ClassId { get; set; }
[Required]
[StringLength(100)]
public string ClassName { get; set; }
[Required]
[StringLength(100)]
public string SchoolName { get; set; } // 以學校名稱作為外鍵,簡化設計
[StringLength(200)]
public string ClassCoverPath { get; set; }
[Required]
[StringLength(50)]
public string CreatorId { get; set; }
public DateTime CreateTime { get; set; }
// 導航屬性
public virtual ICollection<User> Members { get; set; }
public virtual ICollection<Album> Albums { get; set; }
public virtual ICollection<Message> Messages { get; set; }
}
註冊頁面使用專屬 `RegisterViewModel`,不直接綁定實體,避免過量張貼攻擊:
public class RegisterViewModel
{
[Required(ErrorMessage = "請輸入使用者名稱")]
[StringLength(10, MinimumLength = 3, ErrorMessage = "長度 3-10 字元")]
[Display(Name = "使用者名稱")]
public string UserName { get; set; }
[Required]
[StringLength(20, MinimumLength = 3, ErrorMessage = "暱稱長度 3-20 字元")]
[Display(Name = "暱稱")]
public string NickName { get; set; }
[Required]
[StringLength(100, MinimumLength = 6, ErrorMessage = "密碼至少 6 字元")]
[DataType(DataType.Password)]
[Display(Name = "密碼")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "確認密碼")]
[Compare("Password", ErrorMessage = "兩次密碼不一致")]
public string ConfirmPassword { get; set; }
[DataType(DataType.Date)]
[Display(Name = "生日")]
public DateTime Birthday { get; set; }
[Required]
[Display(Name = "性別")]
public bool Sex { get; set; }
[Required]
[EmailAddress(ErrorMessage = "Email 格式不正確")]
[Display(Name = "Email")]
public string Email { get; set; }
}
<div class="form-group">
@Html.LabelFor(m => m.UserName, new { @class = "control-label" })
@Html.TextBoxFor(m => m.UserName, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.UserName, "", new { @class = "text-danger" })
</div>





實體模型是系統骨架,合理分離認證與業務資料、善用資料註解驗證、區分 Entity 與 ViewModel,能讓後續開發事半功倍。下一篇將回顧模型設計演進歷程與踩坑經驗。