'Are there any alternatives to aspnet_compiler when it comes to build the Asp.Net views?

Consider a trivial Asp.Net web application - https://github.com/MarkKharitonov/TinyWebApp

It features two tiny projects:

  • TinyWebApp - a tiny Asp.Net application with a single aspx page outputing Hello World!
  • Utility.TinyWebApp - a utility project that runs aspnet_compiler to build the views.

Building the code from the command line:

C:\work\TinyWebApp [master ≡]> msbuild /v:m /m
Microsoft (R) Build Engine version 17.0.0+c9eb9dd64 for .NET Framework
Copyright (C) Microsoft Corporation. All rights reserved.

CSC : warning CS2008: No source files specified. [C:\work\TinyWebApp\TinyWebApp\TinyWebApp.csproj]
  TinyWebApp -> C:\work\TinyWebApp\bin\TinyWebApp.dll
  Running AspNetCompiler for C:\work\TinyWebApp\bin\_PublishedWebsites\TinyWebApp
C:\work\TinyWebApp [master ≡]>

Currently the utility project uses the AspNetCompiler msbuild task to build the views, which internally calls aspnet_compiler.exe. In this example it runs very fast, but in general it is very slow.

In the past I asked a question on how to speed it up (How to speed up aspnet_compiler.exe?), but I failed to provide a concrete example and hence I was unable to get a concrete answer. The accepted answer seems to show the way, but I failed to make a working code out of it.

This time I am providing a concrete, tiny and working Asp.Net web application (not Asp.Net Core). My question is - how to replace the aspnet_compiler with something else (Roslyn?) to precompile the views?


The code: (https://github.com/MarkKharitonov/TinyWebApp)

C:\work\TinyWebApp [master ≡]> tree /f
Folder PATH listing for volume OSDisk
Volume serial number is F6C4-7BEF
C:.
│   .gitignore
│   Directory.Build.props
│   TinyWebApp.sln
│
├───TinyWebApp
│       index.aspx
│       TinyWebApp.csproj
│       Web.config
│
└───Utility.TinyWebApp
        Mvc.Targets
        Utility.TinyWebApp.proj

C:\work\TinyWebApp [master ≡]> dir -r -file |% { "`r`n========`r`n$($_.FullName)`r`n========`r`n" ; cat $_.FullName }

========
C:\work\TinyWebApp\.gitignore
========

.vs*
bin
obj
*.user
*.binlog

========
C:\work\TinyWebApp\Directory.Build.props
========

<Project>
  <PropertyGroup>
    <WorkspaceRoot>$(MSBuildThisFileDirectory)</WorkspaceRoot>
    <OutDir Condition="'$(OutDir)' != ''" >$([MSBuild]::EnsureTrailingSlash($(OutDir)))</OutDir>
    <OutDir Condition="'$(OutDir)' == ''">$(WorkspaceRoot)bin\</OutDir>
    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
  </PropertyGroup>
</Project>

========
C:\work\TinyWebApp\TinyWebApp.sln
========


Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.32014.148
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TinyWebApp", "TinyWebApp\TinyWebApp.csproj", "{A3D07730-5AEC-4A07-98AF-5C932BADD329}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Utility.TinyWebApp", "Utility.TinyWebApp\Utility.TinyWebApp.proj", "{72731699-9885-4A09-A180-87494677192D}"
EndProject
Global
        GlobalSection(SolutionConfigurationPlatforms) = preSolution
                Debug|Any CPU = Debug|Any CPU
                Release|Any CPU = Release|Any CPU
        EndGlobalSection
        GlobalSection(ProjectConfigurationPlatforms) = postSolution
                {A3D07730-5AEC-4A07-98AF-5C932BADD329}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                {A3D07730-5AEC-4A07-98AF-5C932BADD329}.Debug|Any CPU.Build.0 = Debug|Any CPU
                {A3D07730-5AEC-4A07-98AF-5C932BADD329}.Release|Any CPU.ActiveCfg = Release|Any CPU
                {A3D07730-5AEC-4A07-98AF-5C932BADD329}.Release|Any CPU.Build.0 = Release|Any CPU
                {72731699-9885-4A09-A180-87494677192D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                {72731699-9885-4A09-A180-87494677192D}.Debug|Any CPU.Build.0 = Debug|Any CPU
                {72731699-9885-4A09-A180-87494677192D}.Release|Any CPU.ActiveCfg = Release|Any CPU
                {72731699-9885-4A09-A180-87494677192D}.Release|Any CPU.Build.0 = Release|Any CPU
        EndGlobalSection
        GlobalSection(SolutionProperties) = preSolution
                HideSolutionNode = FALSE
        EndGlobalSection
        GlobalSection(ExtensibilityGlobals) = postSolution
                SolutionGuid = {AAED32B9-8739-4AE7-B747-9511E31839CD}
        EndGlobalSection
EndGlobal

========
C:\work\TinyWebApp\TinyWebApp\index.aspx
========

<%@ Page Language="C#" AutoEventWireup="false" ValidateRequest="false" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title>Hello World!</title>
    </head>
    <body>
        <div">
            <%=HelloWorld() %>
        </div>
        <br/>
    </body>
</html>
<script runat="server">
    private string HelloWorld()
    {
        return "Hello World!";
    }
</script>

========
C:\work\TinyWebApp\TinyWebApp\TinyWebApp.csproj
========

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{A3D07730-5AEC-4A07-98AF-5C932BADD329}</ProjectGuid>
    <ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>TinyWebApp</RootNamespace>
    <AssemblyName>TinyWebApp</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <UseIISExpress>true</UseIISExpress>
    <Use64BitIISExpress />
    <IISExpressSSLPort />
    <IISExpressAnonymousAuthentication>enabled</IISExpressAnonymousAuthentication>
    <IISExpressWindowsAuthentication>disabled</IISExpressWindowsAuthentication>
    <IISExpressUseClassicPipelineMode>false</IISExpressUseClassicPipelineMode>
    <UseGlobalApplicationHostFile />
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <Optimize>false</Optimize>
    <OutputPath>Bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <Optimize>true</Optimize>
    <OutputPath>Bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System" />
  </ItemGroup>
  <ItemGroup>
    <Content Include="index.aspx" />
    <Content Include="Web.config">
      <SubType>Designer</SubType>
    </Content>
  </ItemGroup>
  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
  <Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSTestNoBuild)' != true" />
  <ProjectExtensions>
    <VisualStudio>
      <FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
        <WebProjectProperties>
          <UseIIS>True</UseIIS>
          <AutoAssignPort>False</AutoAssignPort>
          <DevelopmentServerPort>57587</DevelopmentServerPort>
          <DevelopmentServerVPath>/</DevelopmentServerVPath>
          <IISUrl>http://localhost:57587</IISUrl>
          <NTLMAuthentication>False</NTLMAuthentication>
          <UseCustomServer>False</UseCustomServer>
          <CustomServerUrl>
          </CustomServerUrl>
          <SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
        </WebProjectProperties>
      </FlavorProperties>
    </VisualStudio>
  </ProjectExtensions>
</Project>

========
C:\work\TinyWebApp\TinyWebApp\Web.config
========

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.web>
    <httpRuntime targetFramework="4.5" />
    <compilation debug="true" targetFramework="4.7.2" />
  </system.web>
  <system.webServer>
    <defaultDocument>
      <files>
        <clear />
        <add value="index.aspx" />
      </files>
    </defaultDocument>
  </system.webServer>
</configuration>

========
C:\work\TinyWebApp\Utility.TinyWebApp\Mvc.Targets
========

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <OutputPath>bin</OutputPath>
    <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
    <MasterDllPathPrefix>$(MasterProject)\..\obj\$(Configuration)\$(MasterAsmName)</MasterDllPathPrefix>
    <MvcBuildViewsOutput>$(MasterDllPathPrefix).MvcBuildViews</MvcBuildViewsOutput>
  </PropertyGroup>

  <!-- These two are expected by Visual Studio. Not needed when building with msbuild on the console. -->
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'" />
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'" />

  <ItemGroup>
    <ProjectReference Include="$(MasterProject)" />

    <MvcBuildViewsInput Include="$(MasterProject)\..\**\*.aspx" />
    <MvcBuildViewsInput Include="$(MasterProject)\..\**\*.cshtml" />
    <MvcBuildViewsInput Include="$(MasterDllPathPrefix).dll" />
    <MvcBuildViewsInput Include="$(MasterProject)\..\web.config" />
    <MvcBuildViewsInput Include="$(MSBuildThisFileFullPath)" />
  </ItemGroup>

  <Import Project="$(MSBuildBinPath)\Microsoft.Common.CurrentVersion.targets" />

  <!-- This target is expected by Visual Studio. Not needed when building with msbuild on the console. -->
  <Target Name="CreateManifestResourceNames" />

  <Target Name="Build"
          DependsOnTargets="ResolveProjectReferences"
          Condition="'$(MasterProject)' != '' And '$(MasterAsmName)' != ''"
          Inputs="@(MvcBuildViewsInput)"
          Outputs="$(MvcBuildViewsOutput)">
    <PropertyGroup>
      <WebProjectOutputDir>$(OutDir)_PublishedWebsites\%(ProjectReference.Filename)</WebProjectOutputDir>
    </PropertyGroup>
    <Message Text="Running AspNetCompiler for $(WebProjectOutputDir)" Importance="High" />
    <AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" />
    <WriteLinesToFile File="$(MvcBuildViewsOutput)" Lines="@(MvcBuildViewsInput)" Overwrite="True"/>
  </Target>
</Project>

========
C:\work\TinyWebApp\Utility.TinyWebApp\Utility.TinyWebApp.proj
========

<Project ToolsVersion="Current">
  <PropertyGroup>
    <MasterProject>..\TinyWebApp\TinyWebApp.csproj</MasterProject>
    <MasterAsmName>TinyWebApp</MasterAsmName>
  </PropertyGroup>
  <Import Project="$(MSBuildThisFileDirectory)Mvc.targets" />
</Project>
C:\work\TinyWebApp [master ≡]>


Solution 1:[1]

take a look at https://stackoverflow.blog/2015/07/23/announcing-stackexchange-precompilation/

That blog post is old, but the github source is, hmm, less old.

Anyway, it directly addresses your concern.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Elroy Flynn