2016-12-27 2 views
0

Передача информации между базой данных SQL и ПЛК с использованием сторонних библиотек OPC.Добавление реализации интерфейса в ExpandoObject

Существует по существу две сделки.

Информация, переданная с ПЛК на сервер SQL, статически типизирована. Очень специфические данные фиксируются ПЛК и передаются в базу данных SQL.

Информация, передаваемая с сервера SQL в ПЛК, динамически типизируется и может быть ограничена одним свойством или сотнями.

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 и другие методы в базовый класс, но мне не удалось это сделать, потому что базовый класс не знает свойств Response и Request, найденных в 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 для всех свойств, а не только Response and Request. –

+0

Я обновил 'DynamicTransactionObject', чтобы теперь запускать все свойства, а не только два из интерфейса (см. Обновленный' TrySetMember'). Для этого метода вы можете просто добавить его в класс так же, как вы добавили свойства. –

+0

Это кажется близким, я предполагаю, что мне нужно вызвать OnPropertyChanged из явной реализации IDictionary?Я предполагаю, что TrySetMember используется ключевым словом dynamic? –

 Смежные вопросы

  • Нет связанных вопросов^_^