2011-01-20 46 views

回答

5

(另一种方法)

的msdeploy包装一个MSBuild过程中,仅仅指刚调用运行您的项目。

TransformXml是.csproj或.vsproj构建的一个包含任务。

只需修改您的构建过程即可在您需要的任何文件上调用该任务。

例如,我们所要做的就是写一个自定义的目标

<Target Name="TransformFile"> 

    <TransformXml Source="$(DestinationPath)\$(Sourcefile)" 
     Transform="$(DestinationPath)\$(TransformFile)" 
     Destination="$(DestinationPath)\$(DestFile)" /> 
    </Target> 

然后修改您的.csproj来发布任务调用之前运行此。

<CallTarget Targets="TransformFile" 
    Condition="'$(CustomTransforms)'=='true'" /> 
2

简短回答:是的,你可以。但这是“困难的”。

龙答: 当我们部署网站的目的地,我们有平常web.test.config和web.prod.config。这工作得很好,直到我们引入了log4net.test.config和log4net.prod.config。 MSBuild不会自动通过并替换所有这些。它只会做web.config的。

如果你想要坚持到最后的代码片段。它显示了取一个配置并替换它的功能。但是......如果我描述整个过程,这会更有意义。

的过程:

  1. 的MSBuild使得网站的压缩文件包。
  2. 我们写了一个自定义的.net应用程序,它将采用该zip文件并对每个文件进行配置替换。重新保存压缩文件。
  3. 执行msdeploy命令来部署打包文件。

MSbuild不会自动替换所有额外的配置。有趣的是MSBuild将删除任何“额外”配置。所以你的log4net.test.config在构建完成后会消失。所以你必须做的第一件事是告诉msdbuild保持这些额外的文件。

你必须修改vbProj文件,包括一个新的设置:

<AutoParameterizationWebConfigConnectionStrings>False</AutoParameterizationWebConfigConnectionStrings> 

打开Web应用程序的vbProj文件到您喜欢的文本编辑器。导航到您希望它应用的每个部署配置(发布,产品,调试等)并将其添加到其中。这是我们的“发布”配置的一个例子。

<?xml version="1.0" encoding="utf-8"?> 
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> 
    ... 
    <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> 
    <DebugType>pdbonly</DebugType> 
    <DefineDebug>false</DefineDebug> 
    <DefineTrace>true</DefineTrace> 
    <Optimize>true</Optimize> 
    <OutputPath>bin\</OutputPath> 
    <DocumentationFile>Documentation.xml</DocumentationFile> 
    <NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022,42353,42354,42355</NoWarn> 
    <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> 
    <DeployIisAppPath>IISAppPath</DeployIisAppPath> 
    <AutoParameterizationWebConfigConnectionStrings>False</AutoParameterizationWebConfigConnectionStrings> 
    </PropertyGroup> 
    ... 
</Project> 

所以,现在msbduild将建立项目,并保留这些额外的文件,而不是做替换。现在你必须手动完成它们。

我们写了一个.net应用程序,它将监视这些新的zip文件。我编写了一些代码,它将遍历整个zip包,并找到与{configname}。{env} .config匹配的任何配置。它会提取它们,替换它们,然后放回去。要进行实际替换,我们使用与MSDeploy使用的相同的DLL。我也使用Ionic.Zip来做zip文件。

所以添加引用:

Microsoft.Build.dll 
Microsoft.Build.Engine.dll 
Microsoft.Web.Publishing.Tasks (possibly, not sure if you need this or not) 

导入:

Imports System.IO 
Imports System.Text.RegularExpressions 
Imports Microsoft.Build.BuildEngine 
Imports Microsoft.Build 

这里是通过压缩文件

specificpackage = "mypackagedsite.zip" 
configenvironment = "DEV" 'stupid i had to pass this in, but it's the environment in web.dev.config 

Directory.CreateDirectory(tempdir) 

Dim fi As New FileInfo(specificpackage) 

'copy zip file to temp dir 
Dim tempzip As String = tempdir & fi.Name 

File.Copy(specificpackage, tempzip) 

''extract configs to merge from file into temp dir 
'regex for the web.config 
'regex for the web.env.config 
'(?<site>\w+)\.(?<env>\w+)\.config$ 

Dim strMainConfigRegex As String = "/(?<configtype>\w+)\.config$" 
Dim strsubconfigregex As String = "(?<site>\w+)\.(?<env>\w+)\.config$" 
Dim strsubconfigregex2 As String = "(?<site>\w+)\.(?<env>\w+)\.config2$" 

Dim MainConfigRegex As New Regex(strMainConfigRegex, RegexOptions.Compiled Or RegexOptions.IgnoreCase) 
Dim SubConfigRegex As New Regex(strsubconfigregex, RegexOptions.Compiled Or RegexOptions.IgnoreCase) 
Dim SubConfigRegex2 As New Regex(strsubconfigregex2, RegexOptions.Compiled Or RegexOptions.IgnoreCase) 

Dim filetoadd As New Dictionary(Of String, String) 
Dim filestoremove As New List(Of ZipEntry) 
Using zip As ZipFile = ZipFile.Read(tempzip) 
    For Each entry As ZipEntry In From a In zip.Entries Where a.IsDirectory = False 
     For Each myMatch As Match In MainConfigRegex.Matches(entry.FileName) 
      If myMatch.Success Then 
       'found main config. 
       're-loop through, find any that are in the same dir as this, and also match the config name 
       Dim currentdir As String = Path.GetDirectoryName(entry.FileName) 
       Dim conifgmatchname As String = myMatch.Groups.Item("configtype").Value 

       For Each subentry In From b In zip.Entries Where b.IsDirectory = False _ 
            And UCase(Path.GetDirectoryName(b.FileName)) = UCase(currentdir) _ 
            And (UCase(Path.GetFileName(b.FileName)) = UCase(conifgmatchname & "." & configenvironment & ".config") Or 
              UCase(Path.GetFileName(b.FileName)) = UCase(conifgmatchname & "." & configenvironment & ".config2")) 

        entry.Extract(tempdir) 
        subentry.Extract(tempdir) 

        'Go ahead and do the transormation on these configs 
        Dim newtransform As New doTransform 
        newtransform.tempdir = tempdir 
        newtransform.filename = entry.FileName 
        newtransform.subfilename = subentry.FileName 
        Dim t1 As New Threading.Tasks.Task(AddressOf newtransform.doTransform) 
        t1.Start() 
        t1.Wait() 
        GC.Collect() 
        'sleep here because the build engine takes a while. 
        Threading.Thread.Sleep(2000) 
        GC.Collect() 

        File.Delete(tempdir & entry.FileName) 
        File.Move(tempdir & Path.GetDirectoryName(entry.FileName) & "/transformed.config", tempdir & entry.FileName) 
        'put them back into the zip file 
        filetoadd.Add(tempdir & entry.FileName, Path.GetDirectoryName(entry.FileName)) 
        filestoremove.Add(entry) 
       Next 
      End If 
     Next 
    Next 

    'loop through, remove all the "extra configs" 
    For Each entry As ZipEntry In From a In zip.Entries Where a.IsDirectory = False 

     Dim removed As Boolean = False 

     For Each myMatch As Match In SubConfigRegex.Matches(entry.FileName) 
      If myMatch.Success Then 
       filestoremove.Add(entry) 
       removed = True 
      End If 
     Next 
     If removed = False Then 
      For Each myMatch As Match In SubConfigRegex2.Matches(entry.FileName) 
       If myMatch.Success Then 
        filestoremove.Add(entry) 
       End If 
      Next 
     End If 
    Next 

    'delete them 
    For Each File In filestoremove 
     zip.RemoveEntry(File) 
    Next 

    For Each f In filetoadd 
     zip.AddFile(f.Key, f.Value) 
    Next 
    zip.Save() 
End Using 

最后,但最重要的家纺代码是我们实际上做了web.configs的替换。

