【问题标题】:Managing organization's internal .Net dependencies管理组织的内部 .Net 依赖项
【发布时间】:2021-11-25 08:09:17
【问题描述】:

我们的组织有许多解决方案(30 多个),每个解决方案都包含许多项目。

在某些情况下,Solution "C" 中的 Class Library "C" 需要 Class Library "B" 中的 Solution "B",而 App "A" 中的 Solution "A" 中又需要 App "A"

我们正在寻找更好的方法来管理它,因为它目前非常耗时。

我们目前管理这些跨解决方案依赖项的做法是创建 NuGet 包,在本例中以 Class Library "C" 开头,并通过 Azure DevOps 将它们发布到我们的私有 NuGet 源。这一切都是在将更改推送到我们的源代码控制时通过自动化管道完成的。

创建 NuGet 包并将其推送到我们的私有 NuGet 源后,我们将转到下一个解决方案 (Class Library "B") 并更新 NuGet 引用以引用新构建的包 (Class Library "C")。

我们在App "A" 中再次重复这个过程。

这已经奏效了一段时间,但随着时间的推移,它已经对我们产生了影响,因为它很耗时。

Class Library "C" 的更改需要提交、构建管道并发布新包(谢天谢地全部自动化),然后我们才能开始处理Class Library "B",最后是App "A"

为了增加复杂性,这一切都假设我们在对深层嵌套依赖项Class Library "C" 进行原始更改期间没有引入任何错误。如果我们这样做了,我们必须在找到错误并修复它后重新从头开始重复整个过程。

此外,构建有时需要 4-5 分钟才能完成所有必要步骤。在此期间,我们继续执行其他任务。这会产生很多心理“上下文切换”当构建完成时,您也不会返回手头的原始任务正确,因为在它的确切时刻停止你正在做的事情是不合理的完成,因此很容易像我刚才提到的那样进行迭代,将一天的时间花在应该微不足道的事情上,而永远不会像您一样真正专注于任何一件事。

我正在寻找改进的解决方案来管理跨解决方案的内部依赖关系,从而减少所涉及的时间。

我们没有与 NuGet 结婚(尽管我们已经进行了大量投资)

【问题讨论】:

  • @omajid,不完全是。我正在寻找比 1 upvote 更久经考验的东西。 Git 子模块确实有效,但我们没有使用 Git(不幸的是)。我刚刚想到的另一种方法是符号链接。这可能行得通……我明天得再考虑一下。
  • 我们拥有庞大的多解决方案设置。我们将所有代码分解为层。 L1-L4。在顶部,我们有 UI 层、Web API 和其他端点。所有 L1-4 解决方案都将输出构建到一个 lib 文件夹中。所有项目都从这个lib 获取参考。端点层输出进入其特定的孤岛,但它们从lib 获取参考。我们从来没有喧嚣。您可以通过脚本进行分支并逐步构建。
  • 虽然可能会令人不快,但一个组织良好的巨型单一存储库可能是一个合法的解决方案。

标签: .net nuget dependency-management


【解决方案1】:

围绕管理您正在使用的 NuGet 版本,您可以使用Directory.Build.targets 为同一解决方案/存储库中的各个项目在一个地方设置共享导入和下游库版本,从而使您的生活更加轻松,因此它们都固定到相同的版本 - 让您更好地控制与 DLL-Hell 等效的 NuGet 依赖项。这是一个很好的参考:https://www.strathweb.com/2018/07/solution-wide-nuget-package-version-handling-with-msbuild-15/

另外,如果它有帮助,我使用此脚本将我的共享项目(位于单独的 repo/sln 中)导入到基于相对路径的相同解决方案中...这让我们可以编写代码并以交互方式调试共享项目主入口点 repos 的上下文。由于重命名同时适用于我的消费库,因此对于共享项目中的快速重构也非常方便。我们不会提交随后受影响的 .Sln / .CSProj 文件。

为了满足您的特定需求,还有很多改进空间,但我们在解决方案旁边转储了一个,称为 Add-CommonAsFramework.ps1 :-)

Param (
    [Parameter(Mandatory=$true, Position=1)]
    [string] $CommonPath, # Relative path to the common libraries, set up a default if your team has a convention
    [Parameter(Mandatory=$true, Position=2)]
    [string] $SolutionFile # Try setting the default up "$PSScriptRoot/MySolution.sln"
)

if (-not (Test-Path -Type Leaf $SolutionFile)) {
    throw "SolutionFile value does not exist, $SolutionFile is not found"
} else {
    $SolutionFile = (Get-Item $SolutionFile).FullName
}

$commonRoot = Get-Item $CommonPath
if (-not (Test-Path $commonRoot)) {
    throw "CommonPath value does not exist, $CommonPath is not found"
}

$commonProjects = @(Join-Path $commonRoot.FullName -ChildPath "src/**/*.csproj") # NOTE: Fix for your convention!
$solutionPath = Split-Path -Parent $SolutionFile

Function Get-PackageReferenceNames (
    [Parameter(Mandatory=$true, Position = 1)]
    [string] $SolutionFile,
    [Parameter(Mandatory=$true, Position = 2)]
    [string] $ProjectRelativePath
) {
    $solutionPath = Split-Path -Parent $SolutionFile
    $projectFullPath = Join-Path $solutionPath -ChildPath $ProjectRelativePath
    $packageReferencesRaw = dotnet list $projectFullPath package
    $packageReferencesRaw -match '^.*>' -replace '^ +> ','' `
        | ForEach-Object { $_.split(" ")[0] } `
        | Sort-Object -Unique
}

Function Update-PackageReferenceWithProjectReference (
    [Parameter(Mandatory=$true, Position = 1)]
    [string] $SolutionFile,
    [Parameter(Mandatory=$true, Position = 2)]
    [string] $ProjectRelativePath,
    [Parameter(Mandatory=$true, Position = 3)]
    [string] $PackageReferenceName,
    [Parameter(Mandatory=$true, Position = 3)]
    [string] $ReplaceProjectReferencePath
) {
    $solutionPath = Split-Path -Parent $SolutionFile
    $projectFullPath = Join-Path $SolutionPath -ChildPath $ProjectRelativePath
    dotnet add $projectFullPath reference $ReplaceProjectReferencePath
    dotnet remove $projectFullPath package $PackageReferenceName
}

Function Get-ProjectsInSolution (
    [Parameter(Mandatory=$true, Position = 1)]
    [string] $SolutionFile
) {
    # Outputs as a string list, with the first two lines as headers
    $projectsListing = & dotnet sln $SolutionFile list
    $projectsListing | Select-Object -Skip 2
}

Function Get-RelativePathTo (
    [Parameter(Mandatory=$true, Position = 1)]
    [string] $relativeTo,
    [Parameter(Mandatory=$true, Position = 2)]
    [string] $path
) {
    if (!(Test-Path $relativeTo)) {
        throw "Invalid path $relativeTo"
    }

    try {
        Push-Location $relativeTo
        return Get-Item $path | Resolve-Path -Relative
    } finally {
        Pop-Location
    }
}

$tmp = Get-Location
try {
    Set-Location $solutionPath
    
    $projects = Get-ProjectsInSolution $SolutionFile

    # Test and add Common project to "framework" folder in solution if not already there
    $commonProjects `
    | Get-Item `
    | Where-Object {
        $commonProjectRelativeToSolutionPath = Get-RelativePathTo $solutionPath $_.FullName
        $projects -notcontains $commonProjectRelativeToSolutionPath
    } `
    | ForEach-Object {
        $commonProjectRelativeToSolutionPath = Get-RelativePathTo $solutionPath $_.FullName
        dotnet sln $SolutionFile add --solution-folder "framework" $commonProjectRelativeToSolutionPath 
    }

    $commonProjectNames = @{}
    $commonProjects `
    | Get-Item `
    | ForEach-Object {
        $commonProjectRelativeToSolutionPath = Get-RelativePathTo $solutionPath $_.FullName
        $commonProjectNames.Add($_.Basename, $commonProjectRelativeToSolutionPath) 
    }

    # Replace any project that references them via NuGet to use the project reference instead
    $commonRootRelativeToSolutionPath = Get-RelativePathTo $solutionPath $commonRoot.FullName
    $projects `
    | Where-Object {
        Test-Path (Join-Path $solutionPath $_)
    } `
    | Where-Object {
        # Is not already a Common project
        -not $_.StartsWith($commonRootRelativeToSolutionPath)
    } `
    | ForEach-Object {
        $project = $_
        $packageReferences = Get-PackageReferenceNames $SolutionFile -ProjectRelativePath $project
        $commonPackageReferences = $packageReferences | Where-Object { $commonProjectNames.ContainsKey($_) }
        $commonPackageReferences | ForEach-Object {
            $commonProjectRelativePath = $commonProjectNames[$_]
            Update-PackageReferenceWithProjectReference $SolutionFile -ProjectRelativePath $project -PackageReferenceName $_ -ReplaceProjectReferencePath $commonProjectRelativePath
        }
    }
} finally {
    Set-Location $tmp
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-07-18
    • 2010-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-04
    • 2014-08-01
    相关资源
    最近更新 更多