init
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ClassDiagram MajorVersion="1" MinorVersion="1">
|
||||
<Class Name="DTS.Common.Core.Config.DTSConfig" Collapsed="true">
|
||||
<Position X="0.5" Y="0.5" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AIAQAAAEAACAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAIA=</HashCode>
|
||||
<FileName>Config\DTSConfig.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Class>
|
||||
<Class Name="DTS.Common.Core.DTSConstants" Collapsed="true">
|
||||
<Position X="2.25" Y="0.5" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAgAAAAA=</HashCode>
|
||||
<FileName>DTSConstants.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Class>
|
||||
<Class Name="DTS.Common.Core.EventManager.EventManager" Collapsed="true">
|
||||
<Position X="4" Y="0.5" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAAAAAIIAAACAAGAEAACAAAAAACAAAAQAAAAABAAAA=</HashCode>
|
||||
<FileName>EventManager\EventManager.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Class>
|
||||
<Class Name="DTS.Common.Core.EventManager.EventMetaData<T>" Collapsed="true">
|
||||
<Position X="5.75" Y="0.5" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAA=</HashCode>
|
||||
<FileName>EventManager\EventManager.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Class>
|
||||
<Class Name="DTS.Common.Core.PluginLib.PluginConfig" Collapsed="true">
|
||||
<Position X="4" Y="1.5" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAgAAAIAAAAAAAAAAAAAA=</HashCode>
|
||||
<FileName>PluginLib\PluginConfig.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Class>
|
||||
<Class Name="DTS.Common.Core.PluginLib.PluginConfigData" Collapsed="true">
|
||||
<Position X="5.75" Y="1.5" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAA=</HashCode>
|
||||
<FileName>PluginLib\PluginConfigData.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Class>
|
||||
<Class Name="DTS.Common.Core.PluginLib.PluginConfigSectionHandler" Collapsed="true">
|
||||
<Position X="0.5" Y="2.5" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAA=</HashCode>
|
||||
<FileName>PluginLib\PluginConfigSectionHandler.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Class>
|
||||
<Class Name="DTS.Common.Core.PluginLib.FilterHashKeyCollection" Collapsed="true">
|
||||
<Position X="2.25" Y="1.5" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAACAAAAAAAAAAAACAAAAAAAAAABAAAAAAAAAAAAAAA=</HashCode>
|
||||
<FileName>PluginLib\PluginConfigSectionHandler.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Class>
|
||||
<Class Name="DTS.Common.Core.PluginLib.FilterHashElement" Collapsed="true">
|
||||
<Position X="0.5" Y="1.5" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAgAAAAAAA=</HashCode>
|
||||
<FileName>PluginLib\PluginConfigSectionHandler.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Class>
|
||||
<Class Name="DTS.Common.Core.PluginLib.PluginManager" Collapsed="true">
|
||||
<Position X="2.25" Y="2.5" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AEAABAAAAAAEAACAAAMAAAAAAAAAAAAAAgAAAAAAAAA=</HashCode>
|
||||
<FileName>PluginLib\PluginManager.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Class>
|
||||
<Class Name="DTS.Common.Core.ServiceManager.ServiceManager" Collapsed="true">
|
||||
<Position X="4" Y="2.5" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>gAAAAAAAAAAAAABAAEAAAAABAAAAAAAAAAAAAAFAAAA=</HashCode>
|
||||
<FileName>ServiceManager\ServiceManager.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Class>
|
||||
<Class Name="DTS.Common.Core.ServiceManager.ServicePublishedEvent" Collapsed="true" BaseTypeListCollapsed="true">
|
||||
<Position X="5.75" Y="2.5" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAA=</HashCode>
|
||||
<FileName>ServiceManager\ServicePublishedEvent.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
<Lollipop Position="0.2" Collapsed="true" />
|
||||
</Class>
|
||||
<Class Name="DTS.Common.Core.Settings.SettingsChangedEventArgs<TKey, TItem>" Collapsed="true">
|
||||
<Position X="0.5" Y="3.5" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAAAAAAAAAAAAQAAAEAAAAEAAAAAAAAAAAAAAAAAAA=</HashCode>
|
||||
<FileName>Settings\SettingsChangedEventArgs.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Class>
|
||||
<Class Name="DTS.Common.Core.Settings.SettingsCollection<TKey, TItem>" Collapsed="true" BaseTypeListCollapsed="true">
|
||||
<Position X="2.25" Y="3.5" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>CAIAAAAAAAAEABAQCAAABAQAABAAAAAEAAAgAABwKAA=</HashCode>
|
||||
<FileName>Settings\SettingsCollection.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
<Lollipop Position="0.2" Collapsed="true" />
|
||||
</Class>
|
||||
<Interface Name="DTS.Common.Core.ServiceManager.IServicePublishedEvent" Collapsed="true">
|
||||
<Position X="0.5" Y="4.75" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAA=</HashCode>
|
||||
<FileName>ServiceManager\IServicePublishedEvent.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Interface>
|
||||
<Enum Name="DTS.Common.Core.EventManager.EventDiagnosticType" Collapsed="true">
|
||||
<Position X="2.25" Y="5.75" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAAAAAAAAAAABAAAAAAAAAAAAAEIACAAAAAAABAAAA=</HashCode>
|
||||
<FileName>EventManager\EventManager.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Enum>
|
||||
<Enum Name="DTS.Common.Core.Settings.ChangeSettingType" Collapsed="true">
|
||||
<Position X="0.5" Y="5.75" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAIAAAAAAAAAAABAABAABAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
|
||||
<FileName>Settings\SettingsChangedEventArgs.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Enum>
|
||||
<Delegate Name="DTS.Common.Core.EventManager.SubscriberCallbackDelegate<T>" Collapsed="true">
|
||||
<Position X="2.25" Y="6.75" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
|
||||
<FileName>EventManager\EventManager.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Delegate>
|
||||
<Delegate Name="DTS.Common.Core.EventManager.DiagnosticCallbackDelegate" Collapsed="true">
|
||||
<Position X="0.5" Y="6.75" Width="1.5" />
|
||||
<TypeIdentifier>
|
||||
<HashCode>AAAAAAAAAACAAEAAAAAAAAAEAAAABAAAAAAAAAAAAAA=</HashCode>
|
||||
<FileName>EventManager\EventManager.cs</FileName>
|
||||
</TypeIdentifier>
|
||||
</Delegate>
|
||||
<Font Name="Segoe UI" Size="9" />
|
||||
</ClassDiagram>
|
||||
@@ -0,0 +1,46 @@
|
||||
using System.Configuration;
|
||||
|
||||
namespace DTS.Common.Core.PluginLib
|
||||
{
|
||||
/// <summary>
|
||||
/// Support class that parses data from DTS.Common.Core.PluginLib.Config configuration section
|
||||
/// </summary>
|
||||
public class PluginConfigSectionHandler : ConfigurationSection
|
||||
{
|
||||
[ConfigurationProperty("PluginFolders")]
|
||||
public FilterHashKeyCollection HashKeys => (FilterHashKeyCollection)base["PluginFolders"];
|
||||
}
|
||||
|
||||
[ConfigurationCollection(typeof(FilterHashElement))]
|
||||
public class FilterHashKeyCollection : ConfigurationElementCollection
|
||||
{
|
||||
protected override ConfigurationElement CreateNewElement()
|
||||
{
|
||||
return new FilterHashElement();
|
||||
}
|
||||
|
||||
protected override object GetElementKey(ConfigurationElement element)
|
||||
{
|
||||
return ((FilterHashElement)element).Key;
|
||||
}
|
||||
|
||||
public FilterHashElement this[int idx] => (FilterHashElement)BaseGet(idx);
|
||||
}
|
||||
|
||||
public class FilterHashElement : ConfigurationElement
|
||||
{
|
||||
[ConfigurationProperty("key", DefaultValue = "", IsKey = true, IsRequired = true)]
|
||||
public string Key
|
||||
{
|
||||
get => (string)base["key"];
|
||||
set => base["key"] = value;
|
||||
}
|
||||
|
||||
[ConfigurationProperty("value", DefaultValue = "", IsKey = false, IsRequired = false)]
|
||||
public string Value
|
||||
{
|
||||
get => (string)base["value"];
|
||||
set => base["value"] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
using DTS.Common.Utilities.Logging;
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.Linq;
|
||||
// ReSharper disable InconsistentNaming
|
||||
// ReSharper disable EmptyConstructor
|
||||
|
||||
namespace DTS.Common.Core.Config
|
||||
{
|
||||
public static class DTSConfig
|
||||
{
|
||||
private static string AltConfigPath;
|
||||
public static string AltConfigPathGet()
|
||||
{
|
||||
lock (MyLock)
|
||||
{
|
||||
return AltConfigPath;
|
||||
}
|
||||
}
|
||||
public static void AltConfigPathSet(string path)
|
||||
{
|
||||
lock (MyLock)
|
||||
{
|
||||
AltConfigPath = path;
|
||||
}
|
||||
}
|
||||
// static constructor
|
||||
static DTSConfig()
|
||||
{
|
||||
}
|
||||
|
||||
public static void DTSConfigInit(string path)
|
||||
{
|
||||
SetAltConfigPath(path);
|
||||
}
|
||||
private static object MyLock = new object();
|
||||
/// <summary>
|
||||
/// Static variable to hold alternate configuration file
|
||||
/// </summary>
|
||||
private static Configuration AltConfig;
|
||||
|
||||
public static Configuration GetAltConfig()
|
||||
{
|
||||
lock (MyLock)
|
||||
{
|
||||
return AltConfig;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Static variable to hold alternate configuration file
|
||||
/// </summary>
|
||||
/// <history>
|
||||
///
|
||||
/// </history>
|
||||
public static void SetAltConfigPath(string path)
|
||||
{
|
||||
lock (MyLock)
|
||||
{
|
||||
AltConfigPath = path;
|
||||
var dtsConfig = new ExeConfigurationFileMap
|
||||
{
|
||||
ExeConfigFilename = string.IsNullOrEmpty(AltConfigPath) ? DTSConstants.CustomConfigPath : AltConfigPath
|
||||
};
|
||||
|
||||
AltConfig = ConfigurationManager.OpenMappedExeConfiguration(dtsConfig, ConfigurationUserLevel.None, true);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Static method to retrieve a setting from config file
|
||||
/// </summary>
|
||||
/// <param name="key">string</param>
|
||||
/// <returns>string</returns>
|
||||
public static string GetAppSetting(string key)
|
||||
{
|
||||
var appSetting = AltConfig.AppSettings.Settings.Cast<KeyValueConfigurationElement>().FirstOrDefault(setting => setting.Key == key);
|
||||
if (appSetting == null)
|
||||
{
|
||||
APILogger.Log(
|
||||
$"## AltConfig.AppSettings.Settings.Cast<KeyValueConfigurationElement>().FirstOrDefault(setting => setting.Key == key) is null for key={key}");
|
||||
}
|
||||
return appSetting == null ? string.Empty : appSetting.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Static method to get a section from config file. Used by plugin code to get plugin library section.
|
||||
/// </summary>
|
||||
/// <param name="sectionName">string</param>
|
||||
/// <returns>object</returns>
|
||||
public static object GetSection(string sectionName)
|
||||
{
|
||||
var section = AltConfig.GetSection(sectionName);
|
||||
if (section == null)
|
||||
{
|
||||
APILogger.Log($"## AltConfig.GetSection(sectionName) is null for sectionName={sectionName}");
|
||||
}
|
||||
return section;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
|
||||
namespace DTS.Common.Core.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Event arguments describing change to settings collection
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">key type used in collection</typeparam>
|
||||
/// <typeparam name="TItem">value type used in collection</typeparam>
|
||||
public class SettingsChangedEventArgs<TKey, TItem> : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="changeType">type of scenario change</param>
|
||||
public SettingsChangedEventArgs(ChangeSettingType changeType)
|
||||
{
|
||||
ChangeType = changeType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="changeType">change type</param>
|
||||
/// <param name="key">key type used in collection</param>
|
||||
public SettingsChangedEventArgs(ChangeSettingType changeType, TKey key)
|
||||
{
|
||||
ChangeType = changeType;
|
||||
Key = key;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="changeType">change type</param>
|
||||
/// <param name="key">key type used in collection</param>
|
||||
/// <param name="item">value type used in collection</param>
|
||||
public SettingsChangedEventArgs(ChangeSettingType changeType, TKey key, TItem item)
|
||||
{
|
||||
ChangeType = changeType;
|
||||
Key = key;
|
||||
Item = item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns type of scenario change
|
||||
/// </summary>
|
||||
public ChangeSettingType ChangeType
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// key associated with changed setting
|
||||
/// </summary>
|
||||
public TKey Key
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Value associated with changed settting
|
||||
/// </summary>
|
||||
public TItem Item
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type of settings changed
|
||||
/// </summary>
|
||||
public enum ChangeSettingType
|
||||
{
|
||||
Add = 0,
|
||||
Remove = 1,
|
||||
Modified = 3,
|
||||
ClearAll = 4
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// ReSharper disable InconsistentNaming
|
||||
namespace DTS.Common.Core
|
||||
{
|
||||
public static class DTSConstants
|
||||
{
|
||||
public const string CustomConfigPath = @"C:\dev\DTS.Viewer\bin\Debug\DTS.Viewer.exe.config";
|
||||
public const string ViewerConfigPath = @"C:\dev\DTS.Viewer\bin\Debug\DTS.Viewer.exe.config";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
|
||||
namespace DTS.Common.Core.EventManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate used by event listeners
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="item"></param>
|
||||
public delegate void SubscriberCallbackDelegate<in T>(T item) where T : class;
|
||||
|
||||
public delegate void DiagnosticCallbackDelegate(EventDiagnosticType eventType, Type t, object eventData, string listener);
|
||||
|
||||
/// <summary>
|
||||
/// Allows components to publish events without knowing who is listening
|
||||
/// </summary>
|
||||
public static class EventManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Tracks listeners
|
||||
/// </summary>
|
||||
private static readonly Dictionary<Type, List<object>> SubscriberList = new Dictionary<Type, List<object>>();
|
||||
|
||||
/// <summary>
|
||||
/// Tracks diagnostic listeners
|
||||
/// </summary>
|
||||
private static readonly List<DiagnosticCallbackDelegate> DiagnosticList = new List<DiagnosticCallbackDelegate>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Publish an event
|
||||
/// </summary>
|
||||
/// <typeparam name="T">type of event</typeparam>
|
||||
/// <param name="eventData">event listener</param>
|
||||
public static void Publish<T>(T eventData) where T : class
|
||||
{
|
||||
if (!SubscriberList.ContainsKey(typeof(T))) return;
|
||||
|
||||
var listeners = SubscriberList[typeof(T)];
|
||||
|
||||
foreach (var listener in listeners)
|
||||
{
|
||||
var metaData = listener as EventMetaData<T>;
|
||||
if (metaData == null) continue;
|
||||
|
||||
var triggerCallback = true;
|
||||
|
||||
if (metaData.EventFilter != null)
|
||||
{
|
||||
triggerCallback = metaData.EventFilter(eventData);
|
||||
}
|
||||
|
||||
if (triggerCallback)
|
||||
{
|
||||
metaData.Callback(eventData);
|
||||
}
|
||||
|
||||
SendDiagnosticEvent(EventDiagnosticType.PublishEvent, typeof(T), eventData, metaData.EventFilter?.Method);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to an event
|
||||
/// </summary>
|
||||
/// <typeparam name="T">type of event</typeparam>
|
||||
/// <param name="listener">event listener</param>
|
||||
public static void Subscribe<T>(SubscriberCallbackDelegate<T> listener) where T : class
|
||||
{
|
||||
Subscribe(listener, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to an event
|
||||
/// </summary>
|
||||
/// <typeparam name="T">type of event</typeparam>
|
||||
/// <param name="listener">event listener</param>
|
||||
/// <param name="eventFilter">predicate to filter events sent to event listener</param>
|
||||
public static void Subscribe<T>(SubscriberCallbackDelegate<T> listener, Predicate<T> eventFilter) where T : class
|
||||
{
|
||||
if (!SubscriberList.ContainsKey(typeof(T)))
|
||||
{
|
||||
SubscriberList.Add(typeof(T), new List<object>());
|
||||
}
|
||||
|
||||
var listeners = SubscriberList[typeof(T)];
|
||||
|
||||
var metaData = new EventMetaData<T> { Callback = listener, EventFilter = eventFilter };
|
||||
|
||||
listeners.Add(metaData);
|
||||
|
||||
SendDiagnosticEvent(EventDiagnosticType.AddListener, typeof(T), null, listener.Method);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows a subscriber to unsubscribe
|
||||
/// </summary>
|
||||
/// <typeparam name="T">type of event</typeparam>
|
||||
/// <param name="listener">event listener</param>
|
||||
public static void UnSubscribe<T>(SubscriberCallbackDelegate<T> listener) where T : class
|
||||
{
|
||||
if (!SubscriberList.ContainsKey(typeof(T))) return;
|
||||
|
||||
var listeners = SubscriberList[typeof(T)];
|
||||
|
||||
listeners.RemoveAll(p =>
|
||||
{
|
||||
return p is EventMetaData<T> eventMetaData && eventMetaData.Callback == listener;
|
||||
});
|
||||
|
||||
SendDiagnosticEvent(EventDiagnosticType.RemoveListener, typeof(T), null, listener.Method);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all listeners
|
||||
/// </summary>
|
||||
public static void Clear()
|
||||
{
|
||||
SubscriberList.Clear();
|
||||
SendDiagnosticEvent(EventDiagnosticType.RemoveListener, null, null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add diagnostic listener
|
||||
/// </summary>
|
||||
/// <param name="listener">event listener</param>
|
||||
public static void SubscribeToDiagnosticEvents(DiagnosticCallbackDelegate listener)
|
||||
{
|
||||
DiagnosticList.Add(listener);
|
||||
SendDiagnosticEvent(EventDiagnosticType.AddListenerDiagnostic, null, null, listener.Method);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear diagnostic event
|
||||
/// </summary>
|
||||
/// <param name="listener">listener to unsubscribe</param>
|
||||
public static void UnSubscribeToDiagnosticEvents(DiagnosticCallbackDelegate listener)
|
||||
{
|
||||
DiagnosticList.RemoveAll(p => p == listener);
|
||||
SendDiagnosticEvent(EventDiagnosticType.RemoveListenerDiagnostic, null, null, listener.Method);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all diagnostic events
|
||||
/// </summary>
|
||||
public static void ClearDiagnosticEvents()
|
||||
{
|
||||
DiagnosticList.Clear();
|
||||
SendDiagnosticEvent(EventDiagnosticType.RemoveListenerDiagnostic, null, null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a diagnostic event
|
||||
/// </summary>
|
||||
/// <param name="diagnosticEventType"></param>
|
||||
/// <param name="eventType"></param>
|
||||
/// <param name="eventData"></param>
|
||||
/// <param name="listenerMethod"></param>
|
||||
private static void SendDiagnosticEvent(EventDiagnosticType diagnosticEventType, Type eventType, object eventData, MemberInfo listenerMethod)
|
||||
{
|
||||
foreach (var diagnosticEvent in DiagnosticList)
|
||||
{
|
||||
string listener = null;
|
||||
|
||||
if (listenerMethod != null && listenerMethod.DeclaringType != null)
|
||||
{
|
||||
listener = $"{listenerMethod.DeclaringType.FullName}.{listenerMethod.Name} , {listenerMethod.DeclaringType.Assembly.FullName}";
|
||||
}
|
||||
|
||||
diagnosticEvent(diagnosticEventType, eventType, eventData, listener);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public enum EventDiagnosticType
|
||||
{
|
||||
AddListener = 0,
|
||||
AddListenerDiagnostic = 1,
|
||||
PublishEvent = 2,
|
||||
RemoveListenerDiagnostic = 3,
|
||||
RemoveListener = 4
|
||||
}
|
||||
|
||||
internal class EventMetaData<T> where T : class
|
||||
{
|
||||
public Predicate<T> EventFilter { get; set; }
|
||||
public SubscriberCallbackDelegate<T> Callback { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
|
||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/></startup></configuration>
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
|
||||
namespace DTS.Common.Core.ServiceManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Event fired when a service interface is published\unpublished
|
||||
/// </summary>
|
||||
public interface IServicePublishedEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns type of service being published\unpublished
|
||||
/// </summary>
|
||||
Type ServiceType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if Service is being published, false if being unpublished
|
||||
/// </summary>
|
||||
bool IsPublished { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace DTS.Common.Core.PluginLib
|
||||
{
|
||||
/// <summary>
|
||||
/// Support class that stores data de-serialized from DatPro.Core.PluginLib.Config in App.Config configuration section
|
||||
/// </summary>
|
||||
[XmlRoot(ElementName = "DatPro.Core.PluginLib.Config")]
|
||||
public class PluginConfigData
|
||||
{
|
||||
[XmlArrayItem("Folder")]
|
||||
public string[] PluginFolders;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using DTS.Common.Core.Config;
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace DTS.Common.Core.PluginLib
|
||||
{
|
||||
/// <summary>
|
||||
/// Static class used to store plugin configuration info
|
||||
/// </summary>
|
||||
public static class PluginConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// name of setting to look in config for equipment database
|
||||
/// </summary>
|
||||
public const string DTSPlugins = "DTSPlugins";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// concatenate plugin name from app setting with class exporter name for DTS plugins
|
||||
/// </summary>
|
||||
/// <param name="setting"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetDTSPluginsSetting(string setting)
|
||||
{
|
||||
return DTSConfig.GetAppSetting(DTSPlugins) + "." + setting;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
|
||||
namespace DTS.Common.Core.ServiceManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Event fired when a service interface is published\unpublished
|
||||
/// </summary>
|
||||
public class ServicePublishedEvent : IServicePublishedEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns type of service being published\unpublished
|
||||
/// </summary>
|
||||
public Type ServiceType
|
||||
{
|
||||
get;
|
||||
internal set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if Service is being published, false if being unpublished
|
||||
/// </summary>
|
||||
public bool IsPublished
|
||||
{
|
||||
get;
|
||||
internal set;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace DTS.Common.Core.ServiceManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Service manager allows a component to publish an implementation of a singleton interface (service) that
|
||||
/// other components can retrieve reference to without knowing who published it
|
||||
/// </summary>
|
||||
public static class ServiceManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores all published interfaces
|
||||
/// </summary>
|
||||
private static readonly Dictionary<Type, object> Services = new Dictionary<Type, object>();
|
||||
|
||||
/// <summary>
|
||||
/// Publishes a service
|
||||
/// </summary>
|
||||
/// <typeparam name="T">interface type of service</typeparam>
|
||||
/// <param name="item">implementation of service</param>
|
||||
public static void Publish<T>(T item) where T : class
|
||||
{
|
||||
if (Services.ContainsKey(typeof(T)))
|
||||
{
|
||||
throw new ArgumentException($"{typeof(T).Name}: has already been published");
|
||||
}
|
||||
|
||||
Services.Add(typeof(T), item);
|
||||
SendServicePublishedEvent(typeof(T), true);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Publishes a list of services
|
||||
/// </summary>
|
||||
/// <param name="item">class that will implement all the listed interfaces</param>
|
||||
/// <param name="interfaceList">list of interfaces</param>
|
||||
/// <param name="skipPublishedInterfaces">true to avoid errors on already published interfaces</param>
|
||||
public static void Publish(object item, IEnumerable<Type> interfaceList, bool skipPublishedInterfaces)
|
||||
{
|
||||
foreach (var t in interfaceList)
|
||||
{
|
||||
if (Exists(t))
|
||||
{
|
||||
if (!skipPublishedInterfaces)
|
||||
{
|
||||
// service already published and caller indicated we should error out
|
||||
throw new ArgumentException($"{t.Name}: has already been published");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// service doesn't exist so publish it
|
||||
Services.Add(t, item);
|
||||
SendServicePublishedEvent(t, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if specifed interface has been published, false if not
|
||||
/// </summary>
|
||||
/// <typeparam name="T">type of interface to check</typeparam>
|
||||
/// <returns>true if interface has been published; false if not published</returns>
|
||||
public static bool Exists<T>() where T : class
|
||||
{
|
||||
return Services.ContainsKey(typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if specifed interface has been published, false if not
|
||||
/// </summary>
|
||||
/// <param name="t">type of interface to check</param>
|
||||
/// <returns>true if interface has been published; false if not published</returns>
|
||||
public static bool Exists(Type t)
|
||||
{
|
||||
return Services.ContainsKey(t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns published service
|
||||
/// </summary>
|
||||
/// <typeparam name="T">interface type of service</typeparam>
|
||||
/// <returns>published service or exception if not currently published</returns>
|
||||
public static T Get<T>() where T : class
|
||||
{
|
||||
if (!Services.ContainsKey(typeof(T)))
|
||||
{
|
||||
throw new ArgumentException($"{typeof(T).Name}: has not been published");
|
||||
}
|
||||
|
||||
return Services[typeof(T)] as T;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears specified service
|
||||
/// </summary>
|
||||
/// <typeparam name="T">interface type of service to clear</typeparam>
|
||||
public static void Clear<T>() where T : class
|
||||
{
|
||||
if (Services.ContainsKey(typeof(T)))
|
||||
{
|
||||
SendServicePublishedEvent(typeof(T), false);
|
||||
Services.Remove(typeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears list of published interfaces
|
||||
/// </summary>
|
||||
/// <param name="interfaceList">list of interfaces to unpublish</param>
|
||||
public static void Clear(IEnumerable<Type> interfaceList)
|
||||
{
|
||||
foreach (var t in interfaceList)
|
||||
{
|
||||
if (!Services.ContainsKey(t)) continue;
|
||||
|
||||
SendServicePublishedEvent(t, false);
|
||||
Services.Remove(t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sends a IServicePublishedEvent through the Event Manger to let subscribers know when
|
||||
/// a service is published or unpublished
|
||||
/// </summary>
|
||||
/// <param name="type">type of interface</param>
|
||||
/// <param name="published">true if being published, false if being unpublished</param>
|
||||
private static void SendServicePublishedEvent(Type type, bool published)
|
||||
{
|
||||
EventManager.EventManager.Publish<IServicePublishedEvent>(new ServicePublishedEvent
|
||||
{
|
||||
IsPublished = published,
|
||||
ServiceType = type
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
namespace DTS.Common.Core.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements a dictionary with events when an item is changed
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">key type</typeparam>
|
||||
/// <typeparam name="TItem">value type</typeparam>
|
||||
public class SettingsCollection<TKey, TItem> : IDictionary<TKey, TItem>
|
||||
{
|
||||
/// <summary>
|
||||
/// Event fired when an item changes in dictionary
|
||||
/// </summary>
|
||||
public event EventHandler<SettingsChangedEventArgs<TKey, TItem>> CollectionItemPropertyChanged;
|
||||
|
||||
/// <summary>
|
||||
/// List of stored items
|
||||
/// </summary>
|
||||
private readonly Dictionary<TKey, TItem> _items = new Dictionary<TKey, TItem>();
|
||||
|
||||
|
||||
#region IDictionary
|
||||
|
||||
/// <summary>
|
||||
/// Add item
|
||||
/// </summary>
|
||||
/// <param name="key">key</param>
|
||||
/// <param name="value">value</param>
|
||||
public void Add(TKey key, TItem value)
|
||||
{
|
||||
_items.Add(key, value);
|
||||
FireItemChangedEvent(ChangeSettingType.Add, key, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if item contains key
|
||||
/// </summary>
|
||||
/// <param name="key">key</param>
|
||||
/// <returns>true if key exists</returns>
|
||||
public bool ContainsKey(TKey key)
|
||||
{
|
||||
return _items.ContainsKey(key);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Returns all keys in collection
|
||||
/// </summary>
|
||||
public ICollection<TKey> Keys => _items.Keys;
|
||||
|
||||
/// <summary>
|
||||
/// Removes key
|
||||
/// </summary>
|
||||
/// <param name="key">key</param>
|
||||
/// <returns>true if key removed</returns>
|
||||
public bool Remove(TKey key)
|
||||
{
|
||||
var res = _items.Remove(key);
|
||||
if (res) FireItemChangedEvent(ChangeSettingType.Remove, key);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trys to get specified value
|
||||
/// </summary>
|
||||
/// <param name="key">key</param>
|
||||
/// <param name="value">value if key exists</param>
|
||||
/// <returns>true if key exists; false if it doesn't exist</returns>
|
||||
public bool TryGetValue(TKey key, out TItem value)
|
||||
{
|
||||
return _items.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of values in collection
|
||||
/// </summary>
|
||||
public ICollection<TItem> Values => _items.Values;
|
||||
|
||||
/// <summary>
|
||||
/// Gets/sets item in collection (overloads [] operator)
|
||||
/// </summary>
|
||||
/// <param name="key">key</param>
|
||||
/// <returns>item in collection</returns>
|
||||
public TItem this[TKey key]
|
||||
{
|
||||
get => _items[key];
|
||||
set
|
||||
{
|
||||
_items[key] = value;
|
||||
FireItemChangedEvent(ChangeSettingType.Add, key, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds new item
|
||||
/// </summary>
|
||||
/// <param name="item">new item to add</param>
|
||||
public void Add(KeyValuePair<TKey, TItem> item)
|
||||
{
|
||||
_items.Add(item.Key, item.Value);
|
||||
FireItemChangedEvent(ChangeSettingType.Add, item.Key, item.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears entire collection
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
_items.Clear();
|
||||
FireItemChangedEvent(ChangeSettingType.ClearAll);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if item is in collection
|
||||
/// </summary>
|
||||
/// <param name="item">item to check</param>
|
||||
/// <returns>true if item exists</returns>
|
||||
public bool Contains(KeyValuePair<TKey, TItem> item)
|
||||
{
|
||||
return _items.ContainsKey(item.Key) && _items.ContainsValue(item.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copys items to array
|
||||
/// </summary>
|
||||
/// <param name="array"></param>
|
||||
/// <param name="arrayIndex"></param>
|
||||
public void CopyTo(KeyValuePair<TKey, TItem>[] array, int arrayIndex)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns count of items
|
||||
/// </summary>
|
||||
public int Count => _items.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if readonly collection
|
||||
/// </summary>
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
/// <summary>
|
||||
/// Removes specified item
|
||||
/// </summary>
|
||||
/// <param name="item">item to remove</param>
|
||||
/// <returns>true if item removed</returns>
|
||||
public bool Remove(KeyValuePair<TKey, TItem> item)
|
||||
{
|
||||
var res = _items.Remove(item.Key);
|
||||
if (res) FireItemChangedEvent(ChangeSettingType.Remove, item.Key);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return enumerator for collection
|
||||
/// </summary>
|
||||
/// <returns>enumerator</returns>
|
||||
public IEnumerator<KeyValuePair<TKey, TItem>> GetEnumerator()
|
||||
{
|
||||
return _items.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return enumerator for collection
|
||||
/// </summary>
|
||||
/// <returns>enumerator</returns>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return _items.GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private methods
|
||||
|
||||
/// <summary>
|
||||
/// Fires collection change event
|
||||
/// </summary>
|
||||
/// <param name="changeType">type of change</param>
|
||||
private void FireItemChangedEvent(ChangeSettingType changeType)
|
||||
{
|
||||
CollectionItemPropertyChanged?.Invoke(this, new SettingsChangedEventArgs<TKey, TItem>(changeType));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fires collection change event
|
||||
/// </summary>
|
||||
/// <param name="changeType">type of change</param>
|
||||
/// <param name="key">key</param>
|
||||
private void FireItemChangedEvent(ChangeSettingType changeType, TKey key)
|
||||
{
|
||||
CollectionItemPropertyChanged?.Invoke(this, new SettingsChangedEventArgs<TKey, TItem>(changeType, key));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fires collection change event
|
||||
/// </summary>
|
||||
/// <param name="changeType">type of change</param>
|
||||
/// <param name="key">key</param>
|
||||
/// <param name="item">item</param>
|
||||
private void FireItemChangedEvent(ChangeSettingType changeType, TKey key, TItem item)
|
||||
{
|
||||
CollectionItemPropertyChanged?.Invoke(this, new SettingsChangedEventArgs<TKey, TItem>(changeType, key, item));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{FAB1F470-1574-4301-B56E-D3364AA93679}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>DTS.Common.Core</RootNamespace>
|
||||
<AssemblyName>DTS.Common.Core</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<SccProjectName>
|
||||
</SccProjectName>
|
||||
<SccLocalPath>
|
||||
</SccLocalPath>
|
||||
<SccAuxPath>
|
||||
</SccAuxPath>
|
||||
<SccProvider>
|
||||
</SccProvider>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.Composition" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Config\DTSConfig.cs" />
|
||||
<Compile Include="DTSConstants.cs" />
|
||||
<Compile Include="EventManager\EventManager.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ServiceManager\IServicePublishedEvent.cs" />
|
||||
<Compile Include="ServiceManager\ServiceManager.cs" />
|
||||
<Compile Include="ServiceManager\ServicePublishedEvent.cs" />
|
||||
<Compile Include="Settings\SettingsChangedEventArgs.cs" />
|
||||
<Compile Include="Settings\SettingsCollection.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<None Include="Design\DTS.Common.CoreClassDiagram.cd" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DTS.Common.Utilities\DTS.Common.Utilities.csproj">
|
||||
<Project>{03EACE47-EA59-44AC-B49D-956E4DC4D618}</Project>
|
||||
<Name>DTS.Common.Utilities</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PreBuildEvent>
|
||||
</PreBuildEvent>
|
||||
</PropertyGroup>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -0,0 +1,272 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.IO;
|
||||
using System.ComponentModel.Composition.Hosting;
|
||||
using System.ComponentModel.Composition.ReflectionModel;
|
||||
using System.Linq;
|
||||
using DTS.Common.Core.Config;
|
||||
using DTS.Common.Utilities.Logging;
|
||||
|
||||
// ReSharper disable RedundantJumpStatement
|
||||
// ReSharper disable UnusedTypeParameter
|
||||
// ReSharper disable UnusedMember.Local
|
||||
// ReSharper disable UnusedParameter.Local
|
||||
|
||||
namespace DTS.Common.Core.PluginLib
|
||||
{
|
||||
/// <summary>
|
||||
/// PluginManager class to manage MEF plugins
|
||||
/// NOTE: This class has to be thread safe
|
||||
/// </summary>
|
||||
public class PluginManager
|
||||
{
|
||||
// reference to singleton plugin manager
|
||||
private static PluginManager _pluginManager;
|
||||
|
||||
/// <summary>
|
||||
/// MEF catalog
|
||||
/// </summary>
|
||||
public AggregateCatalog PluginCatalog { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// MEF container
|
||||
/// </summary>
|
||||
private CompositionContainer PluginContainer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Thread lock
|
||||
/// </summary>
|
||||
private static readonly object THREAD_LOCK = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that intializes catalog and container
|
||||
/// </summary>
|
||||
private PluginManager(string appPath)
|
||||
{
|
||||
// Create MEF catalog
|
||||
PluginCatalog = new AggregateCatalog();
|
||||
DTSConfig.DTSConfigInit(appPath);
|
||||
// Get plugin folder from App.Config;
|
||||
var pcsh = (PluginConfigSectionHandler)DTSConfig.GetSection("DTS.Common.Core.PluginLib.Config"); //DTS.Common.Core.PluginLib.Config
|
||||
if (pcsh == null)
|
||||
{
|
||||
APILogger.Log("## Unable to retrieve plugin config data from DTS.Config (DTS.Common.Core.PluginLib.Config)");
|
||||
throw new Exception("Unable to retrieve plugin config data from DTS.config (DTS.Common.Core.PluginLib.Config)");
|
||||
}
|
||||
|
||||
|
||||
foreach (FilterHashElement element in pcsh.HashKeys)
|
||||
{
|
||||
try
|
||||
{
|
||||
var info = new DirectoryInfo(element.Value);
|
||||
if (!info.Exists)
|
||||
{
|
||||
APILogger.Log($"## Plugin directory does not exist: {element.Value}");
|
||||
throw new IOException($"Plugin directory does not exist: {element.Value}");
|
||||
}
|
||||
|
||||
PluginCatalog.Catalogs.Add(new DirectoryCatalog(element.Value));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
APILogger.Log(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// Create MEF container
|
||||
PluginContainer = new CompositionContainer(PluginCatalog);
|
||||
|
||||
foreach (var catalog in PluginCatalog.Catalogs)
|
||||
{
|
||||
var directoryCatalog = catalog as DirectoryCatalog;
|
||||
|
||||
if (directoryCatalog == null)
|
||||
{
|
||||
APILogger.Log("## directory catalog is null");
|
||||
continue;
|
||||
}
|
||||
var pluginDir = directoryCatalog.FullPath;
|
||||
|
||||
var files = new DirectoryInfo(pluginDir).GetFiles("*.dll");
|
||||
|
||||
foreach (var assembly in files.Where(f => !f.Name.StartsWith("DTS.Common") && !f.Name.StartsWith("C1") && !f.Name.StartsWith("Xceed")))
|
||||
{
|
||||
var assemblyName = pluginDir + @"\" + assembly.Name;
|
||||
//var assemblyName = $"{pluginDir}{assembly.Name}";
|
||||
// verift assembly exists and matches requested paramaters
|
||||
if (!File.Exists(assemblyName))
|
||||
{
|
||||
APILogger.Log($"## File Not Found!: {assemblyName}");
|
||||
continue;
|
||||
}
|
||||
|
||||
//var info = AssemblyName.GetAssemblyName(assemblyName);
|
||||
|
||||
// NOTE: sometimes args.Name only has short assembly name, sometimes full name with public key and version
|
||||
//if (info == null || (info.FullName != args.Name && (info.Name != args.Name)))
|
||||
//{
|
||||
// continue;
|
||||
//}
|
||||
|
||||
// Load assembly
|
||||
try
|
||||
{
|
||||
var asm = Assembly.LoadFrom(assemblyName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
APILogger.Log("## Failed to load assembly:", ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public List<Assembly> GetPluginList<T>() where T : class
|
||||
{
|
||||
var assemblyList = new List<Assembly>();
|
||||
var manager = GetPluginManager(string.Empty);
|
||||
foreach (var catalog in manager.PluginCatalog.Catalogs)
|
||||
{
|
||||
var directoryCatalog = catalog as DirectoryCatalog;
|
||||
if (directoryCatalog == null) continue;
|
||||
assemblyList.AddRange(directoryCatalog.Parts.Select(part => ReflectionModelServices.GetPartType(part).Value.Assembly).Distinct().ToList());
|
||||
|
||||
}
|
||||
return assemblyList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns MEF plugin of type t
|
||||
/// Throws error if no type exported by plugin or more than one plugin has exported type
|
||||
/// </summary>
|
||||
/// <typeparam name="T">type to get</typeparam>
|
||||
/// <returns>reference to type t from MEF plugin</returns>
|
||||
public static T GetPlugin<T>() where T : class
|
||||
{
|
||||
var export = GetPluginManager(string.Empty).PluginContainer.GetExport<T>();
|
||||
return export?.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns MEF plugin of type T from a collection of plugins that export the same type
|
||||
/// </summary>
|
||||
/// <typeparam name="T">exported type implemented by potentially multiple plugins</typeparam>
|
||||
/// <param name="configPluginSetting">string that tells us which specific plugin we want</param>
|
||||
/// <returns>type T</returns>
|
||||
public static T GetPlugin<T>(string configPluginSetting) where T : class
|
||||
{
|
||||
var result = new Lazy<T>();
|
||||
var manager = GetPluginManager(configPluginSetting);
|
||||
var plugins = manager.PluginContainer.GetExports<T>();
|
||||
|
||||
//loop through plugins returned and get the specific one we're looking for
|
||||
foreach (var item in plugins)
|
||||
{
|
||||
if (item.Value.ToString() != configPluginSetting) continue;
|
||||
result = item;
|
||||
break;
|
||||
}
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns list of MEF plugins that export type t
|
||||
/// </summary>
|
||||
/// <typeparam name="T">type to get</typeparam>
|
||||
/// <returns>list of references to type t from MEF plugins</returns>
|
||||
public static IEnumerable<Lazy<T>> GetPlugins<T>() where T : class
|
||||
{
|
||||
var manager = GetPluginManager(string.Empty);
|
||||
return manager.PluginContainer.GetExports<T>();
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns static singleton reference to plugin manager class
|
||||
/// </summary>
|
||||
/// <returns>plugin class</returns>
|
||||
public static PluginManager GetPluginManager(string appPath)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(appPath))
|
||||
{
|
||||
var directoryInfo = new DirectoryInfo(appPath);
|
||||
}
|
||||
lock (THREAD_LOCK)
|
||||
{
|
||||
if (_pluginManager != null)
|
||||
{
|
||||
foreach (var catalog in _pluginManager.PluginCatalog.Catalogs)
|
||||
{
|
||||
var directoryCatalog = catalog as DirectoryCatalog;
|
||||
if (directoryCatalog == null) continue;
|
||||
}
|
||||
}
|
||||
return _pluginManager ?? (_pluginManager = new PluginManager(appPath));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This event triggered if system can't find plugin dependancies
|
||||
/// CurrentDomain_AssemblyResolve can search through list of directories instead of just one
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
|
||||
{
|
||||
foreach (var catalog in PluginCatalog.Catalogs)
|
||||
{
|
||||
var directoryCatalog = catalog as DirectoryCatalog;
|
||||
if (directoryCatalog == null)
|
||||
{
|
||||
APILogger.Log("## null directoryCatalog");
|
||||
continue;
|
||||
}
|
||||
var pluginDir = directoryCatalog.FullPath;
|
||||
|
||||
// get name of assembly
|
||||
var baseName = args.Name.Split(new[] { ',' })[0];
|
||||
var assemblyName = $"{pluginDir}\\{baseName}.dll";
|
||||
|
||||
// verift assembly exists and matches requested paramaters
|
||||
if (!File.Exists(assemblyName))
|
||||
{
|
||||
APILogger.Log($"## assembly does not exist: {assemblyName}");
|
||||
continue;
|
||||
}
|
||||
var info = AssemblyName.GetAssemblyName(assemblyName);
|
||||
|
||||
// NOTE: sometimes args.Name only has short assembly name, sometimes full name with public key and version
|
||||
if (info == null || info.FullName != args.Name && info.Name != args.Name)
|
||||
{
|
||||
APILogger.Log("## assembly info is incomplete");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Load assembly
|
||||
try
|
||||
{
|
||||
var asm = Assembly.LoadFrom(assemblyName);
|
||||
return asm;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
APILogger.Log("## failed to load", ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("DTS.Common.Core")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("DTS.Common.Core")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2016")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("bdf5ad7a-51db-4ad0-8186-d1ead7405848")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
Reference in New Issue
Block a user