diff --git a/src/Connected.Host/DependencyLoader.cs b/src/Connected.Host/DependencyLoader.cs
new file mode 100644
index 0000000..dbf160d
--- /dev/null
+++ b/src/Connected.Host/DependencyLoader.cs
@@ -0,0 +1,343 @@
+using System.Reflection;
+using System.Runtime.Versioning;
+using Connected.Host.Configuration;
+using Microsoft.Extensions.DependencyModel;
+using NuGet.Configuration;
+using NuGet.Frameworks;
+using NuGet.Packaging;
+using NuGet.Packaging.Core;
+using NuGet.Packaging.Signing;
+using NuGet.Protocol.Core.Types;
+using NuGet.Resolver;
+using NuGet.Versioning;
+
+
+namespace Connected.Host;
+
+public class DependencyLoader
+{
+ ///
+ /// Loads packages from the specified repositories into memory.
+ ///
+ /// A list of package Id and version descriptors to load.
+ /// A list of NuGet repositories to check.
+ ///
+ /// Thrown when a package identity could not be resolved.
+ private async Task Load(IEnumerable packages, IEnumerable repositoryLocations, CancellationToken? cancellationToken = null)
+ {
+ cancellationToken ??= CancellationToken.None;
+
+ //TODO: Wire up actual logger
+ var logger = new NuGet.Common.NullLogger();
+
+ /*
+ * Define a source provider with specified sources.
+ */
+ var packageSources = GetPackageSources(repositoryLocations);
+ /*
+ * Define a package source provider with empty settings with the specified package sources.
+ */
+ var packageSourceProvider = new PackageSourceProvider(Settings.LoadMachineWideSettings(Settings.DefaultSettingsFileName), packageSources);
+ /*
+ * Define a repository provider with our package source and the latest API capabilities.
+ */
+ var repositoryProvider = new SourceRepositoryProvider(packageSourceProvider, Repository.Provider.GetCoreV3());
+ /*
+ * Setup contexts for dependency resolution and package downloading.
+ * First, the disposable cache
+ */
+ using var sourceCacheContext = new SourceCacheContext();
+ /*
+ * Next, the compatible framework versions.
+ */
+ var version = Assembly.GetExecutingAssembly()
+ .GetCustomAttributes(typeof(TargetFrameworkAttribute), false)
+ .SingleOrDefault() as TargetFrameworkAttribute;
+
+ var targetFramework = NuGetFramework.ParseFrameworkName(version.FrameworkName, DefaultFrameworkNameProvider.Instance);
+ /*
+ * Complete list of all touched packages, to be filled out by installer and downloader.
+ */
+ var allPackages = new HashSet();
+ /*
+ * Default dependency context.
+ */
+ var dependencyContext = DependencyContext.Default;
+ /*
+ * Extract list of repositories.
+ */
+ var repositories = repositoryProvider.GetRepositories();
+ /*
+ * Get all package identities and packages.
+ */
+ foreach (var dependency in packages)
+ {
+ var packageIdentity = await GetPackageIdentity(dependency, sourceCacheContext, logger, repositories, cancellationToken.Value);
+
+ if (packageIdentity is null)
+ throw new InvalidOperationException($"Cannot find package {dependency.Name}.");
+
+ await GetPackageDependencies(packageIdentity, sourceCacheContext, targetFramework, logger, repositories, dependencyContext, allPackages, cancellationToken.Value);
+ }
+
+ var packagesToInstall = GetPackagesToInstall(repositoryProvider, logger, packages, allPackages);
+ /*
+ * Install packages in standard location.
+ */
+ var packageDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, ".packages");
+
+ var nugetSettings = Settings.LoadDefaultSettings(packageDirectory);
+
+ await InstallPackages(sourceCacheContext, logger, packagesToInstall, packageDirectory, nugetSettings, cancellationToken.Value);
+ }
+ ///
+ /// Installs the list of packages and loads them into memory.
+ ///
+ /// The shared context for resolved packages.
+ /// A standard NuGet logger.
+ /// A list of packages to install.
+ /// The root directory into which the packages are downloaded and extracted.
+ /// A set of NuGet settings, containing local repos and download behavior settings.
+ ///
+ private async Task InstallPackages(SourceCacheContext sourceCacheContext, NuGet.Common.ILogger logger,
+ IEnumerable packagesToInstall, string rootPackagesDirectory,
+ ISettings nugetSettings, CancellationToken cancellationToken)
+ {
+ var packagePathResolver = new PackagePathResolver(rootPackagesDirectory, true);
+ var packageExtractionContext = new PackageExtractionContext(
+ PackageSaveMode.Defaultv3,
+ XmlDocFileSaveMode.Skip,
+ ClientPolicyContext.GetClientPolicy(nugetSettings, logger),
+ logger);
+
+ foreach (var package in packagesToInstall)
+ {
+ var downloadResource = await package.Source.GetResourceAsync(cancellationToken);
+
+ /*
+ * Download package (from online, local or shared cache).
+ */
+ var downloadResult = await downloadResource.GetDownloadResourceResultAsync(
+ package,
+ new PackageDownloadContext(sourceCacheContext),
+ SettingsUtility.GetGlobalPackagesFolder(nugetSettings),
+ logger,
+ cancellationToken);
+
+ /*
+ * Extract package.
+ */
+ var extractedFiles = await PackageExtractor.ExtractPackageAsync(
+ downloadResult.PackageSource,
+ downloadResult.PackageStream,
+ packagePathResolver,
+ packageExtractionContext,
+ cancellationToken);
+
+ /*
+ * Install applicable dlls.
+ */
+ foreach (var assembly in extractedFiles.Where(e => e.EndsWith(".dll")))
+ {
+ try
+ {
+ Assembly.LoadFrom(assembly);
+ }
+ catch (Exception e)
+ {
+ logger.LogWarning($"Could not load assembly. {e}");
+ }
+ }
+ }
+ }
+
+ ///
+ /// Generates a list of package sources from a list of repository urls.
+ ///
+ /// A list of URIs of specified sources.
+ /// A list of package sources for the specified NuGet repository URIs.
+ private IEnumerable GetPackageSources(IEnumerable sources)
+ {
+ foreach (var source in sources)
+ yield return new PackageSource(source);
+ }
+
+ ///
+ /// Returns a list of missing packages to satisfy all dependencies.
+ ///
+ /// A source repository provider for all resources to load packages from.
+ /// A standard NuGet logger.
+ /// A list of packages for which to resolve all dependencies.
+ /// A list of available packages to resolve from.
+ ///
+ private IEnumerable GetPackagesToInstall(SourceRepositoryProvider sourceRepositoryProvider, NuGet.Common.ILogger logger, IEnumerable packages, HashSet availablePackages)
+ {
+ /*
+ * Create resolver to resolve missing packages
+ */
+ var resolverContext = new PackageResolverContext(
+ DependencyBehavior.Lowest,
+ packages.Select(x => x.Name),
+ Enumerable.Empty(),
+ Enumerable.Empty(),
+ Enumerable.Empty(),
+ availablePackages,
+ sourceRepositoryProvider.GetRepositories().Select(s => s.PackageSource),
+ logger);
+
+ var packagesToInstall = new PackageResolver()
+ .Resolve(resolverContext, CancellationToken.None)
+ .Select(p => availablePackages.Single(x => PackageIdentityComparer.Default.Equals(x, p)));
+
+ return packagesToInstall;
+ }
+ ///
+ /// Resolves a package identity from the supplied Id and version.
+ ///
+ /// The version and id of the required package.
+ /// The shared cache in which resolved packages are stored.
+ /// A standard NuGet logger.
+ /// A list of repositories to check when resolving the package version.
+ ///
+ /// Returns the resolved package identity or null if it could not be resolved.
+ /// Thrown when the version range could not be parsed.
+ private async Task GetPackageIdentity(MicroServiceDescriptor microserviceDescriptor, SourceCacheContext cache, NuGet.Common.ILogger nugetLogger, IEnumerable repositories, CancellationToken cancelToken)
+ {
+ /*
+ * Go through repositories in order.
+ */
+ foreach (var sourceRepository in repositories)
+ {
+ /*
+ * Get resource, available versions and attempt to resolve the appropriate version. If none is available, take the last one.
+ */
+ var findPackageResource = await sourceRepository.GetResourceAsync();
+
+ var availableVersions = await findPackageResource.GetAllVersionsAsync(microserviceDescriptor.Name, cache, nugetLogger, cancelToken);
+
+ NuGetVersion selected;
+
+ if (microserviceDescriptor.Version is not null)
+ {
+ if (!VersionRange.TryParse(microserviceDescriptor.Version, out var range))
+ throw new InvalidOperationException("Invalid version range provided.");
+
+ var bestVersion = range.FindBestMatch(availableVersions);
+
+ selected = bestVersion;
+ }
+ else
+ selected = availableVersions.LastOrDefault();
+
+ if (selected is not null)
+ return new PackageIdentity(microserviceDescriptor.Name, selected);
+ }
+
+ return null;
+ }
+ ///
+ /// Searches the package dependency graph for the chain of all packages to install.
+ ///
+ private async Task GetPackageDependencies(PackageIdentity package,
+ SourceCacheContext cacheContext,
+ NuGetFramework framework,
+ NuGet.Common.ILogger logger,
+ IEnumerable repositories,
+ DependencyContext hostDependencies,
+ ISet availablePackages,
+ CancellationToken cancelToken)
+ {
+ /*
+ * Already present, skip.
+ */
+ if (availablePackages.Contains(package))
+ return;
+
+ foreach (var sourceRepository in repositories)
+ {
+ /*
+ * Get dependency info.
+ */
+ var dependencyInfoResource = await sourceRepository.GetResourceAsync();
+
+ var dependencyInfo = await dependencyInfoResource.ResolvePackage(
+ package,
+ framework,
+ cacheContext,
+ logger,
+ cancelToken);
+
+ if (dependencyInfo is null)
+ continue;
+ /*
+ * Only return packages not provided by the host.
+ */
+ var sourceDependencies = new SourcePackageDependencyInfo(
+ dependencyInfo.Id,
+ dependencyInfo.Version,
+ dependencyInfo.Dependencies.Where(dependency => !DependencySuppliedByHost(hostDependencies, dependency)),
+ dependencyInfo.Listed,
+ dependencyInfo.Source);
+
+ availablePackages.Add(sourceDependencies);
+ /*
+ * Get the dependency's packages.
+ */
+ foreach (var dependency in sourceDependencies.Dependencies)
+ {
+ await GetPackageDependencies(
+ new PackageIdentity(dependency.Id, dependency.VersionRange.MinVersion),
+ cacheContext,
+ framework,
+ logger,
+ repositories,
+ hostDependencies,
+ availablePackages,
+ cancelToken);
+ }
+
+ break;
+ }
+ }
+ ///
+ /// Checks if a package dependency is already provided by the host.
+ ///
+ /// The context packages.
+ /// The dependency to check for.
+ /// True if the host provides the dependency, false otherwise.
+ private bool DependencySuppliedByHost(DependencyContext hostDependencies, PackageDependency dependency)
+ {
+ /*
+ * Check runtimes for package with same id.
+ */
+ var runtimeLib = hostDependencies.RuntimeLibraries.FirstOrDefault(r => r.Name == dependency.Id);
+
+ if (runtimeLib is null)
+ return false;
+ /*
+ * Check for version compatibility.
+ */
+ var parsedLibVersion = NuGetVersion.Parse(runtimeLib.Version);
+
+ if (parsedLibVersion.IsPrerelease)
+ /*
+ * Latest and greatest. Always accept due to system compatiblity reasons.
+ */
+ return true;
+
+ /*
+ * Check for version compatibility.
+ */
+ return dependency.VersionRange.Satisfies(parsedLibVersion);
+
+ }
+ ///
+ /// Loads packages from the specified repositories into memory.
+ ///
+ /// A list of package Id and version descriptors to load.
+ /// A list of NuGet repositories to check.
+ internal static async Task LoadPackages(IEnumerable packages, IEnumerable repositories)
+ {
+ await new DependencyLoader().Load(packages, repositories);
+ }
+}
\ No newline at end of file