17 using System.Collections;
18 using System.Collections.Concurrent;
19 using System.Collections.Generic;
20 using System.ComponentModel.Composition;
21 using System.ComponentModel.Composition.Hosting;
22 using System.ComponentModel.Composition.Primitives;
23 using System.ComponentModel.Composition.ReflectionModel;
26 using System.Reflection;
27 using System.Threading;
28 using System.Threading.Tasks;
40 private static string PluginDirectory;
41 private static readonly Lazy<Composer> LazyComposer =
new Lazy<Composer>(
44 PluginDirectory =
Config.
Get(
"plugin-directory");
63 var dllDirectoryString =
Config.
Get(
"composer-dll-directory");
64 if (
string.IsNullOrWhiteSpace(dllDirectoryString))
67 if (!
string.IsNullOrEmpty(AppDomain.CurrentDomain.BaseDirectory) && Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory,
"QuantConnect.*.dll").Any())
69 dllDirectoryString = AppDomain.CurrentDomain.BaseDirectory;
75 var currentDirectory = Directory.GetCurrentDirectory();
76 var parentDirectory = Directory.GetParent(currentDirectory)?.FullName ?? currentDirectory;
80 dllDirectoryString = Directory.GetFiles(parentDirectory,
"QuantConnect.*.dll").Any() ? parentDirectory : currentDirectory;
85 var primaryDllLookupDirectory =
new DirectoryInfo(dllDirectoryString).FullName;
86 Log.
Trace($
"Composer(): Loading Assemblies from {primaryDllLookupDirectory}");
88 var loadFromPluginDir = !
string.IsNullOrWhiteSpace(PluginDirectory)
89 && Directory.Exists(PluginDirectory) &&
90 new DirectoryInfo(PluginDirectory).FullName != primaryDllLookupDirectory;
91 _composableParts = Task.Run(() =>
95 var catalogs =
new List<ComposablePartCatalog>
97 new DirectoryCatalog(primaryDllLookupDirectory,
"*.dll"),
98 new DirectoryCatalog(primaryDllLookupDirectory,
"*.exe")
100 if (loadFromPluginDir)
102 catalogs.Add(
new DirectoryCatalog(PluginDirectory,
"*.dll"));
104 var aggregate =
new AggregateCatalog(catalogs);
105 _compositionContainer =
new CompositionContainer(aggregate);
106 return _compositionContainer.Catalog.Parts.ToList();
108 catch (Exception exception)
111 if (!(exception is ThreadAbortException))
116 return new List<ComposablePartDefinition>();
121 var exportedTypes =
new ConcurrentBag<Type>();
122 var fileNames = Directory.EnumerateFiles(primaryDllLookupDirectory, $
"{nameof(QuantConnect)}.*.dll");
123 if (loadFromPluginDir)
125 fileNames = fileNames.Concat(Directory.EnumerateFiles(PluginDirectory, $
"{nameof(QuantConnect)}.*.dll"));
129 var files =
new Dictionary<string, string>();
130 foreach (var filePath
in fileNames)
132 var fileName = Path.GetFileName(filePath);
133 if (!
string.IsNullOrEmpty(fileName))
135 files[fileName] = filePath;
138 Parallel.ForEach(files.Values,
144 Assembly.LoadFrom(file).ExportedTypes.Where(type => !type.IsAbstract && !type.IsInterface && !type.IsEnum))
146 exportedTypes.Add(type);
155 _exportedTypes =
new List<Type>(exportedTypes);
158 private CompositionContainer _compositionContainer;
159 private readonly IReadOnlyList<Type> _exportedTypes;
160 private readonly Task<List<ComposablePartDefinition>> _composableParts;
161 private readonly
object _exportedValuesLockObject =
new object();
162 private readonly Dictionary<Type, IEnumerable> _exportedValues =
new Dictionary<Type, IEnumerable>();
169 public T Single<T>(Func<T, bool> predicate)
171 if (predicate ==
null)
173 throw new ArgumentNullException(nameof(predicate));
176 return GetExportedValues<T>().Single(predicate);
184 public void AddPart<T>(T instance)
186 lock (_exportedValuesLockObject)
189 if (_exportedValues.TryGetValue(typeof(T), out values))
191 ((IList<T>)values).Add(instance);
195 values =
new List<T> { instance };
196 _exportedValues[typeof(T)] = values;
205 public T GetPart<T>()
207 return GetPart<T>(
null);
214 public T GetPart<T>(Func<T, bool> filter)
216 lock (_exportedValuesLockObject)
219 if (_exportedValues.TryGetValue(typeof(T), out values))
221 return ((IList<T>)values).Where(x => filter ==
null || filter(x)).FirstOrDefault();
230 public IEnumerable<Type> GetExportedTypes<T>() where T : class
232 var type = typeof(T);
233 return _exportedTypes.Where(type1 =>
237 return type.IsAssignableFrom(type1);
257 public T GetExportedValueByTypeName<T>(
string typeName,
bool forceTypeNameOnExisting =
true)
264 var type = typeof(T);
265 lock (_exportedValuesLockObject)
267 if (_exportedValues.TryGetValue(type, out values))
270 instance = values.OfType<T>().FirstOrDefault(x => !forceTypeNameOnExisting || x.GetType().MatchesTypeName(typeName));
271 if (instance !=
null)
278 var typeT = _exportedTypes.Where(type1 =>
282 return type.IsAssignableFrom(type1) && type1.MatchesTypeName(typeName);
293 instance = (T)Activator.CreateInstance(typeT);
296 if (instance ==
null)
299 var selectedPart = _composableParts.Result
304 var xType = ReflectionModelServices.GetPartType(x).Value;
305 return type.IsAssignableFrom(xType) && xType.MatchesTypeName(typeName);
315 if (selectedPart ==
null)
317 throw new ArgumentException(
318 $
"Unable to locate any exports matching the requested typeName: {typeName}. Type: {type}", nameof(typeName));
321 var exportDefinition =
322 selectedPart.ExportDefinitions.First(
323 x => x.ContractName == AttributedModelServices.GetContractName(type));
324 instance = (T)selectedPart.CreatePart().GetExportedValue(exportDefinition);
327 var exportedParts = instance.GetType().GetInterfaces()
328 .Where(interfaceType => interfaceType.GetCustomAttribute<InheritedExportAttribute>() !=
null);
330 lock (_exportedValuesLockObject)
332 foreach (var export
in exportedParts)
334 var exportList = _exportedValues.SingleOrDefault(kvp => kvp.Key == export).Value;
337 if (exportList ==
null)
339 var list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(export));
341 _exportedValues[export] = list;
345 ((IList)exportList).Add(instance);
352 catch (ReflectionTypeLoadException err)
354 foreach (var exception
in err.LoaderExceptions)
360 if (err.InnerException !=
null)
Log.
Error(err.InnerException);
368 public IEnumerable<T> GetExportedValues<T>()
372 lock (_exportedValuesLockObject)
375 if (_exportedValues.TryGetValue(typeof(T), out values))
377 return values.OfType<T>();
380 if (!_composableParts.IsCompleted)
382 _composableParts.Wait();
384 values = _compositionContainer.GetExportedValues<T>().ToList();
385 _exportedValues[typeof(T)] = values;
386 return values.OfType<T>();
389 catch (ReflectionTypeLoadException err)
391 foreach (var exception
in err.LoaderExceptions)
405 lock (_exportedValuesLockObject)
407 _exportedValues.Clear();