Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Embedded cshtml can't be found in ASP.NET Core MVC 3.0 #15315

Closed
Nefcanto opened this issue Oct 23, 2019 · 15 comments
Closed

Embedded cshtml can't be found in ASP.NET Core MVC 3.0 #15315

Nefcanto opened this issue Oct 23, 2019 · 15 comments
Assignees
Labels
area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. question
Milestone

Comments

@Nefcanto
Copy link

Nefcanto commented Oct 23, 2019

I have a library in which MVC Razor views are defined. For example \Views\Default\Index.cshtml. And we use this configuration in .csproj to reuse those views across projects:

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

Now when we compile our library DLL, we can make sure that our chtml razor views are actually embedded inside the DLL file, inside Resources folder, using ILSpy.

The we use this library inside an ASP.NET Core MVC Web Application, to reuse the .cshtml files.

We use this line of code to register that library as a place for searching views:

services.Configure<MvcRazorRuntimeCompilationOptions>(options =>
{
	foreach (var assembly in assemblies)
	{
		options.FileProviders.Add(new EmbeddedFileProvider(assembly));
	}
});

This would work in ASP.NET Core MVC 2.2. But after upgrading to version 3.0, it's now complaining that:

The view 'Index' was not found. The following locations were searched:
/Views/Default/Index.cshtml
/Views/Shared/Index.cshtml
/Pages/Shared/Index.cshtml

What's wrong here?

@pranavkm
Copy link
Contributor

@Nefcanto have you configured MVC to use runtime compilation by calling AddRazorRuntimeCompilation? I ask because you would typically configure MvcRazorRuntimeCompilationOptions as part of it:

   .AddRazorRuntimeCompilationOptions(options => ...);

Out of curiosity, is there a reason you're unable to use build-time compiled views?

@pranavkm pranavkm added the area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates label Oct 23, 2019
@javiercn
Copy link
Member

@Nefcanto Thanks for contacting us.

The recommended approach is to use Razor Class Libraries instead of embedding the views in the assemlby. See for details https://docs.microsoft.com/en-us/aspnet/core/razor-pages/ui-class?view=aspnetcore-3.0&tabs=visual-studio

@Nefcanto
Copy link
Author

@pranavkm I also prefer build-time. But for a lot of breaking changes that we encountered, we had to compromise in order to meet the market pressure and deadline. We simply used RuntimeCompilation because of another error and it solved it. I know it's spaghetti, but it's business.

@Nefcanto
Copy link
Author

@javiercn your samples are too complicated, Too many technologies there. Razor, Blazor, Pages, Components, etc. and you keep adding new stuff. We can't keep up to date in business with all of your innovative ideas. And the point is your docs are out of date now I guess. But I'll give it a try because we're stuck now. Thanks for your guidance.

@Nefcanto
Copy link
Author

@javiercn but for the older approach, can you give me some advice on what should I do to make our code work?

@javiercn
Copy link
Member

@Nefcanto have you configured MVC to use runtime compilation by calling AddRazorRuntimeCompilation? I ask because you would typically configure MvcRazorRuntimeCompilationOptions as part of it:

   .AddRazorRuntimeCompilationOptions(options => ...);

Does the original suggestion from @pranavkm does not work?

@javiercn javiercn added the Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. label Oct 23, 2019
@Nefcanto
Copy link
Author

Based on this issue, I had to replace RazorViewEngineOptions with MvcRazorRuntimeCompilationOptions which is located inside Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation NuGet package.

Based on @pranavkm comment, we realized that we don't need run-time compilation of Razor views at all. We need build-time compilation and that suffices for us.

The suggestion of @javiercn (RCL) actually has a completely different layout than the old MVC layout in which Razor views were located in Views folder, separated for each controller in a folder named exactly like the controller by convention, and named after action method names. If we follow @javiercn's suggestion, then we incur a huge amount of work over our web front-end. Because we have more than 2000 .cshtml files.

@pranavkm's suggestion worked. By adding AddRazorRuntimeCompilation to MVC configuration it worked.

Now I have two questions:

  1. How to configure old embedded views approach in version 3.0 without using run-time compilation, without adding a reference to Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation NuGet package?

  2. How to register assemblies as file providers for searching for controllers in API projects that do not have views?

@javiercn
Copy link
Member

The suggestion of @javiercn (RCL) actually has a completely different layout than the old MVC layout in which Razor views were located in Views folder, separated for each controller in a folder named exactly like the controller by convention, and named after action method names. If we follow @javiercn's suggestion, then we incur a huge amount of work over our web front-end. Because we have more than 2000 .cshtml files.

You can still keep this layout, razor class libraries work for razor pages and MVC views. Am I missing something?

@Nefcanto
Copy link
Author

So, here's what I did:

  • I added a framework reference: <FrameworkReference Include="Microsoft.AspNetCore.App" /> to my ProjectAspNetCore.targets file that is the base of all of my MVC projects, Web or API.

  • I added these properties to my ProjectWeb.targets which is the base of all of my web projects, but not API projects:

  <PropertyGroup>
    <MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish>
    <CopyRefAssembliesToPublishDirectory>true</CopyRefAssembliesToPublishDirectory>
    <RazorLangVersion>3.0</RazorLangVersion>
    <AddRazorSupportForMvc>true</AddRazorSupportForMvc>
  </PropertyGroup>

To find out correct configuration, I created a project from Razor Class Library template, I checked Support for pages and views, and I looked inside the .csproj file to see what do I need for MVC Razor Views. I also used tips of this page.

  • I changed SDK of my library projects from Sdk="Microsoft.NET.Sdk" to Sdk="Microsoft.NET.Sdk.Razor" and built them again.

  • I reused the output DLL files of these libraries inside my main web project (my projects are divided into web and API. I'm talking about web which only contains CSHTML and not data)

Now I'm stuck at configuring my Startup.cs. I need to add file providers for my DLL that includes my .cshtml files. And I don't want to include runtime compilation. Because I only need build-time compilation. This line of code doesn't work:

services.Configure<RazorViewEngineOptions>(options =>
            {
                foreach (var viewAssembly in AssembliesToSearchForViews)
                {
                    options.FileProviders.Add(new EmbeddedFileProvider(viewAssembly));
                }
            });

I can add a package reference to Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation and configure it using AddRazorRuntimeCompilation. But I don't want it, because it litters the output directory with a lot of unwanted resource files for a transitive dependency of C# Code Analysis.

So, I'm stuck at this point. Can you help?

I can send you log files of MSBuild in case of necessity.

@Nefcanto
Copy link
Author

I've also removed the <EmbeddedResource Include="Views\**\*.cshtml" /> property. Because as I read, Razor SDK target files include all .cshtml files in the assembly as content.

@javiercn
Copy link
Member

@Nefcanto The views should be found automatically in your web app project if it's referencing the class libraries. You can check the generated assembly from the class library and you'll see that it contains a bunch of assembly metadata attributes that specify all the views in the assembly, if those are not present, then something is not going according to plan.

Also on your main web application you can check the list of application parts to see if your views are there. If any of those things is not present then there is something on your setup causing issues.

  • The views are not being correctly compiled.
  • The views are not being correctly discovered.

@Nefcanto
Copy link
Author

How to check the assembly metadata attributes? I'm using ILSpy. In previous builds, I could see that my .cshtml files are inside the assembly as embedded resources. But I don't know where to look for metadata attributes.

image

@javiercn
Copy link
Member

javiercn commented Oct 25, 2019

Here is a sample with all the views inside a class library.
sample.zip
Here are the assembly attributes in the library assembly
image
Here are the assembly attributes in the library companion assembly
image
Here is the assembly attribute in the host app pointing to the library for discovering views and other MVC primitives
image

@mkArtakMSFT mkArtakMSFT added this to the Discussions milestone Oct 28, 2019
@lvmajor
Copy link

lvmajor commented Nov 4, 2019

@javiercn would you have an idea of what could cause the dll of the host app to not display the ApplicationPart like in your last picture?
I'm trying to figure out what happens with my app, I'm migrating from 2.2 to 3.0, but now the RCL I'm linking to don't seem to be correctly found/populated for the ViewComponents.

The weirdest part though is that if I clean the solution, rebuild and restart, then it works 50% of the time, that's mesmerizing me...

@lvmajor
Copy link

lvmajor commented Nov 5, 2019

Okay, seems like the bug was related to Resharper Ultimate as after updating it, the assembly started containing the said metadata, and the ViewComponent started being available 100% of the time.

@javiercn javiercn closed this as completed Nov 5, 2019
@ghost ghost locked as resolved and limited conversation to collaborators Dec 5, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. question
Projects
None yet
Development

No branches or pull requests

5 participants