可观察的DataContracts和双向绑定

时间:2011-09-28 10:18:48

标签: wpf wcf architecture inotifypropertychanged

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

当然,我需要响应更改,并在ViewModel上实现INotifyPropertyChanged,但是由于某些对象实际上是DataContracts,因此我必须重新构造它们,以便它们实现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吗?

谢谢!

2 个答案:

答案 0 :(得分: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服务调用异步,这可能会提高性能,但如果多个人可以修改单个对象,则可能需要某种消息传递系统来警告客户{当另一个用户更新属性时,{1}}已更改。

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

MyObject

答案 1 :(得分:0)

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

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

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

    [DataMember]
    public string MyField;

    [DataMember]
    public string MyFieldModified;
}

注意: INotifyPropertyChanged以通常的方式实现,我只是为了提高可读性而将其删除,因为我在这台计算机上没有我的代码段。

当在服务上调用GetMyData()时,它返回该类的实例,只填充“MyField”。 MyFieldModified留空。

在我的DAL(客户端),收到MyWcfData的地方:

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中,我绑定到MyFieldModified中的TextBox(双向绑定)。
然后我使用System.Windows.Interactivity来在DelegateCommand活动中致电TextChanged 当最后一个命令被触发时,我将更改放入队列(以跟踪更改顺序),并在用户按下“保存”按钮时将其发送回Wcf服务以获得数据持久性。

注意:我实际上使用了自定义的ObservableQueue<T>,以便我可以跟踪更改的数量,并通过绑定相应地启用保存按钮。我将发表关于它的博客文章,敬请关注; - )

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