如何借助Humanizer实现Code First模型枚举到字符串属性的自动赋值
正确实现方案
方案1:属性set同步逻辑(最小改动适配现有代码)
你之前实现失败的核心诱因基本都是:直接对自动实现属性编写set逻辑,没有维护独立的私有备份字段,导致set递归触发栈溢出,或是缺少必要的命名空间引用导致转换方法调用失效。
改造后的实体代码如下:
// 必须引入的命名空间 using Humanizer; using System.ComponentModel.DataAnnotations.Schema; public class Request : BaseEntity { public string Uuid { get; set; } public string Email { get; set; } // 枚举属性的私有备份字段,避免自动属性的递归问题 private Status _status; [NotMapped] public Status Status { get { // 兜底逻辑:EF查询实体时不会给NotMapped字段赋值,从数据库读取的StatusName反向匹配对应枚举值 if (_status == default && !string.IsNullOrWhiteSpace(StatusName)) { _status = StatusName.DehumanizeTo<Status>(); } return _status; } set { _status = value; // 赋值时自动调用Humanizer转换可读字符串,同步到需要映射数据库的字段 StatusName = _status.Humanize(); } } public string StatusName { get; set; } public string Description { get; set; } }
该方案直接满足赋值时自动同步的需求,不需要改动EF的原有配置。
方案2:EF Core保存拦截器(更解耦、稳定性更高)
如果不想把同步逻辑写在实体属性里,可通过拦截器在实体写入数据库前统一同步字段值,完全规避实体材料化、序列化过程中对set访问器的异常触发问题,适合复杂业务场景:
public class StatusNameSyncInterceptor : SaveChangesInterceptor { public override InterceptionResult<int> SavingChanges( DbContextEventData eventData, InterceptionResult<int> result) { // 捕获所有被修改的Request实体 var changedRequests = eventData.Context.ChangeTracker .Entries<Request>() .Where(entry => entry.State is EntityState.Added or EntityState.Modified); foreach (var entry in changedRequests) { // 保存前自动同步枚举对应的可读字符串到StatusName entry.Entity.StatusName = entry.Entity.Status.Humanize(); } return base.SavingChanges(eventData, result); } }
注册拦截器后即可自动生效,不需要改动原有实体的属性结构。
常见踩坑点
- 禁止在枚举属性的set块中对自身递归赋值,这类写法会直接触发栈溢出导致程序崩溃
- 调用Humanizer的枚举转换方法前,必须引入
Humanizer命名空间,否则会出现编译期找不到方法的问题 - 不需要在set块中手写冗余的枚举遍历、字符串替换逻辑,直接使用Humanizer内置的转换能力即可覆盖绝大多数场景
- 必须考虑EF实体查询的材料化逻辑:NotMapped属性不会被EF自动赋值,缺少兜底逻辑的话,从数据库读出来的实体Status永远是默认值,不符合使用预期
内容的提问来源于stack exchange,提问作者lonelydev101
相关产品推荐
相关产品推荐

