I'm using Razor with emails templates in .NET core 3.
While I can get this all to works, I'd like to change my templates file (called EmailLayout.cshtml and located at /Pages/Shared/) as an Embedded resource.
Whenever I do that in VS, I got this error:
InvalidOperationException: The layout view 'EmailLayout' could not be
located. The following locations were searched:
/Views/EmailLayout.cshtml /Views/Shared/EmailLayout.cshtml
/Pages/Shared/EmailLayout.cshtml
Why my file couldn't be found?
What should I add to get this to work?
Finally found the answer.
1) Install Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation nuget package
2) Add this to configuration in Startup :
services.Configure<MvcRazorRuntimeCompilationOptions>(options => {
options.FileProviders.Clear();
options.FileProviders.Add(new EmbeddedFileProvider(appDirectory));
});
3) Change the service to services.AddRazorPages().AddRazorRuntimeCompilation();
Make sure that the Build Action of the file is set to Content in the properties of the View.
You could follow the steps to use EmailLayout.cshtml:
1.Create Layout View:
2.Be sure it is in /Pages/Shared folder:
3.EmailLayout should be like:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
</head>
<body>
<h2>EmailLayout</h2>
<div>
#RenderBody()
</div>
</body>
</html>
4.Add the following code to your Razor Pages or _ViewStart.cshtml:
#{
Layout = "EmailLayout";
}
Related
I have a c# .NET 6 app. Some of the web pages (razor pages) of the app are using dependency injection, injecting configuration into the razor pages (the .cshtml files) so that some config elements can be displayed in the UI.
My question is, is it possible to inject configuration in a similar way to a .css file? Is it possible to have something similar to this in a .css file:
/**mycssfile.css**/
#using Microsoft.Extensions.Configuration
#inject IConfiguration Configuration
.mystyle {
background-image: Configuration.image;
}
You might be looking for the #import keyword, if you are just looking to bring in rules from other style sheets.
The #import CSS at-rule is used to import style rules from other valid stylesheets. An #import rule must be defined at the top of stylesheet, before any other at-rule (except #charset and #layer) and style declaration, else it will be ignored.
https://developer.mozilla.org/en-US/docs/Web/CSS/#import
However, if you are trying to bring in values, strings, or any other kind of data from C# (which you appear to want in your example), you’re going to have to dynamically build the CSS file content in an endpoint on your server (where you have access to the C# values), then send that down to the browser where it acts like just another style sheet.
The issue is, once the browser parses CSS, it does not have access to your server’s code, because it’s already been downloaded.
Although, the functionality I just described may already be available to use as a built-in feature of the .NET framework, otherwise, you’ll have to code it yourself in .NET.
CSS files are plain text and are not processed by ASP.NET Core. Consequently, it is not possible to execute server-side code within them. However, you can easily use a Razor page to generate your CSS file, which will support DI etc (although you likely lose any CSS support offered by your IDE).
Here's the PageModel for an example. Note that the ContentType for the Response has been altered:
public class CssModel : PageModel
{
public string TextColor => "green";
public void OnGet()
{
Response.ContentType= "text/css; charset=utf-8";
}
}
Here's the .cshtml file content. The Layout has been set to null:
#page
#model WebApplication3.Pages.CssModel
#{
Layout = null;
}
body{
font-family:consolas;
color: #Model.TextColor;
}
Here's an example of a page that uses the generated CSS. Note here that the script is being output in a section:
#page
#model CssTestModel
#{
}
<h1>Hello World!</h1>
<p>This page tests Razor generated CSS</p>
#section styles{
<link rel="stylesheet" href="/css" />
}
And here's the layout page's head section that includes the necessary RenderSection call:
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>#ViewData["Title"]</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
#RenderSection("styles", false)
</head>
This is a new MVC5 project, created from the blank ASP.NET template.
Here's my bundle registration:
Imports System.Web.Optimization
Public Class BundleConfig
Public Shared Sub RegisterBundles(Bundles As BundleCollection)
Bundles.Add(New StyleBundle("~/styles/main").Include("~/Styles/Layout.css"))
End Sub
End Class
...here's the relevant section of my view:
#Code
Me.Layout = Nothing
End Code
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
#Styles.Render("~/styles/main")
</head>
... and here's the HTML output:
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
<link href="/styles/main" rel="stylesheet"/>
</head>
Note that the link's href should instead be:
<link href="/Styles/Layout.css" rel="stylesheet"/>
All of this of course results in a 404 error.
Things I've tried:
Followed the general guidance from the official documentation
Added a CssRewriteUrlTransform object, as indicated here
Both enabled and disabled Optimization, as indicated here
Updated the WebGrease package to the latest version
Checked Web.config for an umbracoReservedPaths key, as indicated here (none exists)
Added Bundles.IgnoreList.Clear() as indicated here
This exact construct is working correctly in another project, so obviously something's different. But I'm unable to spot it.
How can I get this bundle to render properly?
How can I include _Layout.cshtml in Razor Content Page ?
For example I created two cshtml files in root of my project.
First file is _Layout.cshtml
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<h1>Main Layout</h1>
<br>
<br>
#RenderBody();
</body>
</html>
Second File is Product.cshtml
#inherits ServiceStack.Razor.ViewPage
#{
Layout = "~/_Layout.cshtml";
}
<h1>Product Page</h1>
When I call http://localhost:6000/product
The result is in browser is
Product Page
but it should be
Main Layout
Product Page
Why ?
What's the problem ?
Layout names should be the name of the file not a path and you should never need to reference _Layout as it's the default.
Also if you want your Views and Content Pages to share the same _Layout.cshtml pages add them once to /Views/_Layout.cshtml or /Views/Shared/_Layout.cshtml instead.
If this is Self hosting HttpListener project you need to ensure all *.cshtml are set to Copy to Output Directory or the WebHostPhysicalPath references your project path.
I am getting:
Type 'ASP._Page_index_cshtml' does not inherit from 'System.Web.WebPages.WebPage'.
when I browse to my index.cshtml file. It is very simple:
#using System.Web.Optimization
#inherits System.Web.Mvc.WebViewPage
<!DOCTYPE html>
<html class="no-js" lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width">
<title>Hello World</title>
#Styles.Render("~/Content/css", "~/Content/themes/base/css")
#Scripts.Render("~/bundles/modernizr")
</head>
<body>
#Scripts.Render(
"~/bundles/jquery",
"~/bundles/jqueryui"
)
</body>
</html>
my index.cshtml file is outside of the Views folder, if that matters at all.
Do not remove the inheritance, it might be necesary and might lead to another problems in the future.
Instead try this:
Enable "View all Files" in the web project that is failing, and search for a file that seems correct but is not included in visual studio, and delete it. If it fails in your deployment folder, try to clean the folder as well, and re-deploy the site, you might have unnecesary files that might cause the same problem.
In my case, at the root of the webproject I had an extra copy of _ViewStart.cshtml (excluded from the project), I deleted that file, and that did the trick.
Hope it helps, let me know if this solve your problem as well.
I just had to remove: #inherits System.Web.Mvc.WebViewPage
Looks like I had a copy paste error when reorganizing my project.
I change the cshtml line from:
Layout = "~/Views/_ViewStart.cshtml";
to
Layout = "~/Views/Shared/_Layout.cshtml";
since _ViewStart.cshtml contains only that line of code. Layout file was required because it contains the script to client-side validation.
It worked also when I remove the line, but I prefer to keep _Layout.cshtml included.
What worked for me was to add...
#inherits System.Web.Mvc.ViewStartPage
...to the top of _ViewStart.cshtml.
I think the reason this worked for me was because of my ~/Views/Web.config file. In it I had placed the following block in order to avoid a bunch of #inherits calls on my views...
<configuration>
<system.web.webPages.razor>
<pages pageBaseType="System.Web.Mvc.WebViewPage"> <!-- Notice pageBaseType -->
...
</pages>
</system.web.webPages.razor>
</configuration>
I think this instructed the razor engine that all views including _ViewStart.cshtml were to be of type System.Web.Mvc.WebViewPage. This is not the same type as System.Web.Mvc.ViewSTartPage and hence the error. Putting #inherits System.Web.Mvc.ViewStartPage at the start of _ViewStart.cshtml explicitly specifies the required type for _ViewStart.cshtml.
It could be that an older version of System.Web.WebPages.dll is loaded to memory, and it tries to cast the your cshtml page to a version of WebPages class from that dll.
To test this, try to see what http modules are currently registered:
var allModules = HttpContext.Current.ApplicationInstance.Modules;
for( int i = 0; i < allModules.Count; i++ ) {
Trace(allModules.GetKey(i));
}
In my case that was:
....
__DynamicModule_System.Web.WebPages.WebPageHttpModule, System.Web.WebPages, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35_bca8e05a-5746-45b0-be95-2b920b455ccf
__DynamicModule_System.Web.WebPages.WebPageHttpModule, System.Web.WebPages, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35_c1a67b42-31a9-47f1-8483-9e712fabe2a7
To fix the problem you need to replace the older version of System.Web.WebPages.dll in your /Bin folders, or some other dll-s that might be referencing it.
Just delete the _ViewStart.cshtml and re-add.
No need to take Tension. Just follow 2 basic steps
Delete the _ViewStart.cshtml
Make statement to a comment. Means for the following statement in index view:
Layout = "~/Views/_ViewStart.cshtml";
and mark it as a comment:
//Layout = "~/Views/_ViewStart.cshtml";
Comment Layout variable in your .cshtml file
//Layout = "~/Views/_ViewStart.cshtml";
Why are you trying to browse directly to a view? And why isn't it in a views folder?
To get a basic "Hello World" page up you first want to create a controller called HomeController.cs:
using System.Web.Mvc;
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
}
Then create a file /Views/Home/Index.cshtml and put your markup in that. You may also need to add:
#{
Layout = null;
}
to the top of the page since it doesn't look like you are using a master page.
As a side note, all this assumes you haven't screwed with your default routing.
Remove the web.config from your Views folder.
As you're including Partial1.cshtml from that folder, it is also including the web.config from within there. And that web.config is saying that all pages must inherit from WebViewPage.
I have asp.net mvc application, that uses razor view engine.
I want to send head to browser, before body renders, to start parallel loading css, js and images, linked in css. (You can see, how this technique works on SO in chrome developer tools - network for example)
I found question about it in asp.net web forms: Send head before body to load CSS and JS asap
I tried to use this solution, but it don't work.
For razor engine next sequense of steps is actual:
action returns view result
_ViewStart.cshtml executes (set ViewBag.Layout)
view executes from first line to last (with code inclusions and sections)
view engine checks ViewBag.Layout and if it found - executes Layout (with renderind body and sections)
I think that good solution is to divide step 3 into 3 parts:
generating content for Head and Css section
send to browser
generating other part of view
Other solution is to static include all.css and basic .js files in (without sections content, generated from view), send head, and then generate view (with generation FooterScript section).
In both ways I need to start execution from Layout page, not from view. For first: Layout (head) - view (sections) - layout (flush) - view (other) - layout (body). For second: Layout (head + flush) - view (all) + Layout (body).
My _Layout.cshtml file:
<html #Html.Raw(ViewBag.xmlns)>
<head>
<title>#ViewBag.Title</title>
#Html.Partial("_MetaTags")
<link href="#Url.ThemeCss("jquery-ui-1.8.18.custom.css")" rel="stylesheet" type="text/css" />
<link href="#Url.Css("masterPage.css")" rel="stylesheet" type="text/css" />
<link rel="shortcut icon" href="/favicon.ico"/>
#RenderSection("Css", required: false)
<script src="#Url.CommonScript("jquery.common.min.js")" type="text/javascript"></script>
<script src="#Url.Script("Master.js")" type="text/javascript"></script>
#RenderSection("Head", required: false)
</head>
<body>
<div id="pageWhithoutFooter">
<div id="main">
#RenderBody()
</div>
</div>
#RenderSection("FooterScript", required: false)
</body>
</html>
Howto?
Try putting your head section into a Partial view then call this in your controller:
PartialView("_PageHeader").ExecuteResult(ControllerContext);
Response.Flush();
// Generate model
return View(model);
Not tested this but I can't see why it wouldn't work.