保证相同版本的 nuget 包
Guarantee same version of nuget packages
我们有一个框架,在一个解决方案中分为许多独立的项目。我现在想为每个单独的项目创建 NuGet 包,但 保证在一个解决方案中只能使用一个版本的框架(可能跨越几个项目)。
例如,假设框架由两个项目组成:
Framework
Framework_1
Framework_2
现在,当使用此框架时,一个项目可能会引用 Framework_1
,而另一个项目会引用 Framework_2
。我想确保两个包具有相同的版本(如果有一个简单的单步过程即可升级到较新版本,则加分)
我想我会只定义一个解决方案级别的框架包,所有其他包都严格依赖它。问题是 NuGet 只需安装几个版本的解决方案级包就没有问题。
基本上我尝试了以下方法:
解决方案级 nuspec 文件:
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>My.Framework</id>
<version>1.0.0</version>
<title>My.Framework</title>
<authors>voo</authors>
<owners>voo</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Some Framework Solution Package</description>
<copyright>Copyright © 2015</copyright>
</metadata>
</package>
和一个 nuspec 包的一部分:
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>My.Framework.BL</id>
<version>1.0.0</version>
<title>My.Framework.BL</title>
<authors>voo</authors>
<owners>voo</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Business Layer</description>
<copyright>Copyright © 2015</copyright>
<dependencies>
<dependency id="My.Framework" version="[1.0.0]"/>
</dependencies>
</metadata>
</package>
现在的问题是,如果我尝试安装另一个 My.Framework.EF
版本为 1.0.1
并且显式依赖于 My.Framework
1.0.1 Visual Studio
的软件包,那么只会安装 My.Framework
两次 - 一次是 1.0.0 版,一次是 1.0.1 版。
您可以在 packages.config 中使用以下语法来限制包的版本,例如:
<package id="jQuery" version="1.9.1" allowedVersions="[1.9.1]" />
同样来自原始的 nuget 文档:
创建 NuGet 包时,可以在 .nuspec 文件中指定包的依赖项。
<dependency id="ExamplePackage" version="[1,3)" />
在示例中,可以接受版本 1 和版本 2.9,但不能接受 0.9 或 3.0。
我假设您可以通过这种方式将其限制为单个或特定范围的版本。
Here您可以阅读更多相关信息。
我会撕掉 "Solution level NuGet Package",将您的框架分成多个组件,并为每个组件创建一个 NuGet 包。没有人会拥有 1 个引用您的 "Framework Wrapper" NuGet 包的项目,以及该项目中的业务逻辑、数据访问和 WCF 代码。
然后你需要做的是,弄清楚你的依赖逻辑到底是什么,以及想要严格执行相同版本策略背后的原因是什么。
例如,假设 My.Framework.BL 依赖于 My.Framework.DAL。所以此时你只有 2 个 Nuspec 文件和 2 个 NuGet 包,你的 My.Framework.BL 的 .nuspec 看起来像这样:
<dependencies>
<dependency id="My.Framework.DAL" version="1.0.0" />
</dependencies>
并且您的 My.Framework.DAL 不包含 My.Framework 特定依赖项。
这很好,但由于某些原因,您希望紧密耦合与版本关联的数字的解决方案存在问题。第一个也是最重要的是,如果您在 My.Framework.DAL 有 0 个更改时更新它,它会让您的框架使用者感到困惑,但是您必须更新它,因为您更改了 My.Framework.BL.
您可以花一个月或更长时间,而不必更新 My.Framework 依赖项,具体取决于您的框架的抽象级别以及您的低级编程程度正在做。在我看来,在实际上没有任何新更改的情况下必须更新 Core Framework dll 版本是一个比所有 My.Framework dll 的版本号都相同的问题要大得多。干杯。 :)
Here 是 nuspec 参考文档。
原来可以在里面调用Install-Package $package.Id -version <someVersion>
Install.ps1然后会导致原来安装的版本被卸载,指定的版本待安装。
稍微简化的版本如下:
param($installPath, $toolsPath, $package, $project)
function GetInstallingVersion() {
$package.Version
}
# Gets the current version of the used framework.
# If no framework is yet installed, we set the framework version
# to the one that's being installed right now.
function GetCurrentFrameworkVersion() {
$solutionPath = Split-Path $dte.Solution.FileName
$fwkVersionFile = "${solutionPath}\framework_version.txt"
if (Test-Path $fwkVersionFile) {
return Get-Content $fwkVersionFile
}
else {
$installingVersion = GetInstallingVersion
$installingVersion > $fwkVersionFile
return $installingVersion
}
}
$currentFwkVersion = GetCurrentFrameworkVersion
$installingVersion = GetInstallingVersion
if ($currentFwkVersion -ne $installingVersion) {
Install-Package $package.Id -version $currentFwkVersion
}
您可以在您的解决方案中创建一个简单的单元测试,以便在您遇到问题时向您发出警告。代码如下。
您需要 install-package NuGet.Core
在单元测试项目中才能使以下代码正常工作。
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NuGet;
[TestClass]
public class NugetPackagesTest
{
/// <summary>
/// This test method makes sure that we do not install different versions of the same nuget package
/// across the solution. For example this test will fail if one project references EntityFramework
/// version 6.1.3 and another project references version 6.2.0. Having different versions of the same
/// package installed often results in unexpected and hard-to-understand errors.
/// </summary>
[TestMethod]
public void PackagesAccrossProjectsAreOfSameVersion()
{
var dir = GetSolutionRoot();
Debug.Assert(dir != null, nameof(dir) + " != null");
var filePaths = Directory.GetFiles(dir.FullName, "*packages.config", SearchOption.AllDirectories);
var installedPackages = filePaths
.Select(t => new PackageReferenceFile(t))
.SelectMany(t => t.GetPackageReferences().Select(x => new { File = t, Package = x }))
.GroupBy(t => t.Package.Id)
.ToList();
foreach (var package in installedPackages)
{
var versions = package
.Select(t => t.Package.Version.ToNormalizedString())
.Distinct()
.ToList();
var report = package
.Select(t => $"{t.Package.Version} @ {t.File.FullPath}")
.OrderBy(t => t);
Assert.IsTrue(
versions.Count == 1,
$"Multiple versions of package {package.Key} are installed: {string.Join(", ", versions)}.\n" +
$"{string.Join("\n", report)}");
}
}
private static DirectoryInfo GetSolutionRoot()
{
var current = AppDomain.CurrentDomain.BaseDirectory;
var dir = Directory.GetParent(current);
while (dir != null)
{
// TODO: replace with name your solution's folder.
if (dir.Name == "MySolution")
{
dir = dir.Parent;
break;
}
dir = dir.Parent;
}
return dir;
}
}
我们有一个框架,在一个解决方案中分为许多独立的项目。我现在想为每个单独的项目创建 NuGet 包,但 保证在一个解决方案中只能使用一个版本的框架(可能跨越几个项目)。
例如,假设框架由两个项目组成:
Framework
Framework_1
Framework_2
现在,当使用此框架时,一个项目可能会引用 Framework_1
,而另一个项目会引用 Framework_2
。我想确保两个包具有相同的版本(如果有一个简单的单步过程即可升级到较新版本,则加分)
我想我会只定义一个解决方案级别的框架包,所有其他包都严格依赖它。问题是 NuGet 只需安装几个版本的解决方案级包就没有问题。
基本上我尝试了以下方法:
解决方案级 nuspec 文件:
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>My.Framework</id>
<version>1.0.0</version>
<title>My.Framework</title>
<authors>voo</authors>
<owners>voo</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Some Framework Solution Package</description>
<copyright>Copyright © 2015</copyright>
</metadata>
</package>
和一个 nuspec 包的一部分:
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>My.Framework.BL</id>
<version>1.0.0</version>
<title>My.Framework.BL</title>
<authors>voo</authors>
<owners>voo</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Business Layer</description>
<copyright>Copyright © 2015</copyright>
<dependencies>
<dependency id="My.Framework" version="[1.0.0]"/>
</dependencies>
</metadata>
</package>
现在的问题是,如果我尝试安装另一个 My.Framework.EF
版本为 1.0.1
并且显式依赖于 My.Framework
1.0.1 Visual Studio
的软件包,那么只会安装 My.Framework
两次 - 一次是 1.0.0 版,一次是 1.0.1 版。
您可以在 packages.config 中使用以下语法来限制包的版本,例如:
<package id="jQuery" version="1.9.1" allowedVersions="[1.9.1]" />
同样来自原始的 nuget 文档: 创建 NuGet 包时,可以在 .nuspec 文件中指定包的依赖项。
<dependency id="ExamplePackage" version="[1,3)" />
在示例中,可以接受版本 1 和版本 2.9,但不能接受 0.9 或 3.0。
我假设您可以通过这种方式将其限制为单个或特定范围的版本。 Here您可以阅读更多相关信息。
我会撕掉 "Solution level NuGet Package",将您的框架分成多个组件,并为每个组件创建一个 NuGet 包。没有人会拥有 1 个引用您的 "Framework Wrapper" NuGet 包的项目,以及该项目中的业务逻辑、数据访问和 WCF 代码。
然后你需要做的是,弄清楚你的依赖逻辑到底是什么,以及想要严格执行相同版本策略背后的原因是什么。
例如,假设 My.Framework.BL 依赖于 My.Framework.DAL。所以此时你只有 2 个 Nuspec 文件和 2 个 NuGet 包,你的 My.Framework.BL 的 .nuspec 看起来像这样:
<dependencies>
<dependency id="My.Framework.DAL" version="1.0.0" />
</dependencies>
并且您的 My.Framework.DAL 不包含 My.Framework 特定依赖项。
这很好,但由于某些原因,您希望紧密耦合与版本关联的数字的解决方案存在问题。第一个也是最重要的是,如果您在 My.Framework.DAL 有 0 个更改时更新它,它会让您的框架使用者感到困惑,但是您必须更新它,因为您更改了 My.Framework.BL.
您可以花一个月或更长时间,而不必更新 My.Framework 依赖项,具体取决于您的框架的抽象级别以及您的低级编程程度正在做。在我看来,在实际上没有任何新更改的情况下必须更新 Core Framework dll 版本是一个比所有 My.Framework dll 的版本号都相同的问题要大得多。干杯。 :)
Here 是 nuspec 参考文档。
原来可以在里面调用Install-Package $package.Id -version <someVersion>
Install.ps1然后会导致原来安装的版本被卸载,指定的版本待安装。
稍微简化的版本如下:
param($installPath, $toolsPath, $package, $project)
function GetInstallingVersion() {
$package.Version
}
# Gets the current version of the used framework.
# If no framework is yet installed, we set the framework version
# to the one that's being installed right now.
function GetCurrentFrameworkVersion() {
$solutionPath = Split-Path $dte.Solution.FileName
$fwkVersionFile = "${solutionPath}\framework_version.txt"
if (Test-Path $fwkVersionFile) {
return Get-Content $fwkVersionFile
}
else {
$installingVersion = GetInstallingVersion
$installingVersion > $fwkVersionFile
return $installingVersion
}
}
$currentFwkVersion = GetCurrentFrameworkVersion
$installingVersion = GetInstallingVersion
if ($currentFwkVersion -ne $installingVersion) {
Install-Package $package.Id -version $currentFwkVersion
}
您可以在您的解决方案中创建一个简单的单元测试,以便在您遇到问题时向您发出警告。代码如下。
您需要 install-package NuGet.Core
在单元测试项目中才能使以下代码正常工作。
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NuGet;
[TestClass]
public class NugetPackagesTest
{
/// <summary>
/// This test method makes sure that we do not install different versions of the same nuget package
/// across the solution. For example this test will fail if one project references EntityFramework
/// version 6.1.3 and another project references version 6.2.0. Having different versions of the same
/// package installed often results in unexpected and hard-to-understand errors.
/// </summary>
[TestMethod]
public void PackagesAccrossProjectsAreOfSameVersion()
{
var dir = GetSolutionRoot();
Debug.Assert(dir != null, nameof(dir) + " != null");
var filePaths = Directory.GetFiles(dir.FullName, "*packages.config", SearchOption.AllDirectories);
var installedPackages = filePaths
.Select(t => new PackageReferenceFile(t))
.SelectMany(t => t.GetPackageReferences().Select(x => new { File = t, Package = x }))
.GroupBy(t => t.Package.Id)
.ToList();
foreach (var package in installedPackages)
{
var versions = package
.Select(t => t.Package.Version.ToNormalizedString())
.Distinct()
.ToList();
var report = package
.Select(t => $"{t.Package.Version} @ {t.File.FullPath}")
.OrderBy(t => t);
Assert.IsTrue(
versions.Count == 1,
$"Multiple versions of package {package.Key} are installed: {string.Join(", ", versions)}.\n" +
$"{string.Join("\n", report)}");
}
}
private static DirectoryInfo GetSolutionRoot()
{
var current = AppDomain.CurrentDomain.BaseDirectory;
var dir = Directory.GetParent(current);
while (dir != null)
{
// TODO: replace with name your solution's folder.
if (dir.Name == "MySolution")
{
dir = dir.Parent;
break;
}
dir = dir.Parent;
}
return dir;
}
}