65.9K
CodeProject 正在变化。 阅读更多。
Home

放弃业务对象中的更改

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (25投票s)

2006年11月16日

3分钟阅读

viewsIcon

97731

downloadIcon

994

如何构建一个具有放弃更改选项的业务对象。

Sample Image - IEditableObject.png

必备组件

为了运行示例并阅读本文,您必须安装 Visual Studio 2005 和 C#。

引言

处理数据的最方便和高级的方法是使用封装业务逻辑的业务对象。然后,这些对象调用数据访问层来持久化信息。业务对象可以手动创建或由 ORM 工具生成。

一旦我们准备好类并将其填充(从持久层提取数据),我们会在一些网格中显示对象,并允许用户在表单中编辑对象。到目前为止一切顺利。感谢微软提供的 Binding 类以及设置绑定后让 .NET Framework 管理表单控件和业务对象之间数据交换的能力。

问题

作为一个典型案例,用户从网格中选择一个对象并打开编辑表单。显示模式表单,并且业务对象的属性绑定到表单控件。通过使用数据绑定,我们可以根据 DataSourceUpdateMode 枚举将控件中更改的数据提交到对象

  • Never - 从不更新数据源,并且输入到控件中的值不会被解析、验证或重新格式化。
  • OnPropertyChanged - (default) 只要控件属性的值发生变化,就会更新数据源。
  • OnValidation - 只要控件属性被验证,就会更新数据源。

这给了我们一些控制“何时”更新数据源的权限。如果我们想放弃更改怎么办?

解决方案

此时,我们有两种解决方案

  1. Validate() 方法

    第一种不是非常优雅,但更明显 - 我们可以使用 DataSourceUpdateMode.OnValidation 模式来更新数据源,并且仅当用户想要确认数据时才调用 Form.Validate() 方法。如果要放弃数据,则不调用 Validate() 方法。这会引入一致性问题:如果某些控件被验证(并且源已更新)而其余的未被验证,该怎么办?当然,我们会要求用户修复输入的信息。但如果此时,用户决定取消对象编辑,我们就陷入困境,因为某些字段已经更新。

  2. IEditableObject 接口

    幸运的是,可以使用 .NET 工具来实现这一点。这就是 IEditableObject 接口。此接口声明了三个公共方法

    • BeginEdit() - 开始对对象进行编辑。
    • CancelEdit() - 放弃自上次 BeginEdit 调用以来的更改。
    • EndEdit() - 将自上次 BeginEdit IBindingList.AddNew 调用以来的更改推送到底层对象。

    还有另一个好消息 - 微软控件支持并与此接口配合良好。我们所要做的就是在我们的业务对象中实现此接口。这需要我们做一些工作,但给了我们足够的灵活性。我个人更喜欢创建一个实现公共逻辑(包括 IEditableObject 方法)的所有业务对象的基类。

通用 IEditableObject 实现

这里提供了一个通用实现,可以轻松添加到基类并包含在现有项目中。此实现保留了所有公共属性的值,不关注所有字段、私有属性、没有 set 访问器的公共属性

首先,我们需要一些 key/value 对集合,它将为我们保存原始值。

存储原始值

这也是对象处于编辑模式的标志。

Hashtable props = null;

BeginEdit()

此方法使用反射存储当前值以供将来恢复。

public void BeginEdit()
{
    //exit if in Edit mode
    //uncomment if  CancelEdit discards changes since the 
    //LAST BeginEdit call is desired action
    //otherwise CancelEdit discards changes since the 
    //FIRST BeginEdit call is desired action
    //if (null != props) return;

    //enumerate properties
    PropertyInfo[] properties = (this.GetType()).GetProperties
                (BindingFlags.Public | BindingFlags.Instance);

    props = new Hashtable(properties.Length - 1);

    for (int i = 0; i < properties.Length; i++)
    {
        //check if there is set accessor
        if (null != properties[i].GetSetMethod())
        {
            object value = properties[i].GetValue(this, null);
            props.Add(properties[i].Name, value);
        }
    }
}

CancelEdit()

此方法恢复旧值。

public void CancelEdit()
{
    //check for inappropriate call sequence
    if (null == props) return;

    //restore old values
    PropertyInfo[] properties =(this.GetType()).GetProperties
        (BindingFlags.Public | BindingFlags.Instance);
    for (int i = 0; i < properties.Length; i++)
    {
        //check if there is set accessor
        if (null != properties[i].GetSetMethod())
        {
            object value = props[properties[i].Name];
            properties[i].SetValue(this, value, null);
        }
    }

    //delete current values
    props = null;
}

EndEdit()

此方法仅删除存储的值(以及我们的标志)。

public void EndEdit()
{
    //delete current values
    props = null;
}
© . All rights reserved.