DB First模式下EF数据注解验证在WPF捕获异常后失效求助
问题原因分析
- 实体验证状态残留:触发Data Annotation验证错误后,实体对象的错误状态会被保留(尤其是实现
INotifyDataErrorInfo的实体,错误集合不会自动清空),后续即使输入合法内容,旧的验证错误依然存在,导致提交时仍被判定为无效。 - DbContext状态管理混乱:同一个DbContext实例中,若先添加合法用户实体、再添加非法实体,验证失败触发回滚时,DbContext会回滚所有未提交的变更,包括之前的合法用户操作;且非法实体未被从DbContext中移除的话,后续提交会被一并校验,导致合法操作也报错。
- 自动生成实体的验证缺陷:Database First生成的
users类默认未实现动态验证状态更新,仅依赖分部类元数据验证,但没有主动清理错误状态的逻辑,导致错误状态持续影响后续操作。
解决办法
1. 每次注册使用全新实体实例
不要复用同一个users对象,每次点击注册按钮时创建新实例,彻底隔离旧的验证状态:
private void RegisterBtn_Click(object sender, RoutedEventArgs e) { // 每次操作新建实体,避免残留旧错误 var newUser = new users() { Username = UsernameTextBox.Text, Password = PasswordTextBox.Text // 其他属性赋值 }; // 后续验证、提交逻辑 }
2. 手动管理DbContext实体状态
验证不通过时,将错误实体从DbContext中分离,避免影响后续操作:
using (var context = new YourDbContext()) { var validationResults = new List<ValidationResult>(); bool isValid = Validator.TryValidateObject(newUser, new ValidationContext(newUser), validationResults, true); if (!isValid) { // 展示错误信息 foreach (var error in validationResults) { MessageBox.Show(error.ErrorMessage); } // 分离错误实体,防止干扰后续提交 context.Entry(newUser).State = EntityState.Detached; return; } // 验证通过,提交数据 context.users.Add(newUser); context.SaveChanges(); MessageBox.Show("注册成功"); }
3. 实现动态验证状态清理(针对INotifyDataErrorInfo)
如果实体分部类实现了INotifyDataErrorInfo,在属性变更时主动清理对应属性的错误状态:
[MetadataType(typeof(UsersMetadata))] public partial class users : INotifyDataErrorInfo { private readonly Dictionary<string, List<string>> _errors = new Dictionary<string, List<string>>(); public string Username { get => base.Username; set { base.Username = value; // 清理当前属性的旧错误 if (_errors.ContainsKey(nameof(Username))) { _errors.Remove(nameof(Username)); ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(Username))); } // 重新验证当前属性 ValidateProperty(nameof(Username), value); } } private void ValidateProperty(string propertyName, object value) { var validationResults = new List<ValidationResult>(); var isValid = Validator.TryValidateProperty( value, new ValidationContext(this) { MemberName = propertyName }, validationResults); if (!isValid) { _errors[propertyName] = validationResults.Select(r => r.ErrorMessage).ToList(); ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName)); } } // 其他INotifyDataErrorInfo接口实现代码... }
4. 每次注册使用独立DbContext实例
避免复用DbContext,每次注册请求都创建新实例,隔离不同操作的状态:
private void RegisterBtn_Click(object sender, RoutedEventArgs e) { // 每次注册新建DbContext,操作之间互不影响 using (var context = new YourDbContext()) { // 实体创建、验证、提交逻辑 } }
内容的提问来源于stack exchange,提问作者SpicyRiceTea
相关产品推荐
相关产品推荐

