【问题标题】:Subscribing to PropertyChanged of WCF service's DataContract proxy classe订阅 WCF 服务的 DataContract 代理类的 PropertyChanged
【发布时间】:2010-02-01 16:01:01
【问题描述】:

我喜欢使用部分类扩展客户端类的想法,这些类是 WCF 服务的数据协定。但是我遇到了一个严重破坏聚会的问题。

想象一下在服务器端我有一堂课:

[DataContract]
public class SolidObject
{
    [DataMember]
    public Point Position { get; set; }
    [DataMember]
    public Size Size { get; set; }
}

在客户端,我生成了一个代理类,用于业务逻辑层。根据业务逻辑的需要,我这样扩展:

public partial class SolidObject
{
    public Rect Bounds { get { return new Rect(Position.X - Size.Width / 2, Position.Y - Size.Height / 2, Size.Width, Size.Height); }}
}

现在我想确保无论何时 Position 或 Size 发生变化,都会调用 Bounds chage 事件。通过代码很容易做到:

PropertyChanged += (sender, e) =>
    {
        if ((e.PropertyName == "Position") || (e.PropertyName == "Size")) PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Bounds"));
    };

问题是放置这段代码的好地方在哪里。

如果对象不是由服务调用创建的,我会将其放入构造函数中。但是 WCF 服务会忽略客户端的构造函数,请参阅 constructor not showing up in my WCF client, serialization problem?

现在,在服务响应之后,我的程序立即搜索数据协定层次结构,获取所需对象并添加事件处理程序。但我认为这是不对的。

所以我很感兴趣在哪里做更好,或者,也许,认为应该改变整个方法。任何想法表示赞赏。

【问题讨论】:

    标签: c# .net wcf inotifypropertychanged


    【解决方案1】:

    您可以使用 [OnDeserlialized] 属性插入在反序列化后调用的方法。

    public partial class SolidObject
    {
        public Rect Bounds
        {
            get
            {
                return new Rect(Position.X - Size.Width / 2, Position.Y - Size.Height / 2, Size.Width, Size.Height);
            }
        }
    
        [OnDeserialized]
        public void Initialize(StreamingContext streamingContext)
        {
            PropertyChanged += (sender, e) =>
            {
                if ((e.PropertyName == "Position") || (e.PropertyName == "Size"))
                    PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Bounds"));
            };
        }
    }
    

    【讨论】:

    • 太棒了!这就是我一直在寻找的。唯一要注意的是,在我公开此方法之前它会引发异常。
    • 这太棒了!有一个类似的问题,这已经解决了。谢谢!
    【解决方案2】:

    我建议使用视图模型而不是分部类。

    public class SolidObjectViewModel : INotifyPropertyChanged
    {
        private SolidObject _solidObject;
    
        public SolidObjectViewModel(SolidObject solidObject)
        {
            _solidObject = solidObject;
            _solidObject.PropertyChanged += (sender, e) =>
            {
                bool positionChanged = e.PropertyName == "Position";
                bool sizeChanged = e.PropertyName == "Size";
                if (positionChanged)
                    FirePropertyChanged("Position");
                if (sizeChanged)
                    FirePropertyChanged("Size");
                if (positionChanged || sizeChanged)
                    FirePropertyChanged("Bounds");
            };
        }
    
        public Point Position
        {
            get { return _solidObject.Position; }
            set { _solidObject.Position = value; }
        }
    
        public Size Size
        {
            get { return _solidObject.Size; }
            set { _solidObject.Size = value; }
        }
    
        public Rect Bounds
        {
            get
            {
                return new Rect(Position.X - Size.Width / 2, Position.Y - Size.Height / 2, Size.Width, Size.Height);
            }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        private void FirePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    【讨论】:

    • +1,但如果你说为什么建议视图模型,答案会更好。
    • 迈克尔,谢谢你提出这个问题,我仍然不知道答案。至于您的方法,它可能有效,但并非总是如此。我确实使用 ViewModel,但用于其他目的。我认为视图模型用于管理 UI。但是,在这种情况下,我需要业务层的附加属性来实现物理模型,它独立于表示层。所以我投了赞成票,但没有选择它作为解决方案。
    • 在这种情况下,视图模型可能是模式的错误术语。这种技术会起作用而部分类不起作用的原因是它给了你一个初始化钩子。
    • 我不喜欢这里的是,事实上我必须在模型和视图模型之间引入另一层。也许是对的,但除了挂钩初始化之外,至少还有其他好处才能进行如此大规模的操作。
    • 添加了另一个不需要额外层的答案。
    【解决方案3】:

    如果要实现业务逻辑,我会说不要使用生成的类。通过在客户端实现 SolidObject 来保持模型干净。当您生成代理时,您可以使用 reuse classes from this dll 的选项。

    实体附加到它自己的事件是没有意义的。实现将是..

    [DataContract]
    public class SolidObject
    {
       [DataMember]
       public Point Position
        {
            get { return _position; }
            set 
              { 
               position = value; 
               FirePropertyChanged("Position");
               FirePropertyChanged("Bounds");
               }
        }
    
        public Size Size
        {  
         //similar to position
        }
    
        public Rect Bounds { get { return new Rect(Position.X - Size.Width / 2, Position.Y - Size.Height / 2, Size.Width, Size.Height); }}
    }
    

    【讨论】:

    • 能否请您澄清一下您建议的代码应该驻留在客户端还是服务器端?
    • 此代码应驻留在客户端。然后您可以使用选项在生成代理时重用类型。
    • 然后我想知道服务器端会发生什么。这些类是否具有相同的名称和命名空间并与它们合并?
    • 您可以拥有 DataContract 的接口,并且在服务器端和客户端有不同的实现..
    • 有趣的想法,但我认为在这些实现重叠的部分会有太多代码并且可能代码重复。在某些情况下可能有意义,但在常见的场景中,我可以想象这个解决方案是重量级的。无论如何,感谢您参与讨论。
    猜你喜欢
    • 1970-01-01
    • 2010-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多