Public Class doTransform 
    Property tempdir As String 
    Property filename As String 
    Property subfilename As String 
    Public Function doTransform() 
     'do the config swap using msbuild 
     Dim be As New Engine 
     Dim BuildProject As New BuildEngine.Project(be) 
     BuildProject.AddNewUsingTaskFromAssemblyFile("TransformXml", "$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll") 
     BuildProject.Targets.AddNewTarget("null") 
     BuildProject.AddNewPropertyGroup(True) 
     DirectCast(BuildProject.PropertyGroups(0), Microsoft.Build.BuildEngine.BuildPropertyGroup).AddNewProperty("GenerateResourceNeverLockTypeAssemblies", "true") 

     Dim bt As BuildTask 
     bt = BuildProject.Targets("null").AddNewTask("TransformXml") 

     bt.SetParameterValue("Source", tempdir & filename) 
     bt.SetParameterValue("Transform", tempdir & subfilename) 
     bt.SetParameterValue("Destination", tempdir & Path.GetDirectoryName(filename) & "/transformed.config") 
     'bt.Execute() 
     BuildProject.Build() 
     be.Shutdown() 

    End Function 

End Class 

就像我说的...这很难但它可以做到。

+0

优秀/全面的答案,但你可以用MsBuild来完成所有这些。看到我的回复。如果“额外”文件是一个问题,你只需要修改包含输入/输出文件的ItemGroups,这些文件将进入Publish/Package调用 – 2011-02-08 19:04:00

+0

是好主意。在我的情况下,只有问题是我们有15个站点使用不同的配置等来完成同样的事情。所以我在部署时建立了这个“做它”。你的很简单。 – 2011-02-08 21:07:14

3

泰勒的回答并不适合我,他也没有提供进一步的细节。所以我去探索Microsoft.Web.Publishing.targets文件来寻找解决方案。可以将以下MSBuild Target添加到项目文件以转换根应用程序目录中的所有其他配置文件。享受:)

<Target Name="TransformOtherConfigs" AfterTargets="CollectWebConfigsToTransform"> 
<ItemGroup> 
    <WebConfigsToTransform Include="@(FilesForPackagingFromProject)" 
          Condition="'%(FilesForPackagingFromProject.Extension)'=='.config'" 
          Exclude="*.$(Configuration).config;$(ProjectConfigFileName)"> 
    <TransformFile>%(RelativeDir)%(Filename).$(Configuration).config</TransformFile> 
    <TransformOriginalFile>$(TransformWebConfigIntermediateLocation)\original\%(DestinationRelativePath)</TransformOriginalFile> 
    <TransformOutputFile>$(TransformWebConfigIntermediateLocation)\transformed\%(DestinationRelativePath)</TransformOutputFile> 
    <TransformScope>$([System.IO.Path]::GetFullPath($(_PackageTempDir)\%(DestinationRelativePath)))</TransformScope> 
    </WebConfigsToTransform> 
    <WebConfigsToTransformOuputs Include="@(WebConfigsToTransform->'%(TransformOutputFile)')" /> 
</ItemGroup> 
</Target> 
1

只需添加到这个awnser,以修改其他文件不是与msdeploy(webdeploy)发表了应用程序的web.config可以设置在的parameters.xml文件scope属性该项目的根:

<parameters> 
    <parameter name="MyAppSetting" defaultvalue="_defaultValue_"> 
    <parameterentry match="/configuration/appSettings/add[@key='MyAppSetting']/@value" scope=".exe.config$" kind="XmlFile"> 
    </parameterentry> 
    </parameter> 
</parameters> 

scope是将用来寻找文件到match的XPath适用于正则表达式。我还没有广泛地尝试过这一点,但据我所知,它只是用后面提供的值替换xpath匹配的内容。

也有可用于kind,将有超过一个XPath不同的行为,见https://technet.microsoft.com/en-us/library/dd569084(v=ws.10).aspx的细节

其他值

注:这适用于当您正在使用的parameters.xml来,而不是当使用web.config.Debug/Release文件