如何跨多个c#项目强制执行相同的nuget包版本?

时间:2014-11-07 01:36:15

标签: c# nuget

我有一些使用几个NuGet包的小型C#项目。我希望能够自动更新给定包的版本。更重要的是:如果项目使用与其他项目不同的版本,我希望收到警告。

如何在多个C#项目中强制实施相同的版本依赖?

4 个答案:

答案 0 :(得分:9)

感谢您提出这个问题 - 所以我并不孤单。我花了大量时间确保我的解决方案中的所有项目都使用相同的软件包版本。 NuGet用户界面(以及命令行界面)也有助于在解决方案中的项目中具有不同的版本。特别是当一个新项目被添加到解决方案中并且包X将被添加到新项目中时,NuGet过于贪心从nuget.org下载最新版本而不是首先使用本地版本,这将是更好的默认处理

我完全同意你的看法,NuGet应警告解决方案中是否使用了不同版本的软件包。它应该有助于避免这种情况并修复这样的版本迷宫。

我现在发现的最好的方法是枚举解决方案文件夹(你的项目 - 根目录)中的所有packages.config文件,它们看起来像

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Newtonsoft.Json" version="6.0.6" targetFramework="net451" />
  ...
</packages>

然后按id排序xml节点并分析版本号。

如果任何软件包出现不同版本号,使它们全部相等,然后运行NuGet命令

Update-Package -ProjectName 'acme.lab.project' -Reinstall

应修复错误的软件包版本。

(因为NuGet是开源的,所以让我们的手弄脏并实现丢失版本冲突避免实用程序肯定是一件很酷的事。)

答案 1 :(得分:9)

我相信我找到了一个可以解决这个(和许多其他)问题的设置。

我刚刚意识到可以使用文件夹作为nuget源。这是我做的:

root
  + localnuget
      + Newtonsoft.Json.6.0.1.nupkg
  + nuget.config
  + packages
      + Newtonsoft.Json.6.0.1
  + src
      + project1

nuget.config看起来像这样:

<configuration>
  <config>
    <add key="repositoryPath" value="packages" />
  </config>
  <packageSources>
    <add key="local source" value="localnuget">
  </packageSources>
</configuration>

您可以将Nuget服务器添加到nuget.config,以便在开发期间访问更新或新的依赖项:

<add key="nuget.org" value="https://www.nuget.org/api/v2/" /> 

完成后,您可以将.nupkg from cache复制到localnuget文件夹进行检查。

我喜欢这个设置有三件事:

  1. 我现在可以使用Nuget功能,例如添加道具和目标。如果你有一个代码生成器(例如protobuf或thrift),这将变得无价。

  2. 它(部分)解决了Visual Studio不复制all DLLs的问题,因为您需要在.nuspec文件中指定依赖项,nuget会自动加载间接依赖项。

  3. 我以前为所有项目都有一个解决方案文件,因此更新nuget包更容易。我还没有尝试,但我认为我也解决了这个问题。我可以为我想从给定解决方案中导出的项目提供nuget包。

答案 2 :(得分:5)

由于我还没有找到另一种方法来执行此操作,因此我编写了一个单元测试,如果在任何子文件夹的任意package.config中找到了不同的软件包版本,该测试都会失败。 由于这可能对其他人有用,因此您将在下面找到代码。您必须调整在GetBackendDirectoryPath()中完成的根文件夹的分辨率。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml;

using NUnit.Framework;

namespace Base.Test.Unit
{
    [TestFixture]
    public class NugetTest
    {
        private const string PACKAGES_CONFIG_FILE_NAME = "packages.config";
        private const string BACKEND_DIRECTORY_NAME = "DeviceCloud/";

        private const string PACKAGES_NODE_NAME = "packages";
        private const string PACKAGE_ID_ATTRIBUTE_NAME = "id";
        private const string PACKAGE_VERSION_ATTRIBUTE_NAME = "version";

