I found several solutions which use Post-Build event.
Is there a way to publish ASP.NET MVC web site with compiled views (to prevent first user view delay) but do not compile them in development environment (to compile site faster)?
Thank you!
P.S. Ideally it would be to configure One Click Publish feature in Visual Studio 2010
Edit
As I understood <AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" /> does the same thing as
<MvcBuildViews>true</MvcBuildViews>
<EnableUpdateable>false</EnableUpdateable>
but for early versions
My .csproj looks like
...
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<MvcBuildViews>true</MvcBuildViews>
<EnableUpdateable>false</EnableUpdateable>
...
and views precompile fine but I can't get a single library for deployment like with Asp .net applications. That's why first load page delay still exist...
Please help
You can configure the post-build event to compile views only when you compile in Release mode so that it does not slow you down during development (when you presumably compile in Debug mode.)
For example, the following node in a .csproj will only compile views when the project is compiled in Release mode.
<Target Name="AfterBuild" Condition="'$(Configuration)'=='Release'">
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" />
</Target>
Or you can set MvcBuildViews to true, but only do it for certain configurations:
<MvcBuildViews>true</MvcBuildViews>
<Target Name="MvcBuildViews" AfterTargets="AfterBuild" Condition="'$(MvcBuildViews)'=='true' And '$(Configuration)'!='Debug'">
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" />
</Target>
Notice i've added And '$(Configuration)'!='Debug'" to the condition.
Related
I'm trying to make it so when I deploy my mvc web application the app_offline screen is the first item copied to the server (using web deploy to azure vm) and then at the end of the deployment process the file is deleted.
The issue I have is that targets in my pubxml file aren't getting hit, below is an example of the targets. I've read various articles which suggest this is the correct way of going about this but I'm wondering if this information is out of date or not compatible for mvc some how.
<Target Name="TakeOffline" AfterTargets="BeforePublish" >
<Message Text="Taking application offline" Importance="high" />
</Target>
<Target Name="RestoreOnline" AfterTargets="AfterPublish" >
<Message Text="Restoring application online" Importance="high" />
</Target>
I had the same problem when publishing an MVC application. The documentation on the AfterTargets and BeforeTargets parameters seems to be lacking, but this page https://learn.microsoft.com/en-au/visualstudio/msbuild/target-build-order?view=vs-2015, a mixture of differing samples and some trial and error, helped me get it working.
I'm using Visual Studio 2019 version 16.7.3, ASP.NET 4.8 and MVC5 - all with the latest updates as of writing. This is what worked for me with Web Deploy, in my .pubxml file...
<Target Name="TakeOffline" BeforeTargets="MSDeployPublish" >
<Message Text="Taking application offline" Importance="high" />
[copy app_offline.htm to destination here]
</Target>
<Target Name="RestoreOnline" AfterTargets="MSDeployPublish" >
<Message Text="Restoring application online" Importance="high" />
[delete app_offline.htm from destination here]
</Target>
I'm using Aspnetboilerplate framework in my Web project.
Everything was working until I noticed that my production environment was very slow on first page access.
Doing some researchs I discovered that the problem was to not pre compile cshtml views on build. So, in my vsts build definition I added the follow:
/p:PrecompileBeforePublish=true /p:UseMerge=true /p:SingleAssemblyName=AppCode
But now I'm getting the follow error on all my cshtml views:
Seduca.Web\obj\Release\AspnetCompileMerge\Source\App\Main\views\aulas\index.cshtml(5,0): Error CS0103: The name 'IsGranted' does not exist in the current context
Works if I put this on top of each view:
#inherits Seduca.Web.Views.SeducaWebViewPageBase
but it looks like my file (web.config) is being ignored.
My .csproj looks like this:
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<MvcBuildViews>true</MvcBuildViews>
<LangVersion>6</LangVersion>
</PropertyGroup>
...
<Target Name="MvcBuildViews" AfterTargets="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
<ItemGroup>
<ExtraWebConfigs Include="$(BaseIntermediateOutputPath)\**\web.config" />
<ExtraPackageTmp Include="$([System.IO.Directory]::GetDirectories("$(BaseIntermediateOutputPath)", "PackageTmp", System.IO.SearchOption.AllDirectories))" />
</ItemGroup>
<Delete Files="#(ExtraWebConfigs)" />
<RemoveDir Directories="#(ExtraPackageTmp)" />
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" />
</Target>
And the web.config:
The error only happens in vsts build. In local deploy, (with release option and precompile) it works.
I've tried with hosted and private agents on vsts.
The issue appears to be that you are not adding the namespaces to the correct Web.config file. .cshtml files get their globally imported namespaces from the Views\Web.config file, not the root Web.config file.
The information you have in the root Web.config file is correct, you just need to move it to the Views\Web.config file.
I have an web project Project A and a library Project B.
Project B is being built into Project A\Plugins.
The Problem is when Project A is being built via CI Project B isn't deployed with Project A. MSBuild params in CI are:
/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:PackageLocation="$(build.artifactstagingdirectory)\\" /t:ProjectA
I added the following piece of code into Project A.csproj to deploy Project B with A:
<ItemGroup>
<ProjectsToBuild Include="..\..\Plugins\**\*ProjectB*proj" />
</ItemGroup>
<Target Name="Plugin" AfterTargets="Build">
<Message Text="Building Plugins" Importance="High" />
<MSBuild Projects="#(ProjectsToBuild)" ContinueOnError="false" Properties="Configuration=$(Configuration)">
<Output ItemName="OutputFiles" TaskParameter="TargetOutputs" />
</MSBuild>
<CallTarget Targets="CopyPlugins"/>
</Target>
<PropertyGroup>
<PluginDirectory>Plugins</PluginDirectory>
</PropertyGroup>
<Target Name="CopyPlugins">
<Message Text="Copying plugins to $(_PackageTempDir)\Plugins\ ($(ProjectDir)$(PluginDirectory))" Importance="high" />
<CreateItem Include="$(ProjectDir)$(PluginDirectory)\**\*.*">
<Output TaskParameter="Include" ItemName="PluginFiles" />
</CreateItem>
<Copy DestinationFolder="$(_PackageTempDir)\Plugins\" SourceFiles="#(PluginFiles)" SkipUnchangedFiles="true" />
</Target>
In the log executing order seems fine (Copy after Build), but after being deployed the Plugin folder doesn't exist on the target server.
Am I missing anything?
The files need to be included in the project. Since the files are generated dynamically, you can add this code to project file (e.g. XX.csproj)
<Content Include="plugin\**\*.*" />
From your description, you would need to check in Project A\Plugins folder into TFS. But it's not suggested.
We suggest you make a project reference in the same solution or create NuGet for package management. It's recommended to build a nuget package for Project B, and publish this Nuget package, then restore the library to Project A when you build it.
In my Asp.Net MVC 4 project, I've set in the .csproj file to build the view <MvcBuildViews>true</MvcBuildViews>. The problem is that building the project I got the error:
It is an error to use a section registered as allowDefinition='MachineToApplication' beyond application level. This error can be caused by a virtual directory not being configured as an application in IIS.
I tried to delete the obj folder but the error keep raising. The error specify that the problem is in the authentication tag row:
<system.web>
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="2880" />
</authentication>
Often, I'm able to run the application by running the application (I got the error), building the app and after that running again.
Doing what #matrixugly suggests will fix the issue, but will also cause the compile-time view checking to stop working as well. I am assuming you still want to error check your views at compile time? If that is the case, better fixes below.
In order to understand why these solutions work, we have to first know how the problem is created:
The developer wants compile-time checking on views, so they set MvcBuildViews=true.
The application builds fine, UNTIL they publish the project.
Subsequent attempts to build the project result in a compile-time error: It is an error to use a section registered as allowDefinition='MachineToApplication' beyond application level. This error can be caused by a virtual directory not being configured as an application in IIS.
So what causes this issue? When the project is published the compiler, by default it uses <project-dir>\obj\ to place copies of the source files that it will work with. Unfortunately, these files are not automatically deleted when publishing is complete. The next time the developer compiles the project with MvcBuildViews=true, it will error out because the aspnet compiler includes the obj\ folder during compilation, since it is underneath the <project-dir> folder.
So how do we fix this? Well, you have four options:
Set MvcBuildViews=false. I don't really consider this a solution, so let's move on.
Delete the files in <project-dir>\obj\. Works, but can be a hassle since it has to be done after every publish.
Change the path that publishing uses as an intermediate directory through the use of the <BaseIntermediateOutputPath> property in your project config file.Example (Ref: this link):
<BaseIntermediateOutputPath>
[SomeKnownLocationIHaveAccessTo]
</BaseIntermediateOutputPath>
Add a new section in your project config file that deletes the offending files for you on build (reference Microsoft Connect). I've even made it easy for you, just copy and paste:
<PropertyGroup>
<_EnableCleanOnBuildForMvcViews Condition=" '$(_EnableCleanOnBuildForMvcViews)'=='' ">true</_EnableCleanOnBuildForMvcViews>
</PropertyGroup>
<Target Name="CleanupForBuildMvcViews" Condition=" '$(_EnableCleanOnBuildForMvcViews)'=='true' and '$(MVCBuildViews)'=='true' " BeforeTargets="MvcBuildViews">
<ItemGroup>
<_TempWebConfigToDelete Include="$(BaseIntermediateOutputPath)**\Package\**\*" />
<_TempWebConfigToDelete Include="$(BaseIntermediateOutputPath)**\TransformWebConfig\**\*" />
<_TempWebConfigToDelete Include="$(BaseIntermediateOutputPath)**\CSAutoParameterize\**\*" />
<_TempWebConfigToDelete Include="$(BaseIntermediateOutputPath)**\TempPE\**\*" />
</ItemGroup>
<Delete Files="#(_TempWebConfigToDelete)"/>
</Target>
My recommendation would be to use either option 3 or 4.
N.B. For those that have never edited their project file, you can't edit it while loaded. It must first be unloaded by right clicking it and selecting Unload Project. You can then right-click the project and edit the project file. Alternatively, you can edit the file outside of Visual Studio.
I had the exact same problem when trying to publish my web application after enabling MvcBuildViews to validate my Razor syntax
I found this code in my web config
<Target Name="MvcBuildViews" AfterTargets="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" />
</Target>
Try commenting it out, so that the compiler behavior is not changed
<!--<Target Name="MvcBuildViews" AfterTargets="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" />
</Target>-->
#newmanth answer is excellent, but outdated. Year 2022 and let me tell you - this CleanupForBuildMvcViews is actually oficially included within C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Microsoft\VisualStudio\v16.0\Web\Microsoft.Web.Publishing.targets! :)
It even features the link to (now broken) Microsoft Connect as #newmanth references.
Here is the snippet:
<!--Deal with http://connect.microsoft.com/VisualStudio/feedback/details/779737/error-allowdefinition-machinetoapplication-beyond-application-level,
we will need to clean up our temp folder before MVC project starts the pre-compile-->
<PropertyGroup>
<_EnableCleanOnBuildForMvcViews Condition=" '$(_EnableCleanOnBuildForMvcViews)'=='' ">true</_EnableCleanOnBuildForMvcViews>
</PropertyGroup>
<Target Name="CleanupForBuildMvcViews" Condition=" '$(_EnableCleanOnBuildForMvcViews)'=='true' and '$(MVCBuildViews)'=='true' " BeforeTargets="MvcBuildViews">
<ItemGroup>
<_PublishTempFolderNamesToCleanup Include="Database;TransformWebConfig;CSAutoParameterize;InsertAdditionalCS;ProfileTransformWebConfig;Package" />
</ItemGroup>
<!--Force msbuild to expand all the wildcard characters so to get real file paths-->
<CreateItem Include="#(_PublishTempFolderNamesToCleanup->'$(BaseIntermediateOutputPath)**\%(identity)\**\*')">
<Output TaskParameter="Include" ItemName="_EvaluatedPublishTempFolderNamesToCleanup" />
</CreateItem>
<Delete Files="#(_EvaluatedPublishTempFolderNamesToCleanup)" />
</Target>
However I still get the said exception. In my Case I had to delete AspnetCompileMerge folder too. And name Target in another name, not to overwrite it:
<PropertyGroup>
<_EnableCleanOnBuildForMvcViews Condition=" '$(_EnableCleanOnBuildForMvcViews)'=='' ">true</_EnableCleanOnBuildForMvcViews>
</PropertyGroup>
<Target Name="CleanupForBuildMvcViews2" Condition=" '$(_EnableCleanOnBuildForMvcViews)'=='true' and '$(MVCBuildViews)'=='true' " BeforeTargets="MvcBuildViews">
<ItemGroup>
<_TempWebConfigToDelete Include="$(BaseIntermediateOutputPath)**\AspnetCompileMerge\**\*" />
</ItemGroup>
<Delete Files="#(_TempWebConfigToDelete)"/>
</Target>
I'm having some strange problems copying files in a custom script in TFS2008 without SP1, I have to run the build several times to get the files copied (most of the times its in the second build that i get the files), let me give you the details:
This is happening with ASP sites and VS2003 Web solutions, (vs2008 solutions are OK)
In ASP I have a dummy 2008 solution, the build compiles this dummy, I override AfterCompile and in there I copy all the files to the drop location
In VS 2003 i have also a dummy 2008 solution, the build first compiles the dummy, I override AfterCompile, use "Exec" and "Command" to compile the 2003 solution and then copy the files to the drop location.
As you can see both approaches are similar, I'm not having problems with the builds per se, my problem is reproducible in two ways (and yes, i do check out, update, check in and then test the build):
Create a new build, configure the script, run the build the first time, some DLL's in the bin folder are not copied, run the build for the second time and i get all the files.
Build already configured and running OK, add some file to the project (this mostly happens with the ASP sites), run the build, don't get this new file, run the build again and i get this new file.
Here is my build script for a VS2003 Web solution as an example
<PropertyGroup>
<TasksPath>D:\BuildTools\</TasksPath>
<VS2003Devenv>D:\Archivos de programa\Microsoft Visual Studio .NET 2003\Common7\IDE\devenv.com</VS2003Devenv>
<VS2003VirtualFolder>CnbvPifWeb</VS2003VirtualFolder>
<VS2003Suba>Cnbv.Pif.Web</VS2003Suba>
<VS2003Project>Cnbv.Pif.Web</VS2003Project>
<VS2003WebSiteName>Sitio Web predeterminado</VS2003WebSiteName>
<VS2003Configuration>Release</VS2003Configuration>
<VS2003Branch>Desarrollo</VS2003Branch>
<VS2003RelativePath>$(SolutionRoot)\$(VS2003Branch)\$(VS2003Suba)\</VS2003RelativePath>
<VS2003SolutionPath>$(VS2003RelativePath)Cnbv.Pif.Web.sln</VS2003SolutionPath>
<VS2003LocalFolder>$(VS2003RelativePath)Sources\$(VS2003Project)\</VS2003LocalFolder>
<VS2003Output>$(BinariesRoot)\$(VS2003Project)\</VS2003Output>
<VS2003CachePath>C:\Documents and Settings\srvfoundation\VSWebCache\230-2555-CPU015\</VS2003CachePath>
<VS2003ProjectExtension>vbproj</VS2003ProjectExtension>
<VS2003CacheFile>$(VS2003CachePath)$(VS2003VirtualFolder)\_vti_pvt\$(VS2003Project).$(VS2003ProjectExtension).cache</VS2003CacheFile>
</PropertyGroup>
<Import Project="$(TasksPath)Microsoft.Sdc.Common.tasks"/>
<UsingTask TaskName="Microsoft.Sdc.Tasks.Web.WebSite.CreateVirtualDirectory" AssemblyFile="Microsoft.Sdc.Tasks.dll" />
<UsingTask TaskName="Microsoft.Sdc.Tasks.Web.WebSite.DeleteVirtualDirectory" AssemblyFile="Microsoft.Sdc.Tasks.dll" />
<ItemGroup>
<!--list of ouput files, excluding .DLL outside bin and some other files-->
<VS2003OutputFiles
Include="$(VS2003LocalFolder)**\*.*"
Exclude="$(VS2003LocalFolder)**\*.vb;$(VS2003LocalFolder)**\*.cs;$(VS2003LocalFolder)**\*.resx;$(VS2003LocalFolder)**\*.vspscc;$(VS2003LocalFolder)**\*.csproj;$(VS2003LocalFolder)**\*.vbproj;$(VS2003LocalFolder)**\*.scc;$(VS2003LocalFolder)**\*.webinfo;$(VS2003LocalFolder)**\*.snk;$(VS2003LocalFolder)**\*.dll;$(VS2003LocalFolder)**\*.exe;" />
<!-- copy dll to bin folder -->
<VS2003OutputBinFiles
Include="$(VS2003LocalFolder)bin\*.dll"/>
</ItemGroup>
<Target Name="AfterCompile">
<Message Text="Deleting cache file" />
<Microsoft.Build.Tasks.Delete
Condition="Exists('$(VS2003CacheFile)')"
Files="$(VS2003CacheFile)" />
<Message Text="Creating virtual folder $(VS2003VirtualFolder) in IIS in local path $(VS2003LocalFolder)" />
<Web.WebSite.CreateVirtualDirectory
VirtualDirectoryName="$(VS2003VirtualFolder)"
Path="$(VS2003LocalFolder)"
WebSiteName="$(VS2003WebSiteName)" />
<Message Text="Compiling $(VS2003Project) in $(VS2003Branch)" />
<Exec
Command=""$(VS2003Devenv)" "$(VS2003SolutionPath)" /build $(VS2003Configuration) /out "$(VS2003LocalFolder)$(VS2003Project).log" "/>
<Message Text="Eliminando la carpeta virtual $(VS2003VirtualFolder) en IIS" />
<Web.WebSite.DeleteVirtualDirectory
WebSiteName="$(VS2003WebSiteName)"
VirtualDirectoryName="$(VS2003VirtualFolder)" />
<MakeDir Condition="!Exists('$(VS2003Output)')" Directories="$(VS2003Output)" />
<Message Text="Copying output files #(VS2003OutputFiles)" />
<Copy
SourceFiles="#(VS2003OutputFiles)"
DestinationFiles="#(VS2003OutputFiles->'$(VS2003Output)%(RecursiveDir)%(Filename)%(Extension)')"
/>
<MakeDir Condition="!Exists('$(VS2003Output)bin\')" Directories="$(VS2003Output)bin\" />
<Message Text="Copying DLL to bin folder #(VS2003OutputBinFiles)" />
<Copy
SourceFiles="#(VS2003OutputBinFiles)"
DestinationFiles="#(VS2003OutputBinFiles->'$(VS2003Output)bin\%(Filename)%(Extension)')"
/>
<OnError ExecuteTargets="VS2003Fail" />
</Target>
<Target Name="VS2003Fail">
<Message Text="Copying log file $(VS2003RelativePath)$(VS2003Project).log" />
<Copy Condition="Exists('$(VS2003RelativePath)$(VS2003Project).log')" SourceFiles="$(VS2003RelativePath)$(VS2003Project).log" DestinationFolder="$(DropLocation)\$(BuildNumber)" />
<CallTarget ContinueOnError ="true" Targets ="CreateWorkItemWhenPartialSucceed" />
</Target>
<Target
Name="CreateWorkItemWhenPartialSucceed"
Condition=" '$(SkipWorkItemCreation)'!='true' and '$(IsDesktopBuild)'!='true' ">
<Message Text="ejecutando work" />
<PropertyGroup>
<WorkItemTitle>$(WorkItemTitle) $(BuildNumber)</WorkItemTitle>
<BuildLogText>$(BuildlogText) <ahref='file:///$(DropLocation)\$(BuildNumber)\BuildLog.txt'>$(DropLocation)\$(BuildNumber)\BuildLog.txt</a >.</BuildLogText>
<ErrorWarningLogText Condition="!Exists('$(MSBuildProjectDirectory)\ErrorsWarningsLog.txt')"></ErrorWarningLogText>
<ErrorWarningLogText Condition="Exists('$(MSBuildProjectDirectory)\ErrorsWarningsLog.txt')">$(ErrorWarningLogText) <a href='file:///$(DropLocation)\$(BuildNumber)\ErrorsWarningsLog.txt'>$(DropLocation)\$(BuildNumber)\ErrorsWarningsLog.txt</a >.</ErrorWarningLogText>
<WorkItemDescription>$(DescriptionText) %3CBR%2F%3E $(BuildlogText) %3CBR%2F%3E $(ErrorWarningLogText)</WorkItemDescription>
</PropertyGroup>
<CreateNewWorkItem
TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
BuildUri="$(BuildUri)"
BuildNumber="$(BuildNumber)"
Description="$(WorkItemDescription)"
TeamProject="$(TeamProject)"
Title="$(WorkItemTitle)"
WorkItemFieldValues="$(WorkItemFieldValues)"
WorkItemType="$(WorkItemType)"
ContinueOnError="true" />
</Target>
When I see the ouput of this message in the log
<Message Text="Copying DLL to bin folder #(VS2003OutputBinFiles)" />
the first time i see just the name of one file, the second time it prints all the correct files, and the same happens with the ASP sites, if I add a file i see the file in the output in the second build.
I hope you can help me out figuring this out, thanks a lot.
Juan Zamudio
this was the answer in the tfs forum by OsirisJakob
The problem is that you define your item groups at the root level. This means that they are evaluated immediately when the project file loaded. What you want is for them to be evaluated when the AfterCompile target is executed.
Since you are running TFS 2008, you can solve this problem by moving the item groups into the AfterCompile target (a.k.a. Dynamic item groups). This will cause the item group to be evaluated by the time the AfterCompile target is executed, and will give you the correct result.