【问题标题】:Observable DataContracts and Two-Way bindingsObservable DataContracts 和双向绑定
【发布时间】:2011-11-26 18:10:12
【问题描述】:

在我的 WPF 应用程序中,我使用 WCF 服务来获取数据。 所以很自然,在某些时候我有“复杂”的对象,需要将 DataContract 作为一个整体传递给 WPF 应用程序。

当然,现在我需要响应更改,并在我的 ViewModel 上实现 INotifyPropertyChanged,但是由于某些对象实际上是 DataContract,我必须重新组合这些对象,以便它们实现 INotifyPropertyChanged。

我觉得它很乱。

我试图做的是直接在 DataContract 定义上实现接口,但我无法对更改做出正确反应。
例如,如果双向数据绑定TextBox 的文本已更改,我的 ViewModel 应通过更改相应 SQL 表中的值(通过 WCF 服务)来对其做出反应,但由于对象是在 WCF 端定义的,我在属性的设置器中不能这样做。

我现在要做的是订阅 DataContracts 的 PropertyChanged 事件,并使用反射来知道哪个属性发生了变化以及它的新值。
但是这些对象保存在ObservableCollection<T> 中,这是很多事件,而且感觉很脆弱……例如,如果我从集合中添加/删除一个元素怎么办?

我这样做(我认为这很糟糕):

foreach (ImageInfo imgi in (param.Images as ObservableCollection<ImageInfo>))
{
    imgi.PropertyChanged += (sender, args) =>
        {
            object newValue = Tools.GetProperty((sender as ImageInfo), args.PropertyName);
        };
}

然后我会将其发送回 WCF 服务。

有没有更优雅的解决方案?我应该只在 ViewModel 上实现 INotifyPropertyChanged,然后重新组合 DataContracts 吗?

谢谢!

【问题讨论】:

  • 我可以解释一下我是如何做到这一点的。数据合约是普通对象。视图模型接收数据契约作为构造函数参数并填写自己的属性。用户单击保存按钮后 - 视图模型收集新值,构造新数据契约并将其发送到服务。
  • 是的,但是我没有“保存”按钮,每次属性更改时我都需要调用服务以反映数据库中的更改。
  • 难度更大,增加了数据库的工作量。但是如果我有这样的要求,我也会这样做:创建新的数据合约并将其发送到服务,即使数据合约只有一个属性不同。如果向 RaisePropertyChanged 方法添加额外的方法调用,则可以处理实时更改。在主视图模型中调用 Save 方法或使用子项中的某种 Messenger 类。

标签: wpf wcf architecture inotifypropertychanged


【解决方案1】:

所以您想要一个通过 WCF 服务器链接到数据库的实时系统?

如何创建模型对象,它的 Get/Set 方法往返于数据库?

public class MyModel : INotifyPropertyChanged
{
    private IMyModelService service;
    public int Id { get; set; }

    public MyModel (IMyModelService wcfService, int id)
    {
        this.Id = id;

        this.service = wcfService;
        AutoMapper.Map<MyModelDTO, MyModel>(service.GetMyModel(this.Id), this);
    }

    public int SomeValue
    {
        get 
        { 
            return service.GetSomeValue(this.Id); 
        }
        set 
        {
            service.SetSomeValue(this.Id, value);
            RaisePropertyChanged("SomeValue");
        }
    }
}

您还可以在本地缓存对象属性并使 WCF 服务调用异步,这可能会提高性能,但如果多个人可以修改单个对象,您可能需要某种消息传递系统来提醒客户端 @当其他用户更新属性时,987654322@ 已更改。

客户端上的 ViewModel 将负责创建模型,而不是 WCF 服务。

public class LoadObject(int id)
{
    CurrentObject = new MyModel(serviceReference, id);
}

【讨论】:

  • 这正是我的想法,只是我不知道如何构建该类。该对象由 WCF 服务在 ViewModel 请求数据时创建,并且该 WCF 对象(合同)实现 INotifyPropertyChanged。它不能自称。您需要组合才能使上面的代码工作。
  • 该应用程序将被数十个用户同时使用,但 WCF 双工不是主题(稍后再说)。
  • @Baboon 我实际上会让 WCF 与数据传输对象 (DTO) 一起工作,并使用 AutoMapper 之类的东西将 DTO 映射到模型。因此 ViewModel 实际上将负责创建 Model 对象,并在构造函数中将当前 WCF 服务实例传递给它,而构造函数将使用 WCF 服务从服务器获取 DTO 并映射它的属性。请参阅我的编辑。即使不使用 DTO,ViewModel 仍应负责创建对象,因此它可以将服务引用传递给它。
【解决方案2】:

好吧,经过一段时间的思考,我现在是这样实现的,但它可能会改变,因为我下周一会见专家(如果他给我更好的主意,我会更新)。

1) WCF 服务有一个 DataContract,例如:

[DataContract]
public class MyWcfData : INotifyPropertyChanged
{
    public MyWcfData()
    {
       MyField = "";
    }

    [DataMember]
    public string MyField;

    [DataMember]
    public string MyFieldModified;
}

注意: INotifyPropertyChanged 是按照通常的方式实现的,为了便于阅读,我只是将其省略了,因为我在这台计算机上没有我的 sn-ps。

在服务上调用 GetMyData() 时,它会返回该类的一个实例,其中仅填充了“MyField”。 MyFieldModified 保留为 null。

在接收 MyWcfData 的我的 DAL(即客户端)上:

public class MyDAL
{
   //... some init code
   public MyWcfData GetMyWcfData()
   {
      MyWcfData newData = m_WcfService.GetMyData();
      newData.MyFieldModified = newData.MyField;
      return newData;
   }
}

这里的重点是需要复制数据,以便我可以跟踪更改,并且最终只更新一次属性,即使用户更改了 10 次(我只是将每次都与原始值进行比较)。但是我不想通过网络发送重复的数据,所以我在 DAL 中进行了复制,然后任何业务对象都可以访问它。

在我的视图的 ViewModel 上:

public class MyViewModel
{
   //.. some init code
           DelegateCommand<MyWcfData> _GetDataCommand;
        public DelegateCommand<MyWcfData> GetDataCommand
        {
            get
            {
                if (_GetDataCommand == null)
                    _GetDataCommand = new DelegateCommand<MyWcfData>(GetData);
                return _GetDataCommand;
            }
        }        
        public void GetData(MyWcfData param)
        {
            m_WcfData = m_DAL.GetMyWcfData();
        }  
}

最后但并非最不重要的一点是,在我的 XAML 中,我在 TextBox 中绑定到 MyFieldModified(在双向绑定中)。
然后我使用 System.Windows.InteractivityTextChanged 事件。
当最后一个命令被触发时,我将更改放入队列中(以跟踪更改顺序),并在用户按下保存按钮时将其发送回 Wcf 服务以实现数据持久性。

注意:我实际上使用了一个定制的ObservableQueue&lt;T&gt;,这样我就可以跟踪更改的数量并通过绑定相应地启用保存按钮。我会写一篇关于它的博文,敬请期待;-)

注意 2: 我放弃了让它保存即时所做的每一个微小更改,尽管在这种情况下它会起作用,因为它不是一个常用的功能,并且预计几乎没有更改;但正如其他答案中指出的那样,这是不好的做法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-26
    • 2016-04-19
    • 2012-05-25
    相关资源
    最近更新 更多