改变跟踪和并发 - 这会失败吗?

时间:2010-09-07 20:23:55

标签: wpf mvvm concurrency

我一直在乱用表达式 - 我可能超出了我的能力 - 但是这里......我实现了'类型安全'的INotifyPropertyChanged实现(例如here),但是走得更远,包括改变跟踪:

public abstract BaseViewModel<TEntity>:INotifyPropertyChanged
{
    private readonly IBaseChangeTracker<TEntity> _changeTracker;
    protected void OnPropertyChanged<T>(Expression<Func<T>> property, T value)
    {
        _changeTracker.AddChange(property, value);
        OnPropertyChanged(property);
    }
    protected virtual void OnPropertyChanged<TProperty>(Expression<Func<TProperty>> property)
    {
        if (PropertyChanged == null) return;
        PropertyChanged(this, new PropertyChangedEventArgs(property.GetMemberInfo().Name));
    }
    public event PropertyChangedEventHandler PropertyChanged;
}

public abstract class BaseChangeTracker<TEntity>:IBaseChangeTracker<TEntity>
{
    private readonly IDictionary<Expression, object> _changes = new Dictionary<Expression, object>();
    public void AddChange<T>(Expression<Func<T>> expression, T newValue)
    {
        _changes.Add(expression, newValue);
    }

    public void ApplyChanges(TEntity entity)
    {
        foreach (var change in _changes)
        {
            var property = typeof(TEntity).GetProperty(change.Key.GetMemberInfo().Name);
            property.SetValue(entity, change.Value, null);
        }
    }

    public virtual void CopyCurrentState(TEntity entity)
    {
        _changes.Clear();

    }
    public virtual void ResetEntity(TEntity entity)
    {
        _changes.Clear();
    }

    public bool HasUnsavedChanges
    {
        get { return _changes.Any(); }
    }
}

这可能看起来有点过分 - 每个实体都会拥有它自己的ChangeTracker在加载时保持实体的原始状态,并且可以将其重置回这些状态,但想法是如果在尝试时存在并发冲突保存更新的实体,我从数据库重新加载实体并通过.ApplyChanges运行它并尝试再次保存它。这将删除大约95%的并发问题......如果它有效。我的测试显示,对于有限的实体,它可以工作,但这是简单的属性更改。

已知问题:

  1. 我还没有找到一种处理藏品的优雅方式。
  2. 我还缺少什么 - 或者我的设计中存在明显的缺陷?

1 个答案:

答案 0 :(得分:1)

我意识到这不是您问题的直接答案,但您可以考虑使用命令模式来实现撤消/重做堆栈。

封装命令中的更改是循环/重新循环更改的一种非常整洁的方式,还有以下优点:(1)一个为应用程序增加价值的好功能,(2)您可以在任何给定的内容中包含许多操作命令,就像在do和undo方向上引发数据绑定支持的事件更改通知一样。

此外,管理集合更改并不比简单的属性更新更具挑战性。

具体到您发布的代码,OnPropertyChanged实施永远不会引发PropertyChanged事件,因为您在return语句后调用if(),然后再用裸括号调用({1}}这些与if条件不对应。

  if (PropertyChanged == null) return; // this returns based on if
    {
        return; // this returns no matter what
    }

此外,似乎用户不会在UI中看到任何更改。在调用ApplyChanges之前,这些值不会更新,并且在没有PropertyChanged事件时也不会更新。 (我可能没有正确地遵循您的代码,但只是查看它似乎就是这种情况。)