与VS 2013相比,使用MSBuild的SSDT项目构建速度要慢得多

时间:2016-08-12 16:50:04

标签: tfs msbuild sql-server-data-tools

我注意到我们的SSDT项目通过TFS构建定义比在我的本地开发盒上通过VS 2013构建得慢得多。

在构建服务器本身,我可以在大约2分钟内通过VS 2013构建我们最大的SSDT项目。完全相同的项目在大约16分钟内使用MSBuild从同一服务器上的命令行构建(即以与TFS构建定义构建SSDT项目相同的方式构建)

我已收集/详细说明:两种方法的诊断输出,但在我的生命中看不到提供给MSBuild的参数的任何重大差异。

在VS 2013中,配置为Debug:AnyCPU。

我使用的MSBuild命令是:

msbuild / t:rebuild / p:Configuration = Debug / p:Platform = AnyCPU .sqlproj

我在更小的SSDT项目上尝试过相同的过程,我在构建时间方面也有类似的相对差异(例如,我在VS 2013中需要10秒钟的另一个项目,以及通过MSBuild需要70-80秒)

我有最新版本的VS 2013(12.0.40629.00 Update 5)和SSDT(12.0.60629.0)

还有其他人看过这个问题,甚至有解决方案吗?

转向VS 2015会解决问题吗?

已编辑以回复评论并包含诊断信息 - 15-08-2016

谢谢你们回复我的建议。

Ed,在针对MSBuild测试VS时,我使用了构建目标Rebuild,我相信它是Clean和Build的组合。在TFS构建定义中,我有" Clean Workspace" "清洁构建"设置为true,但是"获取来源"步骤只有9秒。

史蒂文,dbmdl因素听起来非常有希望。但是,我在从解决方案中删除所有dbmdl文件后再次尝试了测试,并且我仍然得到相同的时间(我验证了dbmdl文件没有在测试版本之间重新生成)。此外,我在测试之前删除了所有bin和obj文件夹,只是为了消除VS构建的缓存数据的任何可能性。

Cece,我已经有效地从这个问题中删除了TFS,因为在测试VS和MSBuild时我会遇到构建时间差异。我想如果我能解决这个问题,那么TFS构建时间也会被破解。你有一个很好的观点,关于构建步骤的时间。我不想用太多的信息充斥初始帖子,但现在是时候了:-)。我使用/ clp:PerformanceSummary选项来分析时间花在哪里。

在第1部分中,您可以看到时间几乎全部花费在" SqlBuildTask"中。很高兴知道,但在查明问题方面不是很有帮助。

在第2和第3部分中,我使用/ verbosity:diagnostic输出

从两种构建方法获得的信息

我看不出两个版本之间有任何实质性差异。两者都必须使用此DLL来执行该实际构建(MSBuild显式声明对此DLL的引用):

 C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\\Extensions\Microsoft\SQLDB\Dac\130\Microsoft.Data.Tools.Schema.Tasks.Sql.dll" 

-------------第1节-------------

(使用/ clp:PerformanceSummary从MSBuild输出)

Project Performance Summary:
        82518 ms  P:\<snip>.sqlproj   1 calls
                  82518 ms  rebuild                                    1 calls

Target Performance Summary:
        0 ms  BuildOnlySettings                          2 calls
        0 ms  GetReferenceAssemblyPaths                  2 calls
        0 ms  GetSqlSymbolsPath                          1 calls
        0 ms  BeforeBuild                                2 calls
        0 ms  ResolveReferences                          2 calls
        0 ms  GetCopyToOutputDirectoryXamlAppDefs        2 calls
        0 ms  SqlStudioSourceFilesToCopy                 2 calls
        0 ms  AssignProjectConfiguration                 1 calls
        0 ms  BeforeClean                                2 calls
        0 ms  _CopySourceItemsToOutputDirectory          2 calls
        0 ms  Build                                      2 calls
        0 ms  GetCopyToOutputDirectoryItems              2 calls
        0 ms  Clean                                      2 calls
        0 ms  CleanPublishFolder                         2 calls
        0 ms  GetFrameworkPaths                          2 calls
        0 ms  GetTargetPath                              2 calls
        0 ms  _CleanGetCurrentAndPriorFileWrites         2 calls
        0 ms  _CopyFilesMarkedCopyLocal                  1 calls
        0 ms  AfterResolveReferences                     2 calls
        0 ms  PreXsdCodeGen                              2 calls
        0 ms  CopyFilesToOutputDirectory                 2 calls
        0 ms  GetNativeManifest                          1 calls
        0 ms  GetInstalledSDKLocations                   2 calls
        0 ms  IncrementalClean                           2 calls
        0 ms  PrepareForRun                              2 calls
        0 ms  _SplitProjectReferencesByFileExistence     2 calls
        0 ms  _SetupSqlBuildOutputs                      2 calls
        0 ms  BeforeResolveReferences                    2 calls
        0 ms  GetSqlTargetPath                           1 calls
        0 ms  Rebuild                                    1 calls
        0 ms  AfterClean                                 2 calls
        0 ms  AfterBuild                                 2 calls
        0 ms  ExpandSDKReferences                        2 calls
        0 ms  CleanStaticCodeAnalysis                    2 calls
        0 ms  BeforeRebuild                              1 calls
        0 ms  CheckRequiredProperties                    2 calls
        0 ms  GenerateSqlTargetFrameworkMoniker          2 calls
        0 ms  ResolveSDKReferences                       2 calls
        0 ms  AfterRebuild                               1 calls
        0 ms  ResolveArtifactReferences                  2 calls
        0 ms  _CopyOutOfDateSourceItemsToOutputDirectoryAlways   1 calls
        0 ms  _CheckForCompileOutputs                    2 calls
        0 ms  PrepareForBuild                            2 calls
        0 ms  CleanXsdCodeGen                            2 calls
       16 ms  _CheckForInvalidConfigurationAndPlatform   2 calls
       16 ms  AssignTargetPaths                          2 calls
       16 ms  CoreClean                                  2 calls
       31 ms  ResolveAssemblyReferences                  2 calls
       31 ms  SqlPrepareForRun                           2 calls
       31 ms  CleanReferencedProjects                    2 calls
       63 ms  PostBuildEvent                             2 calls
       63 ms  PreBuildEvent                              2 calls
       94 ms  CoreCompile                                2 calls
      625 ms  _SetupSqlBuildInputs                       2 calls
     6452 ms  ResolveProjectReferences                   2 calls
    81502 ms  SqlBuild                                   2 calls

Task Performance Summary:
        0 ms  AssignTargetPath                          26 calls
        0 ms  CallTarget                                 2 calls
        0 ms  FindAppConfigFile                          2 calls
        0 ms  ConvertToAbsolutePath                      2 calls
        0 ms  AssignProjectConfiguration                 1 calls
        0 ms  ReadLinesFromFile                          4 calls
        0 ms  WriteLinesToFile                           4 calls
        0 ms  Copy                                       6 calls
        0 ms  FindUnderPath                             14 calls
        0 ms  RemoveDuplicates                           6 calls
        0 ms  MakeDir                                    4 calls
       16 ms  SqlScriptDependenciesTask                  2 calls
       16 ms  Delete                                    12 calls
       31 ms  ResolveAssemblyReference                   2 calls
       31 ms  Message                                    8 calls
       94 ms  Csc                                        1 calls
      126 ms  Exec                                       4 calls
      516 ms  SqlModelResolutionTask                     2 calls
     6468 ms  MSBuild                                    7 calls
    81424 ms  SqlBuildTask                               2 calls

-------------第2节-------------

(在VS的构建的SqlBuildTask阶段期间给出的诊断输出)

Target "SqlBuild" in file "C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v12.0\SSDT\Microsoft.Data.Tools.Schema.SqlTasks.targets" from project "P:<snip>.sqlproj" (target "Build" depends on it):
    Building target "SqlBuild" completely.
    Output file "P:<snip>.dacpac" does not exist.
    Task "SqlBuildTask"
        Task Parameter:SqlTarget=P:<snip>.dacpac
        Task Parameter:ContributorArguments=ConfigurationName=Debug;
        Task Parameter:BuildContributors=;
        Task Parameter:DeploymentContributors=;
        Task Parameter:CreateScriptFileName=<snip>.sql
        Task Parameter:DacApplicationName=<snip>
        Task Parameter:DacDescription=<snip>
        Task Parameter:DacFile=P:<snip>\bin\Debug\
        Task Parameter:DacVersion=3.27.0.0
        Task Parameter:DatabaseName=<snip>
        Task Parameter:DatabaseSchemaProviderName=Microsoft.Data.Tools.Schema.Sql.Sql100DatabaseSchemaProvider
        Task Parameter:DefaultSchema=dbo
        Task Parameter:DeploymentScriptName=<snip>.sql
        Task Parameter:DeployToDatabase=True
        Task Parameter:ImplicitDllAssemblyName=<snip>
        Task Parameter:ImplicitDllFileName=P:<snip>.dll
        Task Parameter:ImplicitDllSymbolsFileName=P:<snip>.pdb
        Task Parameter:ImplicitDllGenerateSqlClrDdl=true
        Task Parameter:IntermediateDirectory=P:<snip>\obj\Debug\
        Task Parameter:ModelCollation=1033,CI
        Task Parameter:OutputDirectory=P:<snip>\bin\Debug\
        Task Parameter:
    Source= <snip - list of all the source files in the SSDT project - matches list for MSBuild builds>

        Task Parameter:
    SqlCmdVariables= <snip - same for both builds>
        Task Parameter:
    SqlReferencePath=
        C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\mscorlib.dll
                CopyLocal=false
                FrameworkFile=true
                FusionName=mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
                ImageRuntime=v4.0.30319
                OriginalItemSpec=C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\mscorlib.dll
                ReferenceSourceTarget=ResolveAssemblyReference
                ResolvedFrom=C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\mscorlib.dll
                Version=4.0.0.0
        Task Parameter:SuppressTSqlWarnings=71562,71502,71558
        Task Parameter:ValidateCasingOnIdentifiers=true
        Task Parameter:CmdLineInMemoryStorage=false
        Task Parameter:DefaultCollation=SQL_Latin1_General_CP1_CI_AS
        Task Parameter:AnsiNullDefault=False
        Task Parameter:AnsiNulls=False
        Task Parameter:AnsiPadding=False
        Task Parameter:AnsiWarnings=False
        Task Parameter:ArithAbort=False
        Task Parameter:ConcatNullYieldsNull=False
        Task Parameter:QuotedIdentifier=False
        Task Parameter:NumericRoundAbort=False
        Task Parameter:RecursiveTriggersEnabled=False
        Task Parameter:DatabaseChaining=False
        Task Parameter:DatabaseState=ONLINE
        Task Parameter:CloseCursorOnCommitEnabled=False
        Task Parameter:DefaultCursor=GLOBAL
        Task Parameter:AutoClose=False
        Task Parameter:AutoCreateStatistics=True
        Task Parameter:AutoShrink=False
        Task Parameter:AutoUpdateStatistics=True
        Task Parameter:TornPageDetection=False
        Task Parameter:DatabaseAccess=MULTI_USER
        Task Parameter:Recovery=FULL
        Task Parameter:EnableFullTextSearch=False
        Task Parameter:DefaultFilegroup=PRIMARY
        Task Parameter:Trustworthy=True
        Task Parameter:AutoUpdateStatisticsAsynchronously=False
        Task Parameter:PageVerify=CHECKSUM
        Task Parameter:ServiceBrokerOption=DisableBroker
        Task Parameter:DateCorrelationOptimizationOn=False
        Task Parameter:Parameterization=SIMPLE
        Task Parameter:AllowSnapshotIsolation=False
        Task Parameter:ReadCommittedSnapshot=True
        Task Parameter:VardecimalStorageFormatOn=True
        Task Parameter:SupplementalLoggingOn=False
        Task Parameter:CompatibilityMode=100
        Task Parameter:IsChangeTrackingOn=False
        Task Parameter:IsChangeTrackingAutoCleanupOn=True
        Task Parameter:ChangeTrackingRetentionPeriod=2
        Task Parameter:ChangeTrackingRetentionUnit=Days
        Task Parameter:IsEncryptionOn=False
        Task Parameter:IsBrokerPriorityHonored=False
        Task Parameter:IncludeCompositeObjects=True
        Loading project references...
        Loading project files...
        Building the project model and resolving object interdependencies...
        Validating the project model...
        Writing model to P:<snip>\obj\Debug\Model.xml...

-------------第3节-------------

(在MSBuild构建的SqlBuildTask阶段给出的诊断输出)

Target "SqlBuild: (TargetId:68)" in file "C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v12.0\SSDT\Microsoft.Data.Tools.Schema.SqlTasks.targets" from project "P:<snip>.sqlproj" (target "Build" depends on it):
Building target "SqlBuild" completely.
Output file "P:<snip>.dacpac" does not exist.
Using "SqlBuildTask" task from assembly "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\\Extensions\Microsoft\SQLDB\Dac\130\Microsoft.Data.Tools.Schema.Tasks.Sql.dll".
Task "SqlBuildTask" (TaskId:56)
  Task Parameter:SqlTarget=P:<snip>.dacpac (TaskId:56)
  Task Parameter:ContributorArguments=ConfigurationName=Debug; (TaskId:56)
  Task Parameter:BuildContributors=; (TaskId:56)
  Task Parameter:DeploymentContributors=; (TaskId:56)
  Task Parameter:CreateScriptFileName=<snip>.sql (TaskId:56)
  Task Parameter:DacApplicationName=<snip> (TaskId:56)
  Task Parameter:DacDescription=<snip> (TaskId:56)
  Task Parameter:DacFile=P:<snip>\bin\Debug\ (TaskId:56)
  Task Parameter:DacVersion=3.27.0.0 (TaskId:56)
  Task Parameter:DatabaseName=<snip> (TaskId:56)
  Task Parameter:DatabaseSchemaProviderName=Microsoft.Data.Tools.Schema.Sql.Sql100DatabaseSchemaProvider (TaskId:56)
  Task Parameter:DefaultSchema=dbo (TaskId:56)
  Task Parameter:DeploymentScriptName=<snip>.sql (TaskId:56)
  Task Parameter:DeployToDatabase=True (TaskId:56)
  Task Parameter:ImplicitDllAssemblyName=<snip> (TaskId:56)
  Task Parameter:ImplicitDllFileName=P:<snip>.dll (TaskId:56)
  Task Parameter:ImplicitDllSymbolsFileName=P:<snip>.pdb (TaskId:56)
  Task Parameter:ImplicitDllGenerateSqlClrDdl=true (TaskId:56)
  Task Parameter:IntermediateDirectory=P:<snip>\obj\Debug\ (TaskId:56)
  Task Parameter:ModelCollation=1033,CI (TaskId:56)
  Task Parameter:OutputDirectory=P:<snip>\bin\Debug\ (TaskId:56)
  Task Parameter:
      Source= <snip - list of all the source files in the SSDT project - matches list for VS builds>
  Task Parameter:
      SqlCmdVariables= <snip - same for both builds>
  Task Parameter:
      SqlReferencePath=
          C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\mscorlib.dll
                  CopyLocal=false
                  FrameworkFile=true
                  FusionName=mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
                  ImageRuntime=v4.0.30319
                  OriginalItemSpec=C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\mscorlib.dll
                  ReferenceSourceTarget=ResolveAssemblyReference
                  ResolvedFrom=C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\mscorlib.dll
                  Version=4.0.0.0 (TaskId:56)
  Task Parameter:SuppressTSqlWarnings=71562,71502,71558 (TaskId:56)
  Task Parameter:ValidateCasingOnIdentifiers=true (TaskId:56)
  Task Parameter:CmdLineInMemoryStorage=false (TaskId:56)
  Task Parameter:DefaultCollation=SQL_Latin1_General_CP1_CI_AS (TaskId:56)
  Task Parameter:AnsiNullDefault=False (TaskId:56)
  Task Parameter:AnsiNulls=False (TaskId:56)
  Task Parameter:AnsiPadding=False (TaskId:56)
  Task Parameter:AnsiWarnings=False (TaskId:56)
  Task Parameter:ArithAbort=False (TaskId:56)
  Task Parameter:ConcatNullYieldsNull=False (TaskId:56)
  Task Parameter:QuotedIdentifier=False (TaskId:56)
  Task Parameter:NumericRoundAbort=False (TaskId:56)
  Task Parameter:RecursiveTriggersEnabled=False (TaskId:56)
  Task Parameter:DatabaseChaining=False (TaskId:56)
  Task Parameter:DatabaseState=ONLINE (TaskId:56)
  Task Parameter:CloseCursorOnCommitEnabled=False (TaskId:56)
  Task Parameter:DefaultCursor=GLOBAL (TaskId:56)
  Task Parameter:AutoClose=False (TaskId:56)
  Task Parameter:AutoCreateStatistics=True (TaskId:56)
  Task Parameter:AutoShrink=False (TaskId:56)
  Task Parameter:AutoUpdateStatistics=True (TaskId:56)
  Task Parameter:TornPageDetection=False (TaskId:56)
  Task Parameter:DatabaseAccess=MULTI_USER (TaskId:56)
  Task Parameter:Recovery=FULL (TaskId:56)
  Task Parameter:EnableFullTextSearch=False (TaskId:56)
  Task Parameter:DefaultFilegroup=PRIMARY (TaskId:56)
  Task Parameter:Trustworthy=True (TaskId:56)
  Task Parameter:AutoUpdateStatisticsAsynchronously=False (TaskId:56)
  Task Parameter:PageVerify=CHECKSUM (TaskId:56)
  Task Parameter:ServiceBrokerOption=DisableBroker (TaskId:56)
  Task Parameter:DateCorrelationOptimizationOn=False (TaskId:56)
  Task Parameter:Parameterization=SIMPLE (TaskId:56)
  Task Parameter:AllowSnapshotIsolation=False (TaskId:56)
  Task Parameter:ReadCommittedSnapshot=True (TaskId:56)
  Task Parameter:VardecimalStorageFormatOn=True (TaskId:56)
  Task Parameter:SupplementalLoggingOn=False (TaskId:56)
  Task Parameter:CompatibilityMode=100 (TaskId:56)
  Task Parameter:IsChangeTrackingOn=False (TaskId:56)
  Task Parameter:IsChangeTrackingAutoCleanupOn=True (TaskId:56)
  Task Parameter:ChangeTrackingRetentionPeriod=2 (TaskId:56)
  Task Parameter:ChangeTrackingRetentionUnit=Days (TaskId:56)
  Task Parameter:IsEncryptionOn=False (TaskId:56)
  Task Parameter:IsBrokerPriorityHonored=False (TaskId:56)
  Task Parameter:IncludeCompositeObjects=True (TaskId:56)
  Creating a model to represent the project... (TaskId:56)
  Loading project references... (TaskId:56)
  Loading project files... (TaskId:56)
  Building the project model and resolving object interdependencies... (TaskId:56)
  Validating the project model... (TaskId:56)
  Writing model to P:<snip>\obj\Debug\Model.xml... (TaskId:56)
Done executing task "SqlBuildTask". (TaskId:56)

2 个答案:

答案 0 :(得分:5)

我对我们的大型SSDT项目和TFS MSBUILD参数进行了以下更改,这些参数通过TFS构建定义将构建时间带到了可解释的级别:

1)添加/ p:CmdLineInMemoryStorage = TRUE到MSBUILD参数

Adding CmdLineInMemoryStorage MSBUILD argument to TFS build definition

这大大减少了我们的构建时间。通过添加此选项,我们的构建定义从40分钟到16分钟。 (时间是预先删除重复的引用)

2)删除了.sqlproj文件中的所有重复引用

随着时间的推移,.sqlproj文件设法引用一些文件2或3次。我们有大约40个重复的引用。这几乎可以肯定是由于我们许多分支机构之间的合并问题。 Visual Studio并没有抱怨这一点,并且非常愉快地构建,但是这些重复对我们在TFS中的构建时间产生了重大影响,当然这也是使用MSBUILD。

在sqlproj文件中查找重复引用

这是我在sqlproj文件中找到重复项的powershell脚本(更改 $ root 指向包含.sqlproj文件的文件夹):

[Reflection.Assembly]::LoadWithPartialName("System.Xml.Linq") | Out-Null

$root = "C:\TFS"

$sqlprojFiles = Get-ChildItem -Path $root -Filter "*.sqlproj" -Recurse
$numFilesChecked=0

foreach ($f in $sqlprojFiles)
{
    $numFilesChecked++
    $fullName = $f.FullName

    $xml = [System.Xml.Linq.XDocument]::Load($fullName)
    $ns = $xml.Root.Name.Namespace;

    $itemGroups = $xml.Descendants() | Where-Object { $_.Parent.Name -eq $ns + "ItemGroup" };

    $dict = @{}
    $itemGroups.Attributes() | Where-Object { $_.Name -eq "Include"} `
        | % { if ($dict.ContainsKey($_.Value)) {$dict[$_.Value]++} else {$dict.Add($_.Value,1)} }

    $duplicates = $dict.Keys | Where-Object { $dict[$_] -gt 1} 

    $fullName

    if ($duplicates.Count -gt 0)
    {
        Write-Output "--------------------------------------------------------"
        $duplicates | % {$_+", "+$dict[$_]}
    }
    else
    {
        Write-Output "No duplicates found"
    }
}

"Files checked: $numFilesChecked"

我使用它的输出以文本方式手动编辑.sqlproj文件(即&#34;卸载项目&#34;然后&#34;编辑.sqlproj&#34;在VS中)

解释构建时间差异

既然我们的大型sqlproj在我们的构建服务器上构建了9分钟,我认为可以解释VS和MSBuild之间的构建时间差异。

我被告知VS利用了MSTild从命令行中未使用的有关SSDT项目的预编译信息。我无法证实这一点,我当然也没有在&#34; / verbosity中看到任何证据:诊断&#34;来自VS的输出(我可能已经错过了它)。然而,在VS完成对项目的分析之后,在VS 中构建大型项目需要2分钟,这表明它在构建过程中使用了缓存信息。 VS需要几分钟才能完成对大型项目的分析。

MSBuild构建时间(来自命令行或TFS构建定义)现在需要一致的9分钟。额外的7分钟几乎可以肯定地由MSBuild进程在每个构建上分析项目 - VS似乎做了一次(或至少定期),然后在构建时利用缓存的信息。

答案 1 :(得分:1)

对于那些经历过 SSDT 构建缓慢且具有许多 dacpac 依赖项的项目的人来说,还有一种选择:简化这些 dacpac 并使它们更小。

关键假设是意识到引用 dacpac 与部署它不同:它不应该需要像“正常”sqlproj 那么多的元数据。

以下是可以使用 dacpac-references 完成的可能步骤,以大幅减少其大小和构建时间(除了 Mark 和其他人提到的选项)。

显示的代码提示在 Powershell 中。

DACPAC 内部

DACPAC 是一个包含 XML 文件的 ZIP 档案。因此,您可以打开它并查看 model.xml,它是项目 本身,表示为元数据和源的单个文件。这个文件可能非常大(100MB+),正如您所知,XML 文件的大小会影响它的处理性能。 SSDT 对不同的 sql 元素生成非常详细的描述。

所以要开始处理它,解压并加载源 XML:

Expand-Archive -Path $dacpac -DestinationPath $expandedFolder | Out-Null;
[xml] $dacpacXml = Get-Content "$expandedFolder/model.xml";

$ns = New-Object System.Xml.XmlNamespaceManager($dacpacXml.value.NameTable);
$ns.AddNamespace("dac", $dacpacXml.value.DocumentElement.NamespaceURI);

未用于引用的元数据

授权

我相信您的 TSQL 代码应该需要对不同数据库中的此类对象进行硬引用,例如:

  • 用户、登录名、角色
  • 权限、角色成员、授权和执行上下文(用户/登录)信息

所以删除它们

$dacpacXml.value.DocumentElement.SelectNodes("//dac:Element[@Type = 'SqlRoleMembership']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Element[@Type = 'SqlPermissionStatement']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Element[@Type = 'SqlLogin']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Element[@Type = 'SqlUser']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Element[@Type = 'SqlRole']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Relationship[@Name = 'Login']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Relationship[@Name = 'User']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Relationship[@Name = 'Authorizer']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Relationship[@Name = 'Schema']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;

如果您将所有这些都保留在您的 sqlproj(用户、登录名)中,我建议您查看此方法并从 代码中删除 利用 级别的数据。

加上敏感数据和签名

$dacpacXml.value.DocumentElement.SelectNodes("//dac:Property[@Name = 'EncryptionPassword' or @Name = 'Password']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Element[@Type = 'SqlCertificate' or @Type = 'SqlSymmetricKey']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Element[@Type = 'SqlSignature']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;

文件存储和分区信息

如果您的分区表具有多个分区,那么您的 dacpac 包含用于每个分区的数百或数千个元素。

elements

$dacpacXml.value.DocumentElement.SelectNodes("//dac:Relationship[@Name = 'DataCompressionOptions' or @Name = 'BoundaryValues']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Relationship[@Name = 'Filegroups']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Relationship[@Name = 'Filegroup']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Relationship[@Name = 'FilegroupForTextImage']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Relationship[@Name = 'PartitionScheme' or @Name = 'PartitionColumn']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;

DML 和 DDL 对象

也不应该从数据库外部引用对象

  • 触发器
  • 指标
  • 默认约束

所以删除它们

$dacpacXml.value.DocumentElement.SelectNodes("//dac:Element[@Type = 'SqlIndex']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Element[@Type = 'SqlDmlTrigger']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Element[@Type = 'SqlIndexedColumnSpecification']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Element[@Type = 'SqlDatabaseDdlTrigger']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Element[@Type = 'SqlDefaultConstraint']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;

来源

虽然可以引用 SP,但它的来源不是必需的 - 我们已经有了它的所有元数据:参数被详细描述为具有类型信息等的单独元素。所以删除所有来源

$dacpacXml.value.DocumentElement.SelectNodes("//dac:Element[@Type = 'SqlView']/dac:Property[@Name = 'QueryScript']", $ns) | % { write-verbose $_.ParentNode.Name; $_.ParentNode.removechild($_); } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Element[@Type = 'SqlProcedure']/dac:Property[@Name = 'BodyScript']", $ns) | % { write-verbose $_.ParentNode.Name; $_.ParentNode.removechild($_); } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Element[@Type = 'SqlScriptFunctionImplementation']/dac:Property[@Name = 'BodyScript']", $ns) | % { write-verbose $_.ParentNode.Name; $_.ParentNode.removechild($_); } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Element[@Type = 'SqlAssemblyFile']/dac:Property[@Name = 'Source']", $ns) | % { write-verbose $_.ParentNode.Name; $_.ParentNode.removechild($_); } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Element[@Type = 'SqlComputedColumn']/dac:Property[@Name = 'ExpressionScript']", $ns) | % { write-verbose $_.ParentNode.Name; $_.ParentNode.removechild($_); } | Out-Null;

以及从这些来源构建的依赖项

$dacpacXml.value.DocumentElement.SelectNodes("//dac:Relationship[@Name = 'BodyDependencies']", $ns) | % { write-verbose $_.ParentNode.Name; $_.ParentNode.removechild($_); } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Relationship[@Name = 'QueryDependencies' or @Name = 'ExpressionDependencies']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Relationship[@Name = 'DynamicObjects']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Relationship[@Name = 'AssemblySources']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Element[@Type = 'SqlQueue']/dac:Relationship", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Element[@Type = 'SqlContract']/dac:Relationship[@Type = 'Messages']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;

注释

$dacpacXml.value.DocumentElement.SelectNodes("//dac:Annotation[@Type = 'SysCommentsObjectAnnotation']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Relationship[@Name = 'BoundTargets']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Annotation[@Type = 'PersistedResolvableAnnotation']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Element[@Type = 'SqlExtendedProperty']", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;
$dacpacXml.value.DocumentElement.SelectNodes("//dac:Element[@Type = 'SqlSimpleColumn']/dac:AttachedAnnotation", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;

清理

删除产生了许多没有子元素的空元素。删除它们

$dacpacXml.value.DocumentElement.SelectNodes("//dac:Entry[not(child::*)]", $ns) | % { $_.ParentNode.RemoveChild($_) } | Out-Null;

保存更改

$dacpacXML.Save("$expandedFolder/model.xml");

重新压缩 DACPAC

编辑后,我们需要将 xml 文件重新转换为 dacpac。在此之前,需要重新计算和更新 Origin.xml 中的校验和,这也是 dacpac 存档的一部分。校验和不匹配的 DACPAC 将不起作用。

$newModelHash = (Get-FileHash -Path "$expandedFolder/model.xml").Hash;

[xml] $originXML = Get-Content "$expandedFolder/Origin.xml";
$originXML.DacOrigin.Checksums.Checksum.InnerText = $checksum;
$originXML.Save("$expandedFolder/Origin.xml");

现在我们准备好了,所以将其压缩回来(注意路径末尾的 /* 以压缩所有文件,但不压缩包含文件夹)。复制,然后重命名。

Compress-Archive -Path "$expandedFolder/*" -DestinationPath "$dacpac.zip" -CompressionLevel Optimal -Force;

构建

现在将重新压缩的引用 (dacpacs) 放入适当的文件夹并重新运行构建过程。经测试,编辑过的 dacpac 可以变小 10-50 倍,适用于 VS 或 MSBuild 中的引用解析。这个肮脏的技巧可以将构建时间减半。

示例 SQLPROJ 构建 (cli msbuild) 统计信息,包含约 3K 进程、数百个表和 10-20 个 dacpac 依赖项:

  • 天真:~15 分钟
  • p:CmdLineInMemoryStorage5-6 分钟
  • 被黑 dacpac:2 分钟

注意!获得的减少的 dacpac 可用于参考,但不适用于部署