我注意到我们的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)
答案 0 :(得分:5)
我对我们的大型SSDT项目和TFS MSBUILD参数进行了以下更改,这些参数通过TFS构建定义将构建时间带到了可解释的级别:
1)添加/ p:CmdLineInMemoryStorage = TRUE到MSBUILD参数
这大大减少了我们的构建时间。通过添加此选项,我们的构建定义从40分钟到16分钟。 (时间是预先删除重复的引用)
2)删除了.sqlproj文件中的所有重复引用
随着时间的推移,.sqlproj文件设法引用一些文件2或3次。我们有大约40个重复的引用。这几乎可以肯定是由于我们许多分支机构之间的合并问题。 Visual Studio并没有抱怨这一点,并且非常愉快地构建,但是这些重复对我们在TFS中的构建时间产生了重大影响,当然这也是使用MSBUILD。
这是我在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 是一个包含 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 包含用于每个分区的数百或数千个元素。
$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;
也不应该从数据库外部引用对象
所以删除它们
$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");
编辑后,我们需要将 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 依赖项:
p:CmdLineInMemoryStorage
:5-6 分钟注意!获得的减少的 dacpac 可用于参考,但不适用于部署!