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

如何在多个c#项目中强制执行相同的nuget包版本?,c#,nuget,C#,Nuget,我有一堆小的C#项目,它们使用了几个NuGet包。我希望能够自动更新给定软件包的版本。更重要的是:如果一个项目使用与其他项目不同的版本,我希望得到警告 我如何在多个C#项目之间强制执行相同的版本依赖关系?谢谢您的提问-所以我并不孤单。我花了大量时间来确保我的解决方案中的所有项目使用相同的包版本。NuGet用户界面(以及命令行界面)也有助于在解决方案中的项目之间拥有不同的版本。特别是当一个新的项目被添加到解决方案中,并且包X应该被添加到新的项目中时,NuGet非常贪婪地从NuGet.org下载最新

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


我如何在多个C#项目之间强制执行相同的版本依赖关系?

谢谢您的提问-所以我并不孤单。我花了大量时间来确保我的解决方案中的所有项目使用相同的包版本。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>
应该修复错误的包版本


(由于NuGet是开源的,因此让我们动手并实现缺少版本的冲突避免实用程序肯定是一件很酷的事情。)

我相信我已经找到了一个解决这个(以及许多其他)问题的设置

我刚刚意识到可以使用文件夹作为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>

转到
localnuget
文件夹以将其签入

关于此设置,我喜欢三件事:

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

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

  • 我以前对所有项目都有一个单一的解决方案文件,所以更新nuget包更容易。我还没试过,但我想我也解决了这个问题。我可以为要从给定解决方案导出的项目提供nuget包


  • 我不知道如何实施它,但我发现“合并”选项卡可以提供帮助。 此选项卡显示在整个解决方案中具有不同版本的包。从那里,您可以选择项目并使用install按钮为其安装相同的软件包版本。可以在“管理解决方案的NuGet”下找到此选项卡


    由于我还没有找到另一种方法来实现这一点,因此我编写了一个单元测试,如果在任何子文件夹的packages.config中发现不同的包版本,该测试将失败。 由于这可能对其他人有用,您可以在下面找到代码。您必须调整在GetBackendDirectoryPath()中完成的根文件夹的解析

    使用系统;
    使用System.Collections.Generic;
    使用System.IO;
    使用System.Linq;
    运用系统反思;
    使用System.Xml;
    使用NUnit.Framework;
    命名空间Base.Test.Unit
    {
    [测试夹具]
    公共类NugetTest
    {
    private const string PACKAGES\u CONFIG\u FILE\u NAME=“PACKAGES.CONFIG”;
    private const string BACKEND_DIRECTORY_NAME=“DeviceCloud/”;
    private const string PACKAGES\u NODE\u NAME=“PACKAGES”;
    私有常量字符串包\u ID\u属性\u NAME=“ID”;
    私有常量字符串包\u版本\u属性\u NAME=“VERSION”;
    /// 
    ///通过执行以下操作,测试所有引用的nuget软件包是否具有相同的版本:
    ///-获取后端中包含的所有packages.config文件
    ///-检索所有包的id和版本
    ///-如果任何引用的包引用了多个版本,则此测试失败
    ///-输出一条消息,说明每个包的不同版本
    /// 
    [测试]
    public void EnforceCoherentReferences()
    {
    //表演
    IDictionary PackageVersionById=新字典();
    foreach(GetAllPackageConfigFilePath()中的字符串PackageConfigFilePath)
    {
    var doc=新的XmlDocument();
    文档加载(PackageConfigFilePath);
    XmlNode packagesNode=doc.SelectSingleNode(PACKAGES\u NODE\u NAME);
    if(packagesNode!=null&&packagesNode.HasChildNodes)
    {
    foreach(packagesNode.ChildNodes.Cast()中的变量packageNode)
    {
    if(packageNode.Attributes==null)
    {
    继续;
    }
    字符串packageId=packageNode.Attributes[包\u ID\u属性\u名称]。值;
    字符串packageVersion=packageNode.Attributes[包\u版本\u属性\u名称]。值;
    if(!packageVersionsById.TryGetValue(packageId,out ICollection packageVersions))
    {
    packageVersions=新列表();
    packageVersionsById.Add(packageId,packageVersions);
    }
    //如果(!packageVersions.Contains(packageVersion))
    如果(!packageVersions.Any(o=>o.Version.Equals(packageVersion)))
    {
    添加(新的PackageVersionItem()
    {
    SourceFile=PackageConfigFilePath,
    版本=打包版本
    });
    }
    如果(packageVersions.Count>1)
    {
    //检查程序包的断点
    
    <configuration>
      <config>
        <add key="repositoryPath" value="packages" />
      </config>
      <packageSources>
        <add key="local source" value="localnuget">
      </packageSources>
    </configuration>
    
    <add key="nuget.org" value="https://www.nuget.org/api/v2/" /> 
    
    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}";
            }
        }
    }