Skip to content

Instantly share code, notes, and snippets.

@RickStrahl
Last active March 18, 2025 23:41
Show Gist options
  • Save RickStrahl/cab310922f5c7b01aa8f6686c7632800 to your computer and use it in GitHub Desktop.
Save RickStrahl/cab310922f5c7b01aa8f6686c7632800 to your computer and use it in GitHub Desktop.
A first-cut NuGet Package Loader
// Paste into LinqPad
// dotnet add package NuGet.Protocol
async Task Main()
{
// package sources
string[] sources = ["/projects/Nuget", "https://api.nuget.org/v3/index.json"];
// Package loader: specify output folder for package assemblies
var pack = new NuGetPackageLoader(@"d:\temp\Packages");
await pack.LoadPackageAsync("Westwind.Ai", "0.2.4.2", sources);
var obj = pack.CreateInstance("Westwind.AI.Configuration.OpenAiConnection");
obj.Dump(); // works
}
public class NuGetPackageLoader
{
private readonly string _packagesFolder;
public NuGetPackageLoader(string packagesFolder)
{
_packagesFolder = packagesFolder;
}
public async Task LoadPackageAsync(string packageId, string version, IEnumerable<string> sources = null)
{
if (sources == null)
sources = ["https://api.nuget.org/v3/index.json"];
var logger = NullLogger.Instance;
var cache = new SourceCacheContext();
var repositories = Repository.Provider.GetCoreV3();
var packageVersion = new NuGetVersion(version);
var packagePath = Path.Combine(_packagesFolder, packageId, version);
Directory.CreateDirectory(packagePath);
using var packageStream = new MemoryStream();
FindPackageByIdResource resource = null;
foreach (var source in sources)
{
if (!source.StartsWith("http"))
{
// local packages
var sourceRepository = Repository.Factory.GetCoreV3(source);
try
{
resource = await sourceRepository.GetResourceAsync<FindPackageByIdResource>();
}
catch { continue; }
}
else
{
// online packages
var packageSource = new PackageSource(source);
var sourceRepository = new SourceRepository(packageSource, repositories);
try
{
resource = await sourceRepository.GetResourceAsync<FindPackageByIdResource>();
}
catch { continue; }
}
if (await resource.CopyNupkgToStreamAsync(packageId, packageVersion, packageStream, cache, logger, default))
{
packageStream.Seek(0, SeekOrigin.Begin);
break;
}
resource = null;
}
if (resource == null)
return;
var packageReader = new PackageArchiveReader(packageStream);
// find the highest compatible framework
string framework = GetTargetFramework(packageReader);
// load dependencies
var dependencies = packageReader
.GetPackageDependencies()
.Where(d => d.TargetFramework.ToString() == framework)
.ToList();
foreach (var packRef in dependencies)
{
var pkg = packRef.Packages.FirstOrDefault();
if (pkg?.Id == null)
continue;
await LoadPackageAsync(pkg.Id, pkg.VersionRange.OriginalString);
}
var files = packageReader.GetFiles().Where(f => f.Contains("/" + framework + "/"));
foreach (var file in files)
{
var filePath = Path.Combine(packagePath, file.Replace("/", "\\"));
if (!File.Exists(filePath))
{
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
//FileStream fileStream = null;
try
{
using var fileStream = File.Create(filePath);
await packageReader.GetStream(file).CopyToAsync(fileStream);
}
catch { /* ignore - most likely the file exists already */ }
}
if (filePath.EndsWith(".dll") && File.Exists(filePath))
{
var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(filePath);
Console.WriteLine($"Loaded assembly: {assembly.FullName}");
}
}
}
// This is non-generic in that it checks for specific framework versions
private string GetTargetFramework(PackageArchiveReader packageReader)
{
string framework = packageReader.GetReferenceItems().
Where(g =>
{
string framework = g.TargetFramework.ToString();
if (framework == "net9.0" || framework == "net8.0" ||
framework == "net6.0" || framework == "net7.0" ||
framework.StartsWith("netstandard"))
return true;
return false;
})
.OrderByDescending(g => g.TargetFramework.ToString())
.Select(g => g.TargetFramework.ToString())
.FirstOrDefault();
return framework;
}
#region Object Instantiation
public object CreateInstance(string typename)
{
try
{
Type type = GetTypeFromName(typename);
if (type != null)
{
object instance = Activator.CreateInstance(type);
return instance;
}
}
catch { }
return null;
}
private Type GetTypeFromName(string typeName)
{
var type = Type.GetType(typeName, false);
if (type != null)
return type;
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
// try to find manually
foreach (Assembly asm in assemblies)
{
type = asm.GetType(typeName, false);
if (type != null)
break;
}
if (type != null)
return type;
return null;
}
#endregion
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment