如何在解决方案中找到未使用的NuGet包?

Ste*_*veC 119 visual-studio nuget

如何在解决方案中找到未使用的NuGet包?

我有很多解决方案,其中有很多已安装的软件包,并且大量标记为有更新.

但是我担心可能会有变化,所以我首先要删除任何未使用的软件包.

Jee*_*Lee 58

Visual Studio 2019(版本16.9)内置了remove-unused-packages功能,我们现在需要手动启用它。

转到工具>选项>文本编辑器>C#>高级>(在分析部分下)勾选显示“删除未使用的引用”命令

Visual Studio 版本 16.10 具有删除未使用的引用功能。右键单击项目 > 删除未使用的引用。

在此输入图像描述

  • 仅供参考,此功能似乎仅适用于新的 csproj 文件;较旧的项目目前没有此选项。https://github.com/dotnet/roslyn/issues/54801 (7认同)
  • 我没有看到这个选项,您是否启用了其他选项来向您显示实验项目?我使用的是 VS Pro 2019,16.9.3 (3认同)
  • @debracey VS 版本 16.10 具有删除未使用的参考功能。右键单击项目 > 删除未使用的引用。 (3认同)
  • 这只是让我删除所有包。它没有说哪些不使用!VS2022 (2认同)
  • 同意丹尼尔的观点 - 刚刚尝试过,它删除了使用的引用并破坏了项目...... (2认同)

ule*_*lex 50

ReSharper 2016.1具有删除未使用的NuGet的功能.

它可以在解决方案和解决方案中的每个项目上运行,它可以执行以下操作:

  1. 分析您的代码并收集对程序集的引用.
  2. 根据程序集的用法构建NuGet使用图.
  3. 没有内容文件,未使用且没有使用依赖项的包被假定为未使用并建议删除.

不幸的是,这不适用于project.json项目(RSRP-454515)和ASP.NET核心项目(RSRP-459076)

  • @claudekennilol刚想通了.右键单击该项目,有一个"优化引用..."选项 (9认同)
  • 我必须是盲人但我没有看到任何实际运行此工具的方法 (4认同)
  • 非常好...再磨牙机可以解决问题 (2认同)
  • 我有2016.1,R#不会删除未使用的nuget引用。.我正在将project.json与nuget3一起使用-这是一个已知问题吗? (2认同)
  • @PeterMcEvoy是的,这是已知问题。感谢您指出。我已经更新了答案以澄清这一点。 (2认同)

小智 28

右键单击 Visual Studio 2019 中的 Dotnet 核心项目,您将看到“删除未使用的引用”选项。 在此输入图像描述

  • 我没有这个选项。您还需要执行其他操作才能启用它吗? (3认同)

dkn*_*ack 20

您可以使用Visual Studio扩展ResolveUR - 解析未使用的引用

通过解决方案和项目节点解决方案资源管理器工具窗口中的菜单项,解决Visual Studio 2012/2013项目中未使用的引用,包括nuget引用.

这不是一件容易的事,所以我建议在出现问题之前进行备份和/或提交,以便在出现问题时进行回滚.

  • [注意] 说明明确指出:`该工具未经测试可用于 DotNet Web 项目(Asp.Net、MVC)、Windows CE、Silverlight 项目类型。使用它需要您自担风险。` (2认同)

Nic*_*ore 16

您可以使用 ReSharper 2019.1.1 完成此操作。

右键单击项目 > 重构 > 删除未使用的引用。

如果你的项目很小,你也可以使用: project > Optimize Used References 。. .

会弹出一个窗口。选择所有引用并将它们全部删除。然后返回并重新添加那些给你一个编译器错误的。

  • 在使用 Resharper 的 VS.Net 2019 中,我在以下位置找到了此选项:_解决方案资源管理器 > **参考** > 优化参考..._ (4认同)

小智 9

在从最新版本开始的 Visual Studio 2019 和 Visual Studio 2022 中,您可以删除以前评论中报告的未使用的包,但仅限于 SDK 样式项目。如果您尝试旧项目(例如 .Net Framework),您将看不到此选项。作为解决方法,为了进行验证,您可以创建两个简单的控制台应用程序:一个使用 .Net Core 或更高版本,另一个使用 .Net Framework 4.7 或 4.8。


请参考:删除未使用的引用


PEK*_*PEK 7

下面是一个小 PowerShell 脚本,它为 .NET Core / .NET 5+ 项目查找冗余 NuGet 包。对于每个项目文件,它会删除每个引用一次并检查它是否编译。这将需要很多时间。在此之后,您将获得可能被排除的每个参考的摘要。最后,由您决定应该删除什么。您很可能无法删除它建议的所有内容(由于依赖性),但它应该为您提供一个良好的起点。

将下面的脚本另存为 ps1 文件,并将第89 行中的字符串C:\MySolutionDirectory替换为您要扫描的目录,然后运行 ​​ps1 文件。如果出现问题,请先进行备份。

function Get-PackageReferences {
    param($FileName, $IncludeReferences, $IncludeChildReferences)

    $xml = [xml] (Get-Content $FileName)

    $references = @()

    if($IncludeReferences) {
        $packageReferences = $xml | Select-Xml -XPath "Project/ItemGroup/PackageReference"

        foreach($node in $packageReferences)
        {
            if($node.Node.Include)
            {
                if($node.Node.Version)
                {
                    $references += [PSCustomObject]@{
                        File = (Split-Path $FileName -Leaf);
                        Name = $node.Node.Include;
                        Version = $node.Node.Version;
                    }
                }
            }
        }
    }

    if($IncludeChildReferences)
    {
        $projectReferences = $xml | Select-Xml -XPath "Project/ItemGroup/ProjectReference"

        foreach($node in $projectReferences)
        {
            if($node.Node.Include)
            {
                $childPath = Join-Path -Path (Split-Path $FileName -Parent) -ChildPath $node.Node.Include

                $childPackageReferences = Get-PackageReferences $childPath $true $true

                $references += $childPackageReferences
            }
        }   
    }

    return $references
}

function Get-ProjectReferences {
    param($FileName, $IncludeReferences, $IncludeChildReferences)

    $xml = [xml] (Get-Content $FileName)

    $references = @()

    if($IncludeReferences) {
        $projectReferences = $xml | Select-Xml -XPath "Project/ItemGroup/ProjectReference"

        foreach($node in $projectReferences)
        {
            if($node.Node.Include)
            {
                $references += [PSCustomObject]@{
                    File = (Split-Path $FileName -Leaf);
                    Name = $node.Node.Include;
                }
            }
        }
    }

    if($IncludeChildReferences)
    {
        $projectReferences = $xml | Select-Xml -XPath "Project/ItemGroup/ProjectReference"

        foreach($node in $projectReferences)
        {
            if($node.Node.Include)
            {
                $childPath = Join-Path -Path (Split-Path $FileName -Parent) -ChildPath $node.Node.Include

                $childProjectReferences = Get-ProjectReferences $childPath $true $true

                $references += $childProjectReferences
            }
        }   
    }

    return $references
}

$files = Get-ChildItem -Path C:\MySolutionDirectory -Filter *.csproj -Recurse

Write-Output "Number of projects: $($files.Length)"

$stopWatch = [System.Diagnostics.Stopwatch]::startNew()

$obseletes = @()

foreach($file in $files) {

    Write-Output ""
    Write-Output "Testing project: $($file.Name)"

    $rawFileContent = [System.IO.File]::ReadAllBytes($file.FullName)

    $childPackageReferences = Get-PackageReferences $file.FullName $false $true
    $childProjectReferences = Get-ProjectReferences $file.FullName $false $true

    $xml = [xml] (Get-Content $file.FullName)

    $packageReferences = $xml | Select-Xml -XPath "Project/ItemGroup/PackageReference"
    $projectReferences = $xml | Select-Xml -XPath "Project/ItemGroup/ProjectReference"

    $nodes = @($packageReferences) + @($projectReferences)

    foreach($node in $nodes)
    {
        $previousNode = $node.Node.PreviousSibling
        $parentNode = $node.Node.ParentNode
        $parentNode.RemoveChild($node.Node) > $null

        if($node.Node.Include)
        {
            $xml.Save($file.FullName)

            if($node.Node.Version)
            {
                $existingChildInclude = $childPackageReferences | Where-Object { $_.Name -eq $node.Node.Include -and $_.Version -eq $node.Node.Version } | Select-Object -First 1

                if($existingChildInclude)
                {
                    Write-Output "$($file.Name) references package $($node.Node.Include) ($($node.Node.Version)) that is also referenced in child project $($existingChildInclude.File)."
                    continue
                }
                else 
                {
                    Write-Host -NoNewline "Building $($file.Name) without package $($node.Node.Include) ($($node.Node.Version))... "
                }
            }
            else
            {
                $existingChildInclude = $childProjectReferences | Where-Object { $_.Name -eq $node.Node.Include } | Select-Object -First 1

                if($existingChildInclude)
                {
                    Write-Output "$($file.Name) references project $($node.Node.Include) that is also referenced in child project $($existingChildInclude.File)."
                    continue
                }
                else 
                {
                    Write-Host -NoNewline "Building $($file.Name) without project $($node.Node.Include)... "
                }
            }
        }
        else 
        {
            continue
        }

        dotnet build $file.FullName > $null

        if($LastExitCode -eq 0)
        {
            Write-Output "Building succeeded."

            if($node.Node.Version)
            {
                $obseletes += [PSCustomObject]@{
                    File = $file;
                    Type = 'Package';
                    Name = $node.Node.Include;
                    Version = $node.Node.Version;
                }
            }
            else
            {
                $obseletes += [PSCustomObject]@{
                    File = $file;
                    Type = 'Project';
                    Name = $node.Node.Include;
                }
            }
        }
        else 
        {
            Write-Output "Building failed."
        }


        if($null -eq $previousNode)
        {
            $parentNode.PrependChild($node.Node) > $null
        } 
        else 
        {
            $parentNode.InsertAfter($node.Node, $previousNode.Node) > $null
        }

        # $xml.OuterXml

        $xml.Save($file.FullName)
    }

    [System.IO.File]::WriteAllBytes($file.FullName, $rawFileContent)

    dotnet build $file.FullName > $null

    if($LastExitCode -ne 0)
    {
        Write-Error "Failed to build $($file.FullName) after project file restore. Was project broken before?"
        return
    }
}

Write-Output ""
Write-Output "-------------------------------------------------------------------------"
Write-Output "Analyse completed in $($stopWatch.Elapsed.TotalSeconds) seconds"
Write-Output "$($obseletes.Length) reference(s) could potentially be removed."

$previousFile = $null
foreach($obselete in $obseletes)
{
    if($previousFile -ne $obselete.File)
    {
        Write-Output ""
        Write-Output "Project: $($obselete.File.Name)"
    }

    if($obselete.Type -eq 'Package')
    {
        Write-Output "Package reference: $($obselete.Name) ($($obselete.Version))"
    }
    else
    {
        Write-Output "Project refence: $($obselete.Name)"
    }

    $previousFile = $obselete.File
}
Run Code Online (Sandbox Code Playgroud)

您可以在此处找到更多信息:https : //devblog.pekspro.com/posts/finding-redundant-project-references