using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Props.Data; using Props.Models.Search; using Props.Options; using Props.Shop.Framework; namespace Props.Services.Modules { public class ModularShopManager : IShopManager { private ILogger logger; private Dictionary shops; private ModulesOptions options; private IConfiguration configuration; private bool disposedValue; public ModularShopManager(IConfiguration configuration, ILogger logger) { this.configuration = configuration; this.logger = logger; options = configuration.GetSection(ModulesOptions.Modules).Get(); shops = new Dictionary(); foreach (IShop shop in LoadShops(options.ShopsDir, options.ShopRegex, options.RecursiveLoad)) { if (!shops.TryAdd(shop.ShopName, shop)) { logger.LogWarning("Duplicate shop {0} detected. Ignoring the latter.", shop.ShopName); } } } public IEnumerable GetAllShopNames() { return shops.Keys; } public IShop GetShop(string name) { return shops[name]; } public IEnumerable GetAllShops() { return shops.Values; } private IEnumerable LoadShops(string shopsDir, string shopRegex, bool recursiveLoad) { Stack asyncInitTasks = new Stack(); Stack directories = new Stack(); directories.Push(shopsDir); string currentDirectory = null; while (directories.TryPop(out currentDirectory)) { if (recursiveLoad) { foreach (string dir in Directory.EnumerateDirectories(currentDirectory)) { directories.Push(dir); } } foreach (string file in Directory.EnumerateFiles(currentDirectory)) { if (Path.GetExtension(file).Equals(".dll") && Regex.IsMatch(file, shopRegex)) { ShopAssemblyLoadContext context = new ShopAssemblyLoadContext(file); Assembly assembly = context.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension(file))); int success = 0; foreach (Type type in assembly.GetTypes()) { if (typeof(IShop).IsAssignableFrom(type)) { IShop shop = Activator.CreateInstance(type) as IShop; if (shop != null) { // TODO: load persisted shop data. shop.Initialize(null); asyncInitTasks.Push(shop.InitializeAsync(null)); success += 1; logger.LogDebug("Loaded \"{0}\".", shop.ShopName); yield return shop; } } } if (success == 0) { logger.LogWarning("There were no shops found within the assembly at path \"{0}\".", file); } } } } logger.LogDebug("Waiting for all shops to finish asynchronous initialization."); Task.WaitAll(asyncInitTasks.ToArray()); logger.LogDebug("All shops finished asynchronous initialization."); } protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { foreach (string shopName in shops.Keys) { // TODO: Get shop data to persist. shops[shopName].Dispose(); } } disposedValue = true; } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } }