2016-12-27 72 views
0

我使用第三方OPC庫在SQL數據庫和PLC之間傳遞信息。將接口實現添加到ExpandoObject

基本上有兩個交易。

從PLC傳遞到SQL服務器的信息是靜態類型的。非常特定的數據被PLC捕獲並傳遞給SQL數據庫。

從SQL服務器傳遞給PLC的信息是動態輸入的,可能會限制爲單個屬性或數百個。

ITransaction.cs

public interface ITransaction : INotifyPropertyChanged 
{ 
    short Response { get; set; } 

    bool Request { get; set; } 

    void Reset(); 
} 

BaseTransaction.cs

internal abstract class BaseTransaction<T> : IDisposable 
    where T : class, INotifyPropertyChanged 
{ 
    private T _opcClient; 

    protected T OpcClient 
    { 
     get { return _opcClient; } 
     set 
     { 
      if (_opcClient != value) 
      { 
       OnOpcClientChanging(); 
       _opcClient = value; 
       OnOpcClientChanged(); 
      } 
     } 
    } 

    protected abstract void OnOpcClientPropertyChanged(object sender, PropertyChangedEventArgs e); 

    private void OnOpcClientChanged() 
    { 
     if (_opcClient != null) 
     { 
      _opcClient.PropertyChanged += OnOpcClientPropertyChanged; 

      OpcManager = new OpcManager(_opcClient); 
     } 
    } 

    private void OnOpcClientChanging() 
    { 
     if (_opcClient != null) 
      _opcClient.PropertyChanged -= OnOpcClientPropertyChanged; 
    } 
} 

StaticTransaction.cs

internal abstract class StaticTransaction<T> : BaseTransaction<T> 
    where T : class, ITransaction, new() 
{ 
    public StaticTransaction() 
    { 
     OpcClient = new T(); 
    } 

    protected override void OnOpcClientPropertyChanged(object sender, PropertyChangedEventArgs e) 
    { 
     switch (e.PropertyName) 
     { 
      case "Response": 
       ProcessResponse(OpcClient.Response); 
       break; 
      case "Request": 
       ProcessRequest(OpcClient.Request); 
       break; 
     } 
    } 
} 

DynamicTransaction.cs

internal abstract class DynamicTransaction : BaseTransaction<ExpandoObject> 
{ 
    protected new dynamic OpcClient 
    { 
     get { return base.OpcClient as dynamic; } 
    } 

    public DynamicTransaction() 
    { 
     dynamic opcClient = new ExpandoObject(); 

     opcClient.Request = false; 
     opcClient.Response = 0; 

     // Access database, use IDictionary interface to add properties to ExpandoObject. 

     opcClient.Reset = new Action(Reset); 

     base.OpcClient = opcClient; 
    } 

    protected override void OnOpcClientPropertyChanged(object sender, PropertyChangedEventArgs e) 
    { 
     switch (e.PropertyName) 
     { 
      case "Response": 
       ProcessResponse(OpcClient.Response); 
       break; 
      case "Request": 
       ProcessRequest(OpcClient.Request); 
       break; 
     } 
    } 

    private void Reset() 
    { 
     // Use IDictionary interface to reset dynamic properties to defaults. 

     OpcClient.Request = false; 
     OpcClient.Response = 0; 
    } 
} 

如圖既StaticTransaction和DynamicTransaction具有的其他方法之中OnOpcClientPropertyChanged未示出相同的實施方式。我想將OnOpcClientPropertyChanged和其他方法帶入基類,但是因爲基類不知道OpcClient中的響應和請求屬性,所以無法這樣做。我能否將接口ITransaction以某種方式帶入基類,並且仍然適應動態實現?

回答

1

您可以繼承DynamicObject(它的行爲與ExpandoObject類似)並製作自己的版本,實現ITransaction。這可以讓您將ITransaction約束移至基類。

BaseTransaction.cs

internal abstract class BaseTransaction<T> : IDisposable where T : class, ITransaction 
{ 
    private T _opcClient; 

    protected T OpcClient 
    { 
     get { return _opcClient; } 
     set 
     { 
      if (_opcClient != value) 
      { 
       OnOpcClientChanging(); 
       _opcClient = value; 
       OnOpcClientChanged(); 
      } 
     } 
    } 


    private void OnOpcClientPropertyChanged(object sender, PropertyChangedEventArgs e) 
    { 
     switch (e.PropertyName) 
     { 
      case "Response": 
       ProcessResponse(OpcClient.Response); 
       break; 
      case "Request": 
       ProcessRequest(OpcClient.Request); 
       break; 
     } 
    } 

    protected abstract void ProcessResponse(short opcClientResponse); 

    protected abstract void ProcessRequest(bool opcClientRequest); 

    private void OnOpcClientChanged() 
    { 
     if (_opcClient != null) 
     { 
      _opcClient.PropertyChanged += OnOpcClientPropertyChanged; 

      OpcManager = new OpcManager(_opcClient); 
     } 
    } 