        /// <summary>
        /// Tests that all referenced nuget packages have the same version by doing:
        /// - Get all packages.config files contained in the backend
        /// - Retrieve the id and version of all packages
        /// - Fail this test if any referenced package has referenced to more than one version accross projects
        /// - Output a message mentioning the different versions for each package 
        /// </summary>
        [Test]
        public void EnforceCoherentReferences()
        {
            // Act
            IDictionary<string, ICollection<PackageVersionItem>> packageVersionsById = new Dictionary<string, ICollection<PackageVersionItem>>();
            foreach (string packagesConfigFilePath in GetAllPackagesConfigFilePaths())
            {
                var doc = new XmlDocument();
                doc.Load(packagesConfigFilePath);

                XmlNode packagesNode = doc.SelectSingleNode(PACKAGES_NODE_NAME);
                if (packagesNode != null && packagesNode.HasChildNodes)
                {
                    foreach (var packageNode in packagesNode.ChildNodes.Cast<XmlNode>())
                    {
                        if (packageNode.Attributes == null)
                        {
                            continue;
                        }

                        string packageId = packageNode.Attributes[PACKAGE_ID_ATTRIBUTE_NAME].Value;
                        string packageVersion = packageNode.Attributes[PACKAGE_VERSION_ATTRIBUTE_NAME].Value;

                        if (!packageVersionsById.TryGetValue(packageId, out ICollection<PackageVersionItem> packageVersions))
                        {
                            packageVersions = new List<PackageVersionItem>();
                            packageVersionsById.Add(packageId, packageVersions);
                        }

                        //if (!packageVersions.Contains(packageVersion))
                        if(!packageVersions.Any(o=>o.Version.Equals(packageVersion)))
                        {
                            packageVersions.Add(new PackageVersionItem()
                            {
                                SourceFile = packagesConfigFilePath,
                                Version = packageVersion
                            });
                        }

                        if (packageVersions.Count > 1)
                        {
                            //breakpoint to examine package source
                        }
                    }
                }
            }

            List<KeyValuePair<string, ICollection<PackageVersionItem>>> packagesWithIncoherentVersions = packageVersionsById.Where(kv => kv.Value.Count > 1).ToList();

            // Assert
            string errorMessage = string.Empty;
            if (packagesWithIncoherentVersions.Any())
            {
                errorMessage = $"Some referenced packages have incoherent versions. Please fix them by adapting the nuget reference:{Environment.NewLine}";
                foreach (var packagesWithIncoherentVersion in packagesWithIncoherentVersions)
                {
                    string packageName = packagesWithIncoherentVersion.Key;
                    string packageVersions = string.Join("\n  ", packagesWithIncoherentVersion.Value);
                    errorMessage += $"{packageName}:\n  {packageVersions}\n\n";
                }
            }

            Assert.IsTrue(packagesWithIncoherentVersions.Count == 0,errorMessage);
            //Assert.IsEmpty(packagesWithIncoherentVersions, errorMessage);
        }

        private static IEnumerable<string> GetAllPackagesConfigFilePaths()
        {
            return Directory.GetFiles(GetBackendDirectoryPath(), PACKAGES_CONFIG_FILE_NAME, SearchOption.AllDirectories)
                .Where(o=>!o.Contains(".nuget"));
        }

        private static string GetBackendDirectoryPath()
        {
            string codeBase = Assembly.GetExecutingAssembly().CodeBase;
            var uri = new UriBuilder(codeBase);
            string path = Uri.UnescapeDataString(uri.Path);
            return Path.GetDirectoryName(path.Substring(0, path.IndexOf(BACKEND_DIRECTORY_NAME, StringComparison.Ordinal) + BACKEND_DIRECTORY_NAME.Length));
        }

    }

    public class PackageVersionItem
    {
        public string SourceFile { get; set; }
        public string Version { get; set; }

        public override string ToString()
        {
            return $"{Version} in {SourceFile}";
        }
    }
}

答案 3 :(得分:2)

我不知道如何强制执行,但我已经找到了&#34;整合&#34;选项卡来帮助。 此选项卡显示整个解决方案中包含不同版本的包。从那里,您可以选择项目并使用安装按钮为它们安装相同的软件包版本。

enter image description here