C# EF Core 实体属性未映射生成数据库列的问题咨询
问题产生原因
列未生成是三个核心问题导致的:
- EF Core默认映射规则只识别带公开get、set访问器的可读写属性,你写的PlayerX、PlayerO、Mode、Winner、Loser、Status全是只有get的计算属性,没有setter,EF Core默认把这些属性归为运行时计算的临时逻辑,不纳入持久化范围,自然不会生成对应列。
- 声明Mode和Status属性时,返回值用的是基类
Enum,不是代码里定义的具体枚举类型GameMode、GameStatus,EF Core无法识别这个通用基类该怎么映射成数据库字段,就算加了setter也会映射失败。 - 现有GameScores实体设计本身不符合EF使用规范:实体是纯数据载体,你在内部直接new了MainWindowViewModel,还依赖运行时的CurrentGame状态计算属性值,后续从数据库读取历史记录时,这些属性找不到对应的游戏实例,会直接抛出空引用错误,根本无法正常使用。
修复方案
按以下步骤修改即可:
- 先把GameScores重构为纯数据模型,删掉所有对ViewModel、游戏逻辑类的依赖,所有需要存库的属性都加上setter,枚举属性使用具体的枚举类型,不要用Enum基类:
public class GameScores { // 主键为数据库自增字段,不需要声明为nullable public int GameScoreId { get; set; } public string PlayerX { get; set; } = string.Empty; public string PlayerO { get; set; } = string.Empty; // 用具体枚举类型代替Enum基类 public GameMode Mode { get; set; } public string? Winner { get; set; } public string? Loser { get; set; } // 用具体枚举类型代替Enum基类 public GameStatus Status { get; set; } }
- 原来写在实体里的属性计算逻辑,挪到游戏结束触发保存的业务代码位置(也就是MainWindowViewModel里处理游戏结束的方法中),手动给GameScores对象赋值后再存入数据库,参考代码:
// 游戏结束判定通过后,调用该方法存储对局记录 private void SaveGameResult() { if (CurrentGame == null || !CurrentGame.GameOver) return; var record = new GameScores { PlayerX = CurrentGame.PlayerX.Name, PlayerO = CurrentGame.PlayerO.Name, Mode = CurrentGame.PlayerO switch { HumanPlayer => GameMode.HumanVsHuman, EasyCom => GameMode.HumanVsEasyBot, HardCom => GameMode.HumanVsHardBot, _ => throw new NotImplementedException() }, Winner = CurrentGame.GetFreeFields().Count != 0 ? CurrentGame.CurrentPlayer!.Name : null, Loser = CurrentGame.GetFreeFields().Count != 0 ? CurrentGame.CurrentPlayer!.Enemy!.Name : null, Status = CurrentGame.GetFreeFields().Count != 0 ? GameStatus.Win : GameStatus.Draw }; using var db = new GameScoresContext(); db.GameScores!.Add(record); db.SaveChanges(); }
- (可选配置)如果想让数据库存储枚举的字符串值而不是数字,避免后续调整枚举顺序导致历史数据错乱,可以在DbContext的OnModelCreating方法中添加转换配置:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<GameScores>().HasKey(e => e.GameScoreId); // 配置枚举转换为字符串存储 modelBuilder.Entity<GameScores>().Property(e => e.Mode).HasConversion<string>(); modelBuilder.Entity<GameScores>().Property(e => e.Status).HasConversion<string>(); }
- 删掉之前生成的错误迁移文件夹,重新在NuGet包管理器控制台执行两条命令:
Add-Migration FixGameScoreTableColumnsUpdate-Database
注意:EF实体类不要耦合任何UI、业务逻辑依赖,它的作用只是和数据库表做字段映射,所有字段值的计算、赋值都要在业务层完成,否则会出现各种映射失败、运行时报错的问题。
内容的提问来源于stack exchange,提问作者44ay_
相关产品推荐
相关产品推荐