    private void OnOpcClientChanging() 
    { 
     if (_opcClient != null) 
      _opcClient.PropertyChanged -= OnOpcClientPropertyChanged; 
    } 
} 

StaticTransaction.cs

internal abstract class StaticTransaction<T> : BaseTransaction<T> 
where T : class, ITransaction, new() 
{ 
    public StaticTransaction() 
    { 
     OpcClient = new T(); 
    } 
} 

DynamicTransactionObject.cs

internal class DynamicTransactionObject : DynamicObject, ITransaction, IDictionary<string, object> 
{ 

    private readonly Dictionary<string, object> _data = new Dictionary<string, object>(); 

    public DynamicTransactionObject() 
    { 
     //Set initial default values for the two properties to populate the entries in the dictionary. 
     _data[nameof(Response)] = default(short); 
     _data[nameof(Request)] = default(bool); 
    } 

    public short Response 
    { 
     get 
     { 
      return (short)_data[nameof(Response)]; 
     } 
     set 
     { 
      if (Response.Equals(value)) 
       return; 

      _data[nameof(Response)] = value; 
      OnPropertyChanged(); 
     } 
    } 

    public bool Request 
    { 
     get 
     { 
      return (bool)_data[nameof(Request)]; 
     } 
     set 
     { 
      if (Request.Equals(value)) 
       return; 

      _data[nameof(Request)] = value; 
      OnPropertyChanged(); 
     } 
    } 

    public override IEnumerable<string> GetDynamicMemberNames() 
    { 
     return _data.Keys; 
    } 

    public override bool TryGetMember(GetMemberBinder binder, out object result) 
    { 

     return _data.TryGetValue(binder.Name, out result); 
    } 

    public override bool TrySetMember(SetMemberBinder binder, object value) 
    { 
     object oldValue; 
     _data.TryGetValue(binder.Name, out oldValue) 
     _data[binder.Name] = value; 
     if(!Object.Equals(oldValue, value) 
      OnPropertyChanged(binder.Name); 
     return true; 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) 
    { 
     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
    } 

    #region IDictionary<string,object> members 
    IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator() 
    { 
     return _data.GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return ((IEnumerable)_data).GetEnumerator(); 
    } 

    void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item) 
    { 
     ((ICollection<KeyValuePair<string, object>>)_data).Add(item); 
    } 

    void ICollection<KeyValuePair<string, object>>.Clear() 
    { 
     _data.Clear(); 
    } 

    bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item) 
    { 
     return ((ICollection<KeyValuePair<string, object>>)_data).Contains(item); 
    } 

    void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex) 
    { 
     ((ICollection<KeyValuePair<string, object>>)_data).CopyTo(array, arrayIndex); 
    } 

    bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item) 
    { 
     return ((ICollection<KeyValuePair<string, object>>)_data).Remove(item); 
    } 

    int ICollection<KeyValuePair<string, object>>.Count 
    { 
     get { return _data.Count; } 
    } 

    bool ICollection<KeyValuePair<string, object>>.IsReadOnly 
    { 
     get { return ((ICollection<KeyValuePair<string, object>>)_data).IsReadOnly; } 
    } 

    bool IDictionary<string, object>.ContainsKey(string key) 
    { 
     return _data.ContainsKey(key); 
    } 

    void IDictionary<string, object>.Add(string key, object value) 
    { 
     _data.Add(key, value); 
    } 

    bool IDictionary<string, object>.Remove(string key) 
    { 
     return _data.Remove(key); 
    } 

    bool IDictionary<string, object>.TryGetValue(string key, out object value) 
    { 
     return _data.TryGetValue(key, out value); 
    } 

    object IDictionary<string, object>.this[string key] 
    { 
     get { return _data[key]; } 
     set { _data[key] = value; } 
    } 

    ICollection<string> IDictionary<string, object>.Keys 
    { 
     get { return _data.Keys; } 
    } 

    ICollection<object> IDictionary<string, object>.Values 
    { 
     get { return _data.Values; } 
    } 
#endregion 
} 

DynamicTransaction.cs

internal abstract class DynamicTransaction : BaseTransaction<DynamicTransactionObject> 
{ 
    protected new dynamic OpcClient 
    { 
     get { return base.OpcClient as dynamic; } 
    } 

    public DynamicTransaction() 
    { 
     var opcClient = new DynamicTransactionObject(); 

     // Access database, use IDictionary<string,object> interface to add properties to DynamicObject. 

     base.OpcClient = opcClient; 
    } 
} 
+0

這看起來很有希望,我仍然可以通過一些細節工作。我的實際使用情況稍微複雜一些。 ITransaction定義了一個方法以及兩個屬性。我還需要爲所有屬性觸發PropertyChanged事件,而不僅僅是響應和請求。 –

+0

我更新了'DynamicTransactionObject'現在觸發所有的屬性,而不是從接口中的兩個(參見更新的'TrySetMember')。對於該方法,您可以像添加屬性一樣將其添加到類中。 –

+0

這似乎很接近,我假設我需要從顯式的IDictionary實現中調用OnPropertyChanged?我猜TrySetMember被動態關鍵字使用? –