aspnet / MvcPrecompilation

[Archived] Tooling that allows compilation of MVC Razor views as part of build and publish. Project moved to https://github.com/aspnet/AspNetCore
Other
42 stars 15 forks source link

Precompiled views in project and library in 2.0.0 #187

Closed svallis closed 7 years ago

svallis commented 7 years ago

I'm upgrading a project and a library with embedded views to .NET Core 2.0.0 and seem to have lost precompilation. I've simplified the csproj files down to their bare bones, and the docs suggest that I should gain precompilation out of the box:

All the ASP.NET Core 2.x project templates set MvcRazorCompileOnPublish to true.

My first publish after the upgrade has removed the live Project.PrecompiledViews.dll and uploaded all the cshtml files that weren't present on 1.1.1. I've tried manually setting MvcRazorCompileOnPublish to true on both the project and the library, but that doesn't have any impact. I've tried manually removing cshtml files from the publish with <Content Update="**\*.cshtml" CopyToPublishDirectory="Never" /> and republishing, but that leaves the application inoperable.

The csproj for the class library:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup Label="Build">
    <TargetFramework>netcoreapp2.0</TargetFramework>
    <OutputType>Library</OutputType>
    <TypeScriptToolsVersion>2.3</TypeScriptToolsVersion>
  </PropertyGroup>

  <PropertyGroup Label="Assembly">
    <AssemblyName>Library</AssemblyName>
    <PackageId>Library</PackageId>
    <Product>Library</Product>
    <Authors>Test</Authors>
    <Company>Test</Company>
    <Description>Library</Description>
    <Copyright>Copyright © 2016 - 2017 Test</Copyright>
    <Version>1.0.0</Version>
    <AssemblyVersion>1.0.0</AssemblyVersion>
    <FileVersion>1.0.0</FileVersion>
  </PropertyGroup>

  <ItemGroup Label="Embedded Resources">
    <EmbeddedResource Include="Views\**\*.cshtml" />
  </ItemGroup>

  <ItemGroup Label="Package References">
    <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
  </ItemGroup>

</Project>

And the csproj for the project itself:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup Label="Build">
    <TargetFramework>netcoreapp2.0</TargetFramework>
    <TypeScriptToolsVersion>2.3</TypeScriptToolsVersion>
  </PropertyGroup>

  <PropertyGroup Label="Assembly">
    <AssemblyName>Project</AssemblyName>
    <PackageId>Project</PackageId>
    <Product>Project</Product>
    <Authors>Test</Authors>
    <Company>Test</Company>
    <Description>Project</Description>
    <Copyright>Copyright © 2009 - 2017 Test</Copyright>
    <Version>1.0.0</Version>
    <AssemblyVersion>1.0.0</AssemblyVersion>
    <FileVersion>1.0.0</FileVersion>
  </PropertyGroup>

  <ItemGroup Label="Remove From Publish Output">
    <Content Update="Typescript\**\*" CopyToPublishDirectory="Never" />
    <Content Update="**\*.map" CopyToPublishDirectory="Never" />
    <Content Update="**\*.md" CopyToPublishDirectory="Never" />
    <Content Update="**\*.css" CopyToPublishDirectory="Never" />
    <Content Update="**\*.js" CopyToPublishDirectory="Never" />
  </ItemGroup>

  <ItemGroup Label="Add Minified Files Back Into Publish Output">
    <Content Update="**\*.min.css" CopyToPublishDirectory="PreserveNewest" />
    <Content Update="**\*.min.js" CopyToPublishDirectory="PreserveNewest" />
  </ItemGroup>

  <ItemGroup Label="Project References">
    <ProjectReference Include="..\Library\Library.csproj" />
  </ItemGroup>

  <ItemGroup Label="Package References">
  </ItemGroup>

  <ItemGroup Label="Tool References">
    <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
  </ItemGroup>

</Project>

The project is currently hosted on Azure. Have things changed and there is no longer a DLL for the views and the cshtml files should be present on the server? Is there any way to test whether a view being served is precompiled or not? It certainly doesn't feel precompiled at present, with an obvious delay on the first run of each view.

What is the minimum required to get this working? I've looked through some of the testapps in this repo, but there seems to be a lot going on in some of them which I assume shouldn't be necessary when not running tests.

pranavkm commented 7 years ago

The test apps in the repo aren't very good representations of what a real app should look like. I wouldn't recommend using it as a reference. When you publish your primary application (or your class library), do you see a ProjectName.PrecompiledViews.dll in the publish output directory? Running publish on your main application would not run publish or copy the precompiled binary from the class library over to the main application's output directory. You'd need to copy the binaries in as a separate task. Something along the lines of https://github.com/aspnet/MvcPrecompilation/blob/dev/testapps/ApplicationUsingPrecompiledViewClassLibrary/ApplicationUsingPrecompiledViewClassLibrary.csproj#L19

Is there any way to test whether a view being served is precompiled or not?

You could print the precompiled views Mvc discovers:

public string GetPrecompiledResourceNames([FromServices] ApplicationPartManager applicationManager)
{
    var feature = new ViewsFeature();
    applicationManager.PopulateFeature(feature);
    return string.Join(Environment.NewLine, feature.ViewDescriptors.Select(v => v.RelativePath));
}

Alternatively you could include a @GetType().Assembly in your view. Precompiled views should say they are located in {AppName}.PrecompiledViews.

svallis commented 7 years ago

@pranavkm There are no PrecompiledViews.dll files in the publish output for the main application. The first push to Azure after converting to 2.0.0 removed the old PrecompiledViews.dll due to the use of the "Remove additional files at destination" publish option.

This application and the library all began as 1.0.0 projects and have migrated through 1.1.x to 2.0.0 and from project.json to .csproj. I've tried to be thorough with the conversions and removing/modifying things as I go, but I'm assuming there must be something I've missed along the way.

Just to double confirm it, the production version shows things like the following when @GetType().Assembly is executed from a view:

4qrgppfh.3sr, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null

Unfortunately both the library and the project are not open source so I can't share a repo with you, but I'm happy to share any specific information and can investigate anything you can point me in the direction of. Appreciate your help, as as far as I understand it this kind of setup is not a documented scenario yet.

Edit to add that manually setting <MvcRazorCompileOnPublish>true</MvcRazorCompileOnPublish> and publishing doesn't help in my case:

PS C:\Development\Project\src\Project> dotnet publish -c Release -o Publish
Microsoft (R) Build Engine version 15.3.409.57025 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

  Library -> C:\Development\Project\src\Library\bin\Release\netcoreapp2.0\Library.dll
  Project -> C:\Development\Project\src\Project\bin\Release\netcoreapp2.0\Project.dll
  Project -> C:\Development\Project\src\Project\Publish\
PS C:\Development\Project\src\Project>
joergpichler commented 7 years ago

I am having the exact same issue on my machine. With Preview2 everything worked fine but after updating to 2.0 final I don't get any precompiled views, even when I create a new MVC project. edit: I just went back to 2.0-preview2 and running the exact same commands yields the PrecompiledViews.dll, again going back to 2.0 final and no PrecompiledViews.dll is generated.

pranavkm commented 7 years ago

Does your csproj have a RuntimeIdentifier property or are you publishing with rid i.e. is it a self contained application? As a stop gap fix for https://github.com/aspnet/MvcPrecompilation/issues/102 (which is basically any application with a RID), we disable precompilation as part of 2.0.0. In the meantime, https://github.com/aspnet/MvcPrecompilation/issues/102#issuecomment-287653103 suggests a work around to allow precompilation for self contained applications.

svallis commented 7 years ago

@pranavkm No RuntimeIdentifier in the .csproj (the two .csproj files I included in the first post are in full, only the names have been changed). I'm publishing with simply dotnet publish -c Release -o Publish. I'm not doing any cross compilation or anything. Simple web application and class library with the .csproj files as above, and deploying to Azure.

I just made a new MVC web application and published it and that's creating the PrecompiledViews.dll just fine, so whatever is causing it is in my project somewhere; it's not environmental for my whole machine or anything.

pranavkm commented 7 years ago

@svallis that's pretty strange. I was able to generate precompiled views using the templates you had. Would you mind running publish from the cmdline with diagnostic msbuild log and uploading it? dotnet publish /fl /flp:v=Diag or dotnet publish /bl. It should tell what's preventing the target from executing.

svallis commented 7 years ago

@pakrym I have a 108MB log file here. Is there a specific part I can share rather than uploading the whole thing? What's the name of the target we're interested in? The word precompile does not appear in the log at all.

pranavkm commented 7 years ago

MvcRazorPrecompile and _MvcRazorPrecompileOnPublish are the targets of interest here.

svallis commented 7 years ago

Neither string is present in the log file.

svallis commented 7 years ago

I found a reference to <PublishFramework>netcoreapp1.1</PublishFramework> in my .pubxml file, but updating it to netcoreapp2.0 makes no difference. I don't think the .pubxml is even part of command line publishing if I understand correctly, but I'm at a bit of a loss here.

pranavkm commented 7 years ago

Does your application reference Microsoft.AspNetCore.Mvc.Razor.ViewCompilation transitively? If you look at obj/project.assets.json it should include a reference to the package. Additionally the {AppName}.csproj.nuget.g.targets should include a reference to Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.targets. Here's a reference to what you should be seeing:

1>Done building target "_ResolveInputArguments" in project "MusicStore.csproj".: (TargetId:93)
11:16:28.734     1>Target "MvcRazorPrecompile: (TargetId:94)" in file "C:\Users\Pranav\.nuget\packages\microsoft.aspnetcore.mvc.razor.viewcompilation\2.0.0\build\netstandard2.0\Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.targets" from project "D:\work\MusicStore\samples\MusicStore\MusicStore.csproj" (target "_MvcRazorPrecompileOnPublish" depends on it):
                   Building target "MvcRazorPrecompile" completely.
svallis commented 7 years ago

To your first question, yes the obj/project.assets.json file contains a reference: "Microsoft.AspNetCore.Mvc.Razor.ViewCompilation": "2.0.0".

To your second question, no the csproj.nuget.g.targets contains no reference to ViewCompilation.


The {AppName}.csproj.nuget.g.targets in full:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
  </PropertyGroup>
  <ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
    <Import Project="$(NuGetPackageRoot)netstandard.library\2.0.0\build\netstandard2.0\NETStandard.Library.targets" Condition="Exists('$(NuGetPackageRoot)netstandard.library\2.0.0\build\netstandard2.0\NETStandard.Library.targets')" />
    <Import Project="$(NuGetPackageRoot)microsoft.netcore.app\2.0.0\build\netcoreapp2.0\Microsoft.NETCore.App.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.netcore.app\2.0.0\build\netcoreapp2.0\Microsoft.NETCore.App.targets')" />
  </ImportGroup>
</Project>

I just did a "Clean Solution", deleted the bin and obj folders for both the Project and the Library and republished - still no PrecompiledViews.dll.

pranavkm commented 7 years ago

Can you try explicitly referencing the precompilation package? Given that your precompilation runs fine on a different app, it's unlikely the package is corrupted. Maybe it's some sort of issue with restore that you're running in to.

svallis commented 7 years ago

First, the good news. Explicitly referencing the package worked.... the next publish to Azure removed all the .cshtml files and uploaded the PrecompiledViews.dll.

Now, the bad news is most of the pages of the web application are throwing the following System.InvalidOperationException:

Cannot find compilation library location for package 'Microsoft.Win32.Registry'

   at Microsoft.Extensions.DependencyModel.CompilationLibrary.ResolveReferencePaths(ICompilationAssemblyResolver resolver, List`1 assemblies)
   at Microsoft.Extensions.DependencyModel.CompilationLibrary.ResolveReferencePaths()
   at Microsoft.AspNetCore.Mvc.ApplicationParts.AssemblyPart.<>c.<GetReferencePaths>b__8_0(CompilationLibrary library)
   at System.Linq.Enumerable.SelectManySingleSelectorIterator`2.MoveNext()
   at Microsoft.AspNetCore.Mvc.Razor.Compilation.MetadataReferenceFeatureProvider.PopulateFeature(IEnumerable`1 parts, MetadataReferenceFeature feature)
   at Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartManager.PopulateFeature[TFeature](TFeature feature)
   at Microsoft.AspNetCore.Mvc.Razor.Internal.DefaultRazorReferenceManager.GetCompilationReferences()
   at System.Threading.LazyInitializer.EnsureInitializedCore[T](T& target, Boolean& initialized, Object& syncLock, Func`1 valueFactory)
   at Microsoft.AspNetCore.Mvc.Razor.Internal.DefaultRazorReferenceManager.get_CompilationReferences()
   at Microsoft.AspNetCore.Mvc.Razor.Internal.LazyMetadataReferenceFeature.get_References()
   at Microsoft.CodeAnalysis.Razor.CompilationTagHelperFeature.GetDescriptors()
   at Microsoft.AspNetCore.Razor.Language.DefaultRazorTagHelperBinderPhase.ExecuteCore(RazorCodeDocument codeDocument)
   at Microsoft.AspNetCore.Razor.Language.RazorEnginePhaseBase.Execute(RazorCodeDocument codeDocument)
   at Microsoft.AspNetCore.Razor.Language.DefaultRazorEngine.Process(RazorCodeDocument document)
   at Microsoft.AspNetCore.Razor.Language.RazorTemplateEngine.GenerateCode(RazorCodeDocument codeDocument)
   at Microsoft.AspNetCore.Mvc.Razor.Internal.RazorViewCompiler.CompileAndEmit(String relativePath)
   at Microsoft.AspNetCore.Mvc.Razor.Internal.RazorViewCompiler.CreateCacheEntry(String normalizedPath)

Removing the package reference and republishing does not fix the issue. Adding the reference manually obviously resolved the problem permanently; I wonder if a dotnet restore --force --no-cache may have had the same effect?

I'm guessing the above is because I now need to copy the PrecompiledViews from the Library into the output; looking into that now.

pranavkm commented 7 years ago

That error typically shows up when Mvc is trying to runtime compile a view at runtime. This likely indicates that Mvc did not discover a precompiled view for a specific path and there are cshtml files in the publish output that it's attempting to compile. Do you perchance have embedded views or just views on disk that that might be runtime compiled? If so, you'd need to set <MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish> to include the refs directory as part of your publish.

svallis commented 7 years ago

There are views embedded in the Library, so I assume it's because of them? I'd like the Library.PrecompiledViews.dll to also be published so that everything is precompiled, which I assume would also prevent this error? The ultimate goal should be to have no .cshtml files on the server, and no runtime compilation in production environments.

pranavkm commented 7 years ago

See my earlier comment about how to generate a precompiled dll from your "class library":

Running publish on your main application would not run publish or copy the precompiled binary from the class library over to the main application's output directory. You'd need to copy the binaries in as a separate task. Something along the lines of https://github.com/aspnet/MvcPrecompilation/blob/dev/testapps/ApplicationUsingPrecompiledViewClassLibrary/ApplicationUsingPrecompiledViewClassLibrary.csproj#L19.

Once you have the precompiled classlib.dll, you can stop embedding the views in there.

svallis commented 7 years ago

What is required to get the class library to output the .cshtml files as a library? I'm not seeing a precompiled folder within obj for my library.

You mentioned that the testapps have a bunch of other stuff going on as well, so would be good to get a definitive answer on what is minimally required to be added to the original Project,csproj and Library.csproj that I posted in the opening question to achieve a fully precompiled solution.

Again, appreciate the help!


Trying to piece things from here:

https://github.com/aspnet/MvcPrecompilation/blob/dev/testapps/ClassLibraryWithPrecompiledViews/ClassLibraryWithPrecompiledViews.csproj#L6

But I'm getting the following error during publish:

The specified runtimeconfig.json [C:\Development\Project\src\Library\bin\Release\netcoreapp2.0\Library.runtimeconfig.json] does not exist
pranavkm commented 7 years ago

Here's a working sample without the shenanigans from the test application: https://github.com/pranavkm/precompilation-classlib/blob/master/app1/app1.csproj

svallis commented 7 years ago

@pranavkm I'm getting the same error doing a clean clone of your test repo and trying to publish the app as I am on my main web application. The following is straight after git clone:

PS C:\Development\precompilation-classlib\app1> dotnet publish -c Release -o ..\publish
Microsoft (R) Build Engine version 15.3.409.57025 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

  lib1 -> C:\Development\precompilation-classlib\lib1\bin\Release\netcoreapp2.0\lib1.dll
  app1 -> C:\Development\precompilation-classlib\app1\bin\Release\netcoreapp2.0\app1.dll
  app1 -> C:\Development\precompilation-classlib\publish\
  The specified runtimeconfig.json [C:\Development\precompilation-classlib\lib1\bin\Release\netcoreapp2.0\lib1.runtimeconfig.json] does not exist

C:\Users\User\.nuget\packages\microsoft.aspnetcore.mvc.razor.viewcompilation\2.0.0\build\netstandard2.0\Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.targets(60,5): error MSB3073: The command ""C:\Program Files\dotnet\dotnet.exe" exec --runtimeconfig "C:\Development\precompilation-classlib\lib1\bin\Release\netcoreapp2.0\lib1.runtimeconfig.json" --depsfile "C:\Development\precompilation-classlib\lib1\bin\Release\netcoreapp2.0\lib1.deps.json" "C:\Users\User\.nuget\packages\microsoft.aspnetcore.mvc.razor.viewcompilation\2.0.0\build\netstandard2.0\Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.dll" @"obj\Release\netcoreapp2.0\microsoft.aspnetcore.mvc.razor.viewcompilation.rsp"" exited with code -2147450733. [C:\Development\precompilation-classlib\lib1\lib1.csproj]

There is no lib1.PrecompiledViews.dll in the publish output, but there is an app1.PrecompiledViews.dll.


I just tried the same exercise on a different machine and am getting exactly the same error as above. Both machines have VS2017 15.3 and the .NET Core 2.0.0 SDK installed.

pranavkm commented 7 years ago

@svallis I must've had extra files around from when I was building it which caused it to work locally. The ClassLibrary has to be a runnable application for precompilation to work: https://github.com/pranavkm/precompilation-classlib/commit/38b48769b625e878d85592435f2a0faac911292c.

svallis commented 7 years ago

Your repo now clones and builds correctly, looks spot on.

Getting it working with the main project is proving problematic though. I've triple checked everything and can't see what's wrong with my setup. I've pared things down to just what you have in your test repo, but there is still some weirdness requiring me to manually specify <PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.ViewCompilation" Version="2.0.0" /> to get razor compilation to work at all, and even when it works for Project it's still not working for Library:

PS C:\Development\Project\src\Project> dotnet publish -c Release -o C:\Development\publish
Microsoft (R) Build Engine version 15.3.409.57025 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

  Library -> C:\Development\Project\src\Library\bin\Release\netcoreapp2.0\Library.dll
  Project -> C:\Development\Project\src\Project\bin\Release\netcoreapp2.0\Project.dll
  Project -> C:\Development\publish\
  The given path's format is not supported.
     at System.IO.Path.GetFullPath(String path)
     at System.IO.Directory.CreateDirectory(String path)
     at Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.Internal.PrecompileRunCommand.EmitAssembly(CSharpCompilation compilation, EmitOptions emitOptions, String assemblyPath, ResourceDescription[] resources)
     at Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.Internal.PrecompileRunCommand.Execute()
     at Microsoft.Extensions.CommandLineUtils.CommandLineApplication.Execute(String[] args)
     at Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.Internal.PrecompilationApplication.Execute(String[] args)
C:\Users\User\.nuget\packages\microsoft.aspnetcore.mvc.razor.viewcompilation\2.0.0\build\netstandard2.0\Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.targets(60,5): error MSB3073: The command ""C:\Program Files\dotnet\dotnet.exe" exec --runtimeconfig "C:\Development\Project\src\Library\bin\Release\netcoreapp2.0\Library.runtimeconfig.json" --depsfile "C:\Development\Project\src\Library\bin\Release\netcoreapp2.0\Library.deps.json" "C:\Users\User\.nuget\packages\microsoft.aspnetcore.mvc.razor.viewcompilation\2.0.0\build\netstandard2.0\Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.dll" @"obj\Release\netcoreapp2.0\microsoft.aspnetcore.mvc.razor.viewcompilation.rsp"" exited with code 1. [C:\Development\Project\src\Library\Library.csproj]
pranavkm commented 7 years ago

Can you include what the --output-path option in your Library\obj\Release\netcoreapp2.0\microsoft.aspnetcore.mvc.razor.viewcompilation.rsp looks like? Might be some oddity with the path passed to it.

svallis commented 7 years ago

You got it, looks like absolute paths aren't supported there.

--output-path=C:\Development\Project\src\Project\C:\Development\publish\
pranavkm commented 7 years ago

Ahh, you'd have to tweak MvcRazorOutputPath to resolve the directory rather than concatenating it from the project directory. It works with relative paths, not so much from regular paths: https://github.com/pranavkm/precompilation-classlib/commit/7d5cf7e0f5d91035cd0042c37c6b4e7ccaac7294#diff-efdb7a7d1306ffecb63ecb38a39eb159R20

svallis commented 7 years ago

I switched to relative paths, solved the error above. Visual Studio however seems to use absolute paths as well, so using your fix above got things rolling in VS again.

Current status is that both dotnet publish -c Release -o and publishing from within VS makes a Library.PrecompiledViews but no Project.PrecompiledViews. It also copies all the libraries .cshtml files into the output folder, but none of the views from the Project.

I'm starting to feel like I'm going a little crazy, it's now doing the opposite of the problem I started with. The library is getting both its .cshtml files and the precompiled output published, and the main web application is getting neither.

Edit: Your test repo is also publishing some of the .cshtml sources, presumably the library's. Do these need to be manually removed from the publish output?

Edit 2: Just tested the above and your test repo acts exactly as I expect. Adding <Content Update="**\*.cshtml" CopyToPublishDirectory="Never" /> to lib1.csproj results in no Views folder in the publish output, but two PrecompiledViews.dll. Same problems persist with my main application.

svallis commented 7 years ago

I figured the easiest way to get to the bottom of this is to work publicly on a fork of your test application. I've removed most of the web application stuff from lib1 to make it more libraryish, and got it to the point where things work in development.

https://github.com/svallis/precompilation-classlib/commits/master

Running this with CTRL+F5 in VS gives the following output in the footer:

app1 Assembly: 5jdwzips.igw, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
lib1 Assembly: cnf4ikdx.i22, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null

So far so good.

Publishing this from the command line looks good - the output folder contains two PrecompiledViews.dll files and no Views directory. Doing a dotnet app1.dll in the publish output directory and making a request however throws the following error:

System.InvalidOperationException: The following precompiled view paths differ only in case, which is not supported:
/Views/Shared/_LibPartial.cshtml
/Views/Shared/_LibPartial.cshtml

_LibPartial.cshtml only exists once in the filesystem, so I'm not sure why it's being seen twice after publish. My suspicion was that the <EmbeddedResource Include="**\*.cshtml" /> was causing a dual inclusion with the precompilation, but removing that line breaks things in development and still doesn't fix the issue when running the published application.

I've tried to break the commits up into logical steps through the process so hopefully you can see if I've made any wrong turns.

pranavkm commented 7 years ago

Removing the AddApplicationPart(lib1) should fix the issue. Mvc doesn't de-duplicate assembly part registrations which explains why the view was discovered multiple times: once via the project-to-project reference and once via the explicitly registered value. I'll file a bug to track in Mvc to track if we can make the experience here better.

svallis commented 7 years ago

@pranavkm Ah I see, that makes a lot of sense. The AddApplicationPart(lib1) stuff has been in my project since the project.json days.

I've updated the test repo and things are looking good:

app1 Assembly: app1.PrecompiledViews, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
lib1 Assembly: lib1.PrecompiledViews, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

I've also added some quick test code to make sure using an embedded EditorTemplate still functions correctly, and that does seem to be the case both in development and post-publish.

The only one issue left in the test repo that I can see is that there is no TagHelper intellisense. This applies to .cshtml files in both app1 and lib1. The TagHelper I used does function correctly (asp-for on an <input>), there's just no editor intellisense. I have the same problem in my main project, so I'm wondering if they are related (I haven't done any searching on this issue though, so it's entirely possible that it's not related to the above). This can be ignored, things just started working again after a VS restart.

Thanks again for all your help on this @pranavkm - I appreciate it!


Edit: I have taken the above and returned to my main project and library and am happy to say everything is now deployed to Azure with no .cshtml files and two PrecompiledView.dll files. The part that makes least sense to me, but that made all the difference, was adding a reference to Microsoft.AspNetCore.All to the Project as well as the Library.

It was my understanding that this should not be required, as the Library package references are inherited by the Project. I was hoping to leverage this to effectively make the Library dictate the version of .NET Core that the Project uses. I'm not sure if this is a bug or not, but it feels wrong to me.

Still, all is working well at the moment and I'm very pleased to have everything finally precompiled in production. Thanks again!

davidfowl commented 7 years ago

I was hoping to leverage this to effectively make the Library dictate the version of .NET Core that the Project uses. I'm not sure if this is a bug or not, but it feels wrong to me.

Did you mean ASP.NET Core and not .NET Core?

svallis commented 7 years ago

Did you mean ASP.NET Core and not .NET Core?

Yeah exactly; if the library gets bumped to say 2.0.x, I think it would be nice for the project to naturally follow it (if you don't specify something more specific in the Project.csproj).

pranavkm commented 7 years ago

@svallis can you start a separate issue for the other work item? References aren't something the tool influences and we've tackled a couple of independent issues in this work item already.

svallis commented 7 years ago

Sure @pranavkm - what's the most appropriate repo for .csproj reference issues?

pranavkm commented 7 years ago

https://github.com/Nuget/Home for restore and package resolution, https://github.com/dotnet/sdk for any SDK specific interactions.

hiyelbaz commented 7 years ago

Can you please clarify what is the simplest workaround in a VS2017 solution.

pranavkm commented 7 years ago

@hiyelbaz I'm not entirely sure what the issue you're running in to is. Could you start a new issue?

pranavkm commented 7 years ago

@svallis closing this since we've figured out a solution to your issue.

PeterHagen commented 7 years ago

I have been trying out this solution to get my project working with precompiling. And I'm starting to need it, cause the compiling in production is taking too much memory and time.

I have multiple 'base' projects with views, that are referenced in my project. I included the target _RunPrecompileForClassLib and that works. But in runtime I get the exception about the multiple viewimports. Then I checked the code of @svallis, and I noticed that he removed the _ViewImports and _ViewStart files from the 'library' projects. This way the file should not be double available. But this can't be a solution to the problem. I can't compile my projects without the _ViewImports, due to missing namespaces etc..

How can it be, then when I use the embedded view solution, it works fine with the multiple _viewImport files, but with precompiling its an issue. Any solution for this?

Edit: When I add a _viewimport to the lib project of @svallis, I'll get the same issues:

fail: Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[0]
      An unhandled exception has occurred: The following precompiled view paths differ only in case, which is not supported:
      /Views/_ViewImports.cshtml
      /Views/_ViewImports.cshtml

Edit: For those how struggle with this same issue, Dean North wrote a really interesting article about Self-Contained UI: Running One ASP.NET Core MVC Site Inside Another

evil-shrike commented 7 years ago

Hi. There were lots of discussions on the topic but I wonder as at the state of core2.0 what is the official solution for embedding views and razor pages in class libraries? Is it documented anywhere? Should we still use csproj hacking (_MvcRazorPrecompileOnBuild)?

pranavkm commented 7 years ago

@PeterHagen see https://github.com/aspnet/MvcPrecompilation/issues/174 for your _ViewImports issue.

@evil-shrike I'm not entirely sure what you're referring to when you say

Should we still use csproj hacking (_MvcRazorPrecompileOnBuild)?

That said, I'd recommened filing a new discussion issue. This thread is long as it is and we resolved it as some form of packaging issue, so your question doesn't seem to apply here

evil-shrike commented 7 years ago

@pranavkm I meant that to enable precompilation of razor views on build in a class library we have to add this into csproj (as described in the post referred by @PeterHagen):

<Target Name="_MvcRazorPrecompileOnBuild"
        DependsOnTargets="SetMvcRazorOutputPath;MvcRazorPrecompile"
        AfterTargets="Build" 
        Condition=" '$(IsCrossTargetingBuild)' != 'true' " />
    <Target Name="IncludePrecompiledViewsInPublishOutput"
        DependsOnTargets="_MvcRazorPrecompileOnBuild"
        BeforeTargets="PrepareForPublish"
        Condition=" '$(IsCrossTargetingBuild)' != 'true' ">
    <ItemGroup>
    <_PrecompiledViewsOutput Include="$(MvcRazorOutputPath)$(MSBuildProjectName).PrecompiledViews.DLL" />
    <_PrecompiledViewsOutput Include="$(MvcRazorOutputPath)$(MSBuildProjectName).PrecompiledViews.pdb" />
    <ContentWithTargetPath Include="@(_PrecompiledViewsOutput->'%(FullPath)')"
        RelativePath="%(_PrecompiledViewsOutput.Identity)" 
        TargetPath="%(_PrecompiledViewsOutput.Filename)%(_PrecompiledViewsOutput.Extension)" 
        CopyToPublishDirectory="PreserveNewest" />
    </ItemGroup>
</Target>

Is it still the only way to go?

pranavkm commented 7 years ago

@evil-shrike yeah. We haven't worked on any features to make the class library scenario easier. Feel free to open a new feature request issue for this.

svallis commented 7 years ago

I've taken the liberty of opening a new ticket to suggest that class library view precompilation should be the default - see #213.