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

Issue

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

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.

Answered By – Elroy Flynn

Answer Checked By – David Goodson (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *