[英]How to programmatically install a NuGet package?
I want to programmatically install a NuGet package to a project, and update the .csproj
file, and the packages.config
file. 我想以编程方式将NuGet包安装到项目中,并更新
.csproj
文件和packages.config
文件。
I am using the official Nuget.core
framework which source code is available here: https://github.com/NuGet/NuGet2 我正在使用官方的
Nuget.core
框架,其源代码可在此处获取: https : //github.com/NuGet/NuGet2
I am not using the NuGet package: https://www.nuget.org/packages/NuGet.Core/ But the source code found on GitHub to be able to do some debugging. 我没有使用NuGet包: https : //www.nuget.org/packages/NuGet.Core/但是在GitHub上找到的源代码能够进行一些调试。
Note: I am using the version
2.11
and not the2.13
注意:我使用的是
2.11
版而不是2.13
I am able to download a package at a desired directory and update the packages.config
file: 我可以在所需的目录下载一个包并更新
packages.config
文件:
// ---- Download and install a package at a desired path ----
string packageID = "Newtonsoft.json";
var sourceUri = new Uri("https://packages.nuget.org/api/v2");
// Return an IPackage
var package = GetNugetPackage(packageID, sourceUri);
IPackageRepository sourceRepository = PackageRepositoryFactory.Default.CreateRepository(sourceUri.ToString());
string packagesPath = "../../TestFiles/packages";
PackageManager packageManager = new PackageManager(sourceRepository, packagesPath);
packageManager.InstallPackage(packageID, SemanticVersion.Parse(package.Version.ToFullString()));
// ---- Update the ‘packages.config’ file ----
var packageReferenceFile = new PackageReferenceFile("../../TestFiles/packages.config");
// Get the target framework of the current project to add --> targetframework="net452" attribute in the package.config file
var currentTargetFw = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(TargetFrameworkAttribute), false);
var targetFrameworkAttribute = ((TargetFrameworkAttribute[])currentTargetFw).FirstOrDefault();
// Update the packages.config file
packageReferenceFile.AddEntry(package.GetFullName(), SemanticVersion.Parse(package.Version.ToFullString()), false, new FrameworkName(targetFrameworkAttribute.FrameworkName));
Now I need to update the .csproj
and here is the tricky part... 现在我需要更新
.csproj
,这是一个棘手的部分......
Here's what I tried so far: 这是我到目前为止所尝试的:
string csprojFilePath = "../../TestFiles/test.csproj";
var project = new MSBuildProjectSystem(csprojFilePath);
string pathToAnExistingNugetPackageDll = "../../TestFiles/packages/Newtonsoft.json/lib/net45/Newtonsoft.json.dll"
project.AddReference(pathToAnExistingNugetPackageDll, Stream.Null);
project.Save();
This piece of code update the .csproj
file, it add a new reference
node like this: 这段代码更新
.csproj
文件,它添加一个新的reference
节点,如下所示:
<Reference Include="Newtonsoft.json">
<HintPath>..\packages\Newtonsoft.json\lib\net45\Newtonsoft.json.dll</HintPath>
</Reference>
But I need a complete reference
node like this: 但我需要一个完整的
reference
节点,如下所示:
<Reference Include="Newtonsoft.json, Version=9.0.8, Culture=neutral, PublicKeyToken=b03f4f7d11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.json\lib\net45\Newtonsoft.json.dll</HintPath>
<Private>True</Private>
</Reference>
How can I do it ? 我该怎么做 ?
Thanks to @MattWard and @bashis , i've written my own code generator: 感谢@MattWard和@bashis ,我编写了自己的代码生成器:
So, to add properly a reference into a given .csproj
here's what I do: 因此,要在给定的
.csproj
正确添加引用,这就是我所做的:
var CsprojDoc = new XmlDocument();
CsprojDoc.LoadXml("*your_csproj_content*");
var Nsmgr = new XmlNamespaceManager(CsprojDoc.NameTable);
Nsmgr.AddNamespace("x", "http://schemas.microsoft.com/developer/msbuild/2003");
IPackage packageInfos = GetNugetPackage(packageId, packageRepositoryUri);
XmlNode referenceNode = CsprojDoc.CreateNode(XmlNodeType.Element, "Reference", XmlNamespaceValue);
XmlAttribute includeAttribute = CsprojDoc.CreateAttribute("Include");
var targetFwProfile = CsprojDoc.SelectSingleNode("//x:TargetFrameworkProfile", Nsmgr);
string targetFrameworkProfile = string.Empty;
if (!string.IsNullOrEmpty(targetFwProfile?.InnerXml))
{
targetFrameworkProfile = targetFwProfile.InnerXml;
}
var targetFwAttribute = GetTargetFrameworkFromCsproj();
Regex p = new Regex(@"\d+(\.\d+)+");
Match m = p.Match(targetFwAttribute.FrameworkName);
Version targetFwVersion = Version.Parse(m.Value);
// Get the package's assembly reference matching the target framework from the given '.csproj'.
var assemblyReference =
packageInfos.AssemblyReferences
.Where(a => a.TargetFramework.Identifier.Equals(targetFwAttribute.FrameworkName.Split(',').First()))
.Where(a => a.TargetFramework.Profile.Equals(targetFrameworkProfile))
.Last(a => (a.TargetFramework.Version.Major.Equals(targetFwVersion.Major) && a.TargetFramework.Version.Minor.Equals(targetFwVersion.Minor)) ||
a.TargetFramework.Version.Major.Equals(targetFwVersion.Major));
DownloadNugetPackage(packageInfos.Id, packageRepositoryUri, packagesFolderPath, packageInfos.Version.ToFullString());
string dllAbsolutePath = Path.GetFullPath($"{packagesFolderPath}\\{packageInfos.GetFullName().Replace(' ', '.')}\\{assemblyReference.Path}");
var assemblyInfos = Assembly.LoadFile(dllAbsolutePath);
includeAttribute.Value = $"{assemblyInfos.FullName}, processorArchitecture=MSIL";
referenceNode.Attributes.Append(includeAttribute);
XmlNode hintPathNode = CsprojDoc.CreateNode(XmlNodeType.Element, "HintPath", XmlNamespaceValue);
XmlNode privateNode = CsprojDoc.CreateNode(XmlNodeType.Element, "Private", XmlNamespaceValue);
hintPathNode.InnerXml = $"$(SolutionDir)\\packages\\{assemblyReference.Path}";
privateNode.InnerXml = "True";
referenceNode.AppendChild(hintPathNode);
referenceNode.AppendChild(privateNode);
var itemGroupNode = CsprojDoc.SelectSingleNode("//x:Project/x:ItemGroup/x:Reference", Nsmgr).ParentNode;
itemGroupNode.AppendChild(referenceNode);
Here's my DownloadNugetPackage
method: 这是我的
DownloadNugetPackage
方法:
private static void DownloadNugetPackage(string packageId, Uri repoUri, string packagesFolderPath, string version)
{
IPackageRepository packageRepository = PackageRepositoryFactory.Default.CreateRepository(repoUri.ToString());
PackageManager packageManager = new PackageManager(packageRepository, packagesFolderPath);
packageManager.InstallPackage(packageId, SemanticVersion.Parse(version));
}
My GetTargetFrameworkFromCsproj
我的
GetTargetFrameworkFromCsproj
public static TargetFrameworkAttribute GetTargetFrameworkFromCsproj()
{
XmlNode targetFrameworkNode = CsprojDoc.SelectSingleNode("//x:TargetFrameworkVersion", Nsmgr);
return new TargetFrameworkAttribute($".NETFramework, Version={targetFrameworkNode.InnerXml}");
}
And my GetNugetPackage
method: 和我的
GetNugetPackage
方法:
public static IPackage GetNugetPackage(string packageId, Uri repoUri, string version = null)
{
IPackageRepository packageRepository = PackageRepositoryFactory.Default.CreateRepository(repoUri.ToString());
IPackage package;
if (!string.IsNullOrEmpty(version))
{
package = packageRepository.FindPackagesById(packageId).SingleOrDefault(p => p.Version.ToFullString().Equals(version));
}
else
{
package = packageRepository.FindPackagesById(packageId).SingleOrDefault(p => p.IsLatestVersion);
}
return package;
}
Note: This time, i'm using the official NuGet package: Nuget.core 2.14 : https://www.nuget.org/packages/NuGet.Core/
注意:这一次,我使用官方的NuGet包: Nuget.core 2.14 : https : //www.nuget.org/packages/NuGet.Core/
Note 2: When I add a new
Reference
node, in theprocessorArchitecture
attribute, I've hard coded the value:MSIL
注意2:当我添加一个新的
Reference
节点时,在processorArchitecture
属性中,我对该值进行了硬编码:MSIL
If you want to test it, you might tweak it a bit. 如果你想测试它,你可能会调整一下。
This work fine for me. 这对我来说很好。
(Assuming you are writing the code generator on your own) (假设您自己编写代码生成器)
I am not sure this is the best solution but you could try something like this: 我不确定这是最好的解决方案,但你可以尝试这样的事情:
var assembly = Assembly.LoadFile("path_to_dll");
string info = assembly.FullName; // contains something like "AssemblyName, Version=0.0.0.0, Culture=neutral, PublicKeyToken=foobar"
which will add some overhead by loading the assembly, but will presumably do the job. 这将通过加载程序集增加一些开销,但可能会完成这项工作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.