保证相同版本的 nuget 包

本文关键字:nuget 版本 | 更新日期: 2023-09-27 17:56:25

我们有一个框架,在一个解决方案中被分成许多单独的项目。我现在想要为每个单独的项目创建 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>

现在的问题是,如果我尝试安装,比如说另一个具有版本 1.0.1My.Framework.EF 包,并且显式依赖于 My.Framework 1.0.1 Visual Studio只会安装 My.Framework 两次 - 一次是 1.0.0 版,一次是 1.0.1 版。

保证相同版本的 nuget 包

您可以通过在

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。

我想你可以这样限制它到一个或一定范围的版本。在这里你可以阅读更多关于它的信息。

可以在解决方案中创建一个简单的单元测试,以便在遇到问题时发出警告。代码如下。

您需要在单元测试项目中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 包",并将您的框架划分为组件,并为每个组件创建一个 NuGet 包。没有人会在单个项目中拥有 1 个引用"框架包装器"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依赖项,这取决于框架的抽象级别,以及你正在做的低级编程的程度。在我看来,当实际上没有任何新的更改时,必须更新核心框架dll版本是一个比所有My.Framework dll的版本号都相同的问题。干杯。:)

以下是 nuspec 参考文档。

事实证明,您可以在 Install.ps1 调用Install-Package $package.Id -version <someVersion>,这将导致卸载最初安装的版本并安装指定的版本。

稍微简化的版本如下:

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
}