如何在C#中实现类SQL触发器的事件监听功能?
实现C#实体属性变更的事件监听(类SQL触发器功能)
要实现类似SQL触发器的属性变更自动记录功能,核心思路是利用.NET的INotifyPropertyChanged接口,让实体属性变更时自动触发事件,再通过统一的监听类处理变更记录插入,彻底避免重复调用记录方法。以下是具体实现方案:
1. 封装实体基类(实现属性变更通知)
先写一个通用基类,所有需要监控的实体都继承它,避免重复编写变更通知逻辑:
using System.ComponentModel; using System.Runtime.CompilerServices; using System.Collections.Generic; public class BaseEntity : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; // 触发属性变更事件的通用方法 protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } // 通用属性赋值方法,值变化时自动触发事件 protected bool SetProperty<T>(ref T backingField, T value, [CallerMemberName] string propertyName = null) { if (EqualityComparer<T>.Default.Equals(backingField, value)) return false; backingField = value; OnPropertyChanged(propertyName); return true; } }
[CallerMemberName]特性会自动获取调用该方法的属性名,不用手动传入,简化代码。
2. 编写业务实体类
以用户实体为例,所有需要监控的属性都通过基类的SetProperty方法赋值:
public class User : BaseEntity { private string _userName; private int _age; public string UserName { get => _userName; set => SetProperty(ref _userName, value); } public int Age { get => _age; set => SetProperty(ref _age, value); } // 可选:如果需要记录具体实体ID,添加主键属性 public int Id { get; set; } }
3. 实现变更记录监听类
这个类负责订阅实体的变更事件,统一处理数据库插入逻辑:
public class EntityChangeMonitor { private readonly YourDbContext _dbContext; // 构造函数注入数据上下文(比如EF Core的DbContext) public EntityChangeMonitor(YourDbContext dbContext) { _dbContext = dbContext; } // 订阅实体的变更事件 public void Subscribe(BaseEntity entity) { entity.PropertyChanged += HandlePropertyChanged; } // 取消订阅(避免内存泄漏,实体销毁前调用) public void Unsubscribe(BaseEntity entity) { entity.PropertyChanged -= HandlePropertyChanged; } private void HandlePropertyChanged(object sender, PropertyChangedEventArgs e) { var entity = sender as BaseEntity; if (entity == null || string.IsNullOrEmpty(e.PropertyName)) return; // 通过反射获取属性新值 var property = entity.GetType().GetProperty(e.PropertyName); var newValue = property?.GetValue(entity)?.ToString(); // 构造变更记录并插入数据库 var changeLog = new ChangeLog { EntityType = entity.GetType().Name, EntityId = (entity as User)?.Id ?? 0, // 替换为你的实体主键逻辑 PropertyName = e.PropertyName, NewValue = newValue, ChangeDate = DateTime.Now }; _dbContext.ChangeLogs.Add(changeLog); _dbContext.SaveChanges(); } } // 变更记录实体(对应数据库表) public class ChangeLog { public int Id { get; set; } public string EntityType { get; set; } public int EntityId { get; set; } public string PropertyName { get; set; } public string NewValue { get; set; } public DateTime ChangeDate { get; set; } }
4. 实际使用示例
在创建实体后订阅监听,之后所有属性变更都会自动触发记录:
// 初始化数据上下文和监听类 var dbContext = new YourDbContext(); var changeMonitor = new EntityChangeMonitor(dbContext); // 创建实体并订阅 var user = new User { Id = 1, UserName = "old_name" }; changeMonitor.Subscribe(user); // 任意更新属性,自动插入变更记录 user.UserName = "new_name"; user.Age = 30; // 实体不再使用时取消订阅(可选,避免内存泄漏) // changeMonitor.Unsubscribe(user);
优化建议
- 批量保存:如果属性变更频繁,可在监听类中缓存变更记录,定时或在业务操作结束后批量调用
SaveChanges,提升性能。 - 属性过滤:可在基类或监听类中添加逻辑,只监控指定属性(比如通过特性标记),排除不需要记录的属性。
- 泛型扩展:写个泛型扩展方法简化订阅,比如
user.SubscribeToChanges(changeMonitor)。
内容的提问来源于stack exchange,提问作者john_boy
相关产品推荐
相关产品推荐

