数据库优先自动生成C#实体类属性变更触发逻辑实现方案咨询
解决自动生成分部类属性变更触发方法的最佳方案
这种数据库优先自动生成的实体类真的太让人头疼了——手动改了setter,下次代码一生成直接被覆盖,完全白忙活。你考虑用分部类的思路绝对是对的,下面给你几个最实用的方案,按推荐程度排序:
方案一:利用分部方法(首推,无侵入性)
很多代码生成工具(比如EF Database First的T4模板)其实可以被调整,让生成的属性自动调用分部方法,这样我们就能在自己的分部类里实现逻辑,完全不碰自动生成的文件。
具体步骤:
- 找到自动生成实体的T4模板文件(通常是
.tt后缀,比如Model.tt) - 修改模板里生成属性的代码块,把原来的自动属性改成带后备字段+分部方法调用的形式:
// 把模板里的属性生成逻辑替换成这段 private <#= property.TypeName #> _<#= property.Name.ToLowerInvariant() #>; public <#= property.TypeName #> <#= property.Name #> { get { return _<#= property.Name.ToLowerInvariant() #>; } set { if (!EqualityComparer<<#= property.TypeName #>>.Default.Equals(_<#= property.Name.ToLowerInvariant() #>, value)) { _<#= property.Name.ToLowerInvariant() #> = value; On<#= property.Name #>Changed(); } } } partial void On<#= property.Name #>Changed(); - 重新生成实体类,这时
Zone类里会自动出现OnLocationIdChanged()的分部方法声明 - 创建自己的分部类,实现这个方法:
public partial class Zone { partial void OnLocationIdChanged() { Process(); // 这里调用你的业务逻辑 } private void Process() { // 写你需要执行的代码 } }
这种方式的好处是:下次重新生成代码时,模板会自动带上分部方法调用,你的实现完全不受影响,而且分部方法如果没实现,编译器会直接优化掉,不会有任何性能开销。
方案二:实现INotifyPropertyChanged接口(无需改模板)
如果不想碰T4模板,也可以在分部类里实现INotifyPropertyChanged,不过得绕个弯——因为自动生成的是自动属性,没法直接修改setter,我们可以通过隐藏原属性的方式来封装逻辑:
public partial class Zone : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private Guid _locationId; // 用new关键字隐藏自动生成的LocationId属性 public new Guid LocationId { get { return _locationId; } set { if (_locationId != value) { _locationId = value; Process(); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(LocationId))); } } } private void Process() { // 业务逻辑 } }
注意:用new关键字隐藏原属性可能会在多态场景下引发问题,所以这个方案更适合不能修改模板的极端情况。
方案三:使用AOP拦截(第三方库辅助)
如果不想改模板也不想写太多代码,可以用AOP框架比如PostSharp,通过特性来拦截属性的set操作:
- 引入PostSharp NuGet包
- 在分部类里定义拦截特性并应用:
[Serializable] public class TriggerProcessOnChangeAttribute : OnMethodBoundaryAspect { public override void OnSuccess(MethodExecutionArgs args) { base.OnSuccess(args); if (args.Instance is Zone zone) { zone.Process(); } } } // 在分部类里给LocationId的set方法应用特性 [TriggerProcessOnChange(AttributeTargetMembers = "set_LocationId")] public partial class Zone { private void Process() { // 业务逻辑 } }
这个方式不需要修改自动生成的代码,但需要引入第三方库,适合不想折腾模板的场景。
方案四:EF ChangeTracker 监听(适合保存时触发)
如果你的Process()方法不需要在属性变更时立即执行,而是在保存到数据库时执行,可以利用EF的ChangeTracker:
在你的DbContext类里重写SaveChanges()方法:
public override int SaveChanges() { foreach (var entry in ChangeTracker.Entries<Zone>()) { // 检查LocationId是否被修改 if (entry.State == EntityState.Modified && entry.Property(z => z.LocationId).IsModified) { entry.Entity.Process(); } } return base.SaveChanges(); }
然后在Zone的分部类里实现Process()方法。这个方式适合不需要实时触发,只在保存时处理的场景。
内容的提问来源于stack exchange,提问作者Zze
相关产品推荐
相关产品推荐

