public static async Task <List <T> > LoadSingle <T>(string file, ILogger logger, IConfiguration config, bool isInDevMode) { var list = new List <T>(); try { var fileInfo = new FileInfo(file); var folder = fileInfo.DirectoryName; logger.LogDebug($"Loading file {file} and check for interfaces"); AssemblyLoader loader; if (isInDevMode) { loader = new AssemblyLoader(folder, new FileInfo(Assembly.GetCallingAssembly().Location).DirectoryName, folder); } else { loader = new AssemblyLoader(folder, folder); } Func <AssemblyLoadContext, AssemblyName, Assembly> assemblyLoader = (context, name) => { logger.LogDebug($"Try to load assembly {name} for {file}"); // avoid loading *.resources dll, because of: https://github.com/dotnet/coreclr/issues/8416 if (name.Name.EndsWith("resources")) { return(null); } logger.LogDebug($"try to load assembly from {folder}"); var foundDll = Directory.GetFileSystemEntries(folder, name.Name + ".dll", SearchOption.AllDirectories); if (foundDll.Any()) { return(context.LoadFromAssemblyPath(foundDll[0])); } var secondPath = new FileInfo(Assembly.GetExecutingAssembly().Location).DirectoryName; logger.LogDebug($"try to load assembly from second path: {secondPath}"); foundDll = Directory.GetFileSystemEntries(secondPath, name.Name + ".dll", SearchOption.AllDirectories); if (foundDll.Any()) { return(context.LoadFromAssemblyPath(foundDll[0])); } var dependencies = DependencyContext.Default.RuntimeLibraries; foreach (var library in dependencies) { if (IsCandidateLibrary(library, name)) { return(loader.LoadAssembly(name)); } } return(context.LoadFromAssemblyName(name)); }; AssemblyLoadContext.Default.Resolving += assemblyLoader; Assembly assembly; try { assembly = Assembly.LoadFrom(file); } catch (Exception e) { logger.LogError(e, $"Could not load assembly {file}"); return(list); } finally { AssemblyLoadContext.Default.Resolving -= assemblyLoader; } var resources = assembly.GetManifestResourceNames() .SingleOrDefault(a => a.EndsWith("automatica-manifest.json")); if (resources == null) { logger.LogWarning($"{file} does not contain a manifest files"); throw new NoManifestFoundException(); } var manifest = Common.Update.Plugin.GetEmbeddedPluginManifest(logger, assembly); await using var database = new AutomaticaContext(config); var plugin = database.Plugins.SingleOrDefault(a => a.PluginGuid == manifest.Automatica.PluginGuid); if (plugin == null) { plugin = new Plugin { ObjId = Guid.NewGuid(), Name = manifest.Automatica.Name, ComponentName = manifest.Automatica.ComponentName, PluginGuid = manifest.Automatica.PluginGuid, PluginType = manifest.Automatica.Type == "driver" ? PluginType.Driver : PluginType.Logic, Version = manifest.Automatica.PluginVersion.ToString(), Loaded = true }; database.Plugins.Add(plugin); } else { plugin.Version = manifest.Automatica.PluginVersion.ToString(); plugin.Loaded = true; database.Plugins.Update(plugin); } await database.SaveChangesAsync(); foreach (var ti in assembly.ExportedTypes) { if (ti.IsSubclassOf(typeof(T))) { if (ti.IsAbstract) { continue; } if (assembly.CreateInstance(ti.FullName ?? throw new InvalidOperationException()) is T factory) { logger.LogDebug($"Found {typeof(T)} in {ti}..."); list.Add(factory); } } } } catch (ReflectionTypeLoadException e) { var builder = new StringBuilder(); foreach (var ex in e.LoaderExceptions) { builder.Append($"{ex}"); } logger.LogError(e.InnerException, $"Could not load {typeof(T)} from {file}...{builder}\n"); } catch (NoManifestFoundException) { // ignore } catch (Exception e) { logger.LogError(e, $"Could not load {typeof(T)}...{e}"); } return(list); }
internal async Task <IEnumerable <RulePage> > Save(List <RulePage> data) { await using var dbContext = new AutomaticaContext(_config); { var transaction = await dbContext.Database.BeginTransactionAsync(); try { foreach (var page in data) { var isNewPage = false; var dbPage = dbContext.RulePages.SingleOrDefault(a => a.ObjId == page.ObjId); if (dbPage == null) { dbPage = page; isNewPage = true; } else { dbContext.Entry(dbPage).State = EntityState.Detached; } if (page.Description == null) { page.Description = ""; } foreach (var ruleInstance in page.RuleInstance) { if (ruleInstance.Description == null) { ruleInstance.Description = ""; } foreach (var ruleInterface in ruleInstance.RuleInterfaceInstance) { ruleInterface.This2RuleInterfaceTemplateNavigation = null; if (dbContext.RuleInterfaceInstances.AsNoTracking() .SingleOrDefault(a => a.ObjId == ruleInterface.ObjId) != null) { dbContext.Entry(ruleInterface).State = EntityState.Modified; dbContext.RuleInterfaceInstances.Update(ruleInterface); } else { dbContext.RuleInterfaceInstances.Add(ruleInterface); dbContext.Entry(ruleInterface).State = EntityState.Added; } } ruleInstance.This2RulePage = page.ObjId; ruleInstance.This2RuleTemplate = ruleInstance.This2RuleTemplateNavigation.ObjId; ruleInstance.This2RuleTemplateNavigation = null; if (dbContext.RuleInstances.AsNoTracking() .SingleOrDefault(a => a.ObjId == ruleInstance.ObjId) == null) { dbContext.RuleInstances.Add(ruleInstance); dbContext.Entry(ruleInstance).State = EntityState.Added; } else { dbContext.Entry(ruleInstance).State = EntityState.Modified; dbContext.RuleInstances.Update(ruleInstance); } } foreach (var node in page.NodeInstance2RulePage) { if (dbContext.NodeInstance2RulePages.AsNoTracking() .SingleOrDefault(a => a.ObjId == node.ObjId) == null) { node.This2NodeInstanceNavigation = null; await dbContext.AddAsync(node); dbContext.Entry(node).State = EntityState.Added; } else { dbContext.Entry(node).State = EntityState.Modified; dbContext.NodeInstance2RulePages.Update(node); } } foreach (var link in page.Link) { link.This2RulePage = dbPage.ObjId; link.This2RuleInterfaceInstanceOutputNavigation = null; link.This2RuleInterfaceInstanceInputNavigation = null; link.This2NodeInstance2RulePageInputNavigation = null; link.This2NodeInstance2RulePageOutputNavigation = null; if (dbContext.Links.AsNoTracking().SingleOrDefault(a => a.ObjId == link.ObjId) == null) { await dbContext.AddAsync(link); dbContext.Entry(link).State = EntityState.Added; } else { dbContext.Update(link); dbContext.Entry(link).State = EntityState.Modified; } } if (isNewPage) { dbContext.Entry(page).State = EntityState.Added; await dbContext.RulePages.AddAsync(page); } else { dbContext.Entry(page).State = EntityState.Modified; dbContext.RulePages.Update(page); } } await dbContext.SaveChangesAsync(true); foreach (var page in data) { var removedRules = from c in dbContext.RuleInstances where !(from o in page.RuleInstance select o.ObjId).Contains(c.ObjId) && c.This2RulePage == page.ObjId select c; var removedRulesList = removedRules.ToList(); dbContext.RuleInstances.RemoveRange(removedRulesList); var removedLinks = from c in dbContext.Links where !(from o in page.Link select o.ObjId).Contains(c.ObjId) && c.This2RulePage == page.ObjId select c; dbContext.Links.RemoveRange(removedLinks); var removedNodes = (from c in dbContext.NodeInstance2RulePages where !(from o in page.NodeInstance2RulePage select o.ObjId).Contains(c.ObjId) && c.This2RulePage == page.ObjId select c).ToList(); dbContext.NodeInstance2RulePages.RemoveRange(removedNodes); } var removedPages = (from c in dbContext.RulePages where !(from o in data select o.ObjId).Contains(c.ObjId) select c).ToList(); foreach (var removedPage in removedPages) { var removedRules = from c in dbContext.RuleInstances where c.This2RulePage == removedPage.ObjId select c; dbContext.RuleInstances.RemoveRange(removedRules); var removedLinks = from c in dbContext.Links where c.This2RulePage == removedPage.ObjId select c; dbContext.Links.RemoveRange(removedLinks); var removedNodes = (from c in dbContext.NodeInstance2RulePages where c.This2RulePage == removedPage.ObjId select c).ToList(); dbContext.NodeInstance2RulePages.RemoveRange(removedNodes); } dbContext.RemoveRange(removedPages); await dbContext.SaveChangesAsync(true); await transaction.CommitAsync(); _logicCacheFacade.ClearInstances(); } catch (Exception e) { SystemLogger.Instance.LogError(e, "Could not save data"); await transaction.RollbackAsync(); throw; } } return(GetPages()); }