I'm trying to implement vertical slice architecture. I have my Program.cs in Web project and my Views are coupled with Controllers in Core project. Razor won't find them when they are placed like that, but easily locates them in Web/Views, for example (relative path)
I register my controllers with views like that:
builder.Services.AddRazorPages(); // I also temporarily have some Razor Pages in my system.
builder.Services.AddControllersWithViews()
.AddApplicationPart(typeof(Faction).Assembly); // Add controllers from another assembly
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllers();
app.MapRazorPages();
return View("/Features/Statistics/GetLast5GamesStatsGlobal/GetLast5GamesStatsGlobal.cshtml", viewModel);
Produces
InvalidOperationException: The view '/Features/Statistics/GetLast5GamesStatsGlobal/GetLast5GamesStatsGlobal.cshtml' was not found. The following locations were searched:
/Features/Statistics/GetLast5GamesStatsGlobal/GetLast5GamesStatsGlobal.cshtml*
File is located on this location, but in another project
How do I make them visible to the engine?
I have tried respecifying paths in different formats, using absolute path.
Related
I have a bare bones razor pages project targeting the net6.0 framework using Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation 6.0.11 (Microsoft.NET.Sdk.Web).
It consumes a Razor Class Library, also targeting net6.0 (Microsoft.NET.Sdk.Razor).
Both projects are in the same solution in VS 2022 Pro.
I am using minimal build:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages().AddRazorRuntimeCompilation();
builder.Services.Configure<MvcRazorRuntimeCompilationOptions>(options => {
var libraryPath = System.IO.Path.GetFullPath(System.IO.Path.Combine(builder.Environment.ContentRootPath, "..", "razor.platform.thehub.cloud"));
options.FileProviders.Add(new PhysicalFileProvider(libraryPath));
});
var app = builder.Build();
app.UseStaticFiles();
app.MapRazorPages();
app.Run();
The project builds and consumes the razor class library perfect fine. Razor pages, wwwroot content all work perfectly in the consuming app. Breakpoints work fine in the RCL and changes to any RCL wwwroot content prompts a hot reload as expected (css, js etc).
My only issue is with .cshtml in the RCL (Pages). Any changes to them prompts a hot reload as expected, so VS debugging is detecting a code change and confirms that 'Code changes were applied successfully'. However, the changes to the .cshtml file in the RCL are not shown. I have to rebuild and restart the local debugging session to see them.
I'm convinced I've been able to do this in the past (Core 2/3), but I may be imagining it!
Any help greatly appreciated.
I was also wondering the same. The feature that existed in the past was called "razor runtime compilation". According to official docs:
Enable runtime compilation with the instructions at Conditionally
enable runtime compilation in an existing project.
Configure the runtime compilation options in
Startup.ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.Configure<MvcRazorRuntimeCompilationOptions>(options =>
{
var libraryPath = Path.GetFullPath(
Path.Combine(HostEnvironment.ContentRootPath, "..", "MyClassLib"));
options.FileProviders.Add(new PhysicalFileProvider(libraryPath));
});
}
In the preceding code, an absolute path to the MyClassLib RCL is
constructed. The PhysicalFileProvider API is used to locate
directories and files at that absolute path. Finally, the
PhysicalFileProvider instance is added to a file providers collection,
which allows access to the RCL's .cshtml files.
One can include js/css by placing them in /wwwroot and then using the InjectJavascript and InjectStylesheet options.
But how can I instead inject a file that exists as an embedded resource? It used to be possible (if I remember correctly), but I can't find out how to do it for the AspNetCore version of Swashbuckle.
(I'm not asking how to embed a file, but rather how to tell Swashbuckle to use it.)
To customize the Swashbuckle UI in asp.net core, as you said, we need to use the SwaggerUIOptions's InjectJavascript and InjectStylesheet options.
For example, I create a Asp.net 6 application with Swashbuckle.AspNetCore 6
Then, add the css and javascript fille in the wwwroot folder.
Finally, in the program.cs file, enable Static File Middleware and inject the css and javascript file as below:
app.UseStaticFiles();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.InjectStylesheet("/swagger-ui/css/custom.css");
c.InjectJavascript("/swagger-ui/js/CustomJavaScript.js");
});
The result like this:
I'm using Visual Studio 2017 and .NET Core 2.2.
Do the following:
New Project and select ASP.NET Core Web Application (press OK)
This takes you to a second dialog where you can select Web Application (has example Razor Pages) or Web Application (Model-View-Controller).
I want to point out one difference between the two options, when looking at Startup.cs:
If you select just Web Application, you will get this at the bottom of the Configure function:
app.UseMvc();
If you select Web Application (Model-View-Controller), you will get this:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
So, the MVC project defines some type of routing. Which specifically tells me that it is going to use the Home Controller by default and if no action is specified then the Index(.cshtml) action will be used.
The first option (Razor pages), routes nothing.
I don't understand. Does this mean that if you don't define a MapRoute it will just always use the Index(.cshtml) action?
Because With the MVC Project, if I used just:
app.UseMvc();
and defined no MapRoute, I would get a 404. How is the Razor app defining it's routing (and where)?
I have been restructuring my .NET MVC Core project to make it easier to manage. A simplified version of my project is as follows:
Controllers/
CatController.cs
DogController.cs
Resources/
Views/
Cat/
Index.en-CY.resx
Dog/
Index.en-CY.resx
Views/
Cat/
Index.cshtml
Dog/
Index.cshtml
Localization works great with this structure, but the structure itself (in the real project) is difficult to navigate.
The new structure is as follows:
Controllers/
Animal/
Cat/
CatController.cs
Dog/
DogController.cs
AbstractAnimalController.cs
Resources/
Views/
Animal/
Cat/
Index.en-CY.resx
Dog/
Index.en-CY.resx
Views/
Animal/
Cat/
Index.cshtml
Dog/
Index.cshtml
However this structure breaks the view localization. Specifically, the view no longer uses the resource file, displaying the keys when it previously displayed the values.
I have narrowed down the issue to being that the controller name is still CatController, but the location of the Cat view is now Animal/Cat/Index.cshtml.
Localization works if I keep my views and resources in the old structure, but I'd rather be able to keep it consistent with the Controllers directory structure.
Localization is done using #inject IViewLocalizer Localizer in each of the views. According to Microsoft's documentation, when using an IViewLocalizer in a view, the resource should either have the same directory structure as the view or use representative dot notation. It does not specify that the view must be in a directory named for its controller, which is what I'm finding here.
Is this a bug with .NET Core, or am I doing something wrong?
I'm using MVC6, and I can't figure out how to relocate these folders in my project:
MyWebApp
Controllers
ViewModels
Views
Into a common folder like this:
MyWebApp
Mvc
Controllers
ViewModels
Views
Physically relocating the files and folders is easy enough, but MVC can't find the files anymore:
InvalidOperationException: The view 'Index' was not found. The
following locations were searched:
/Views/App/Index.cshtml
/Views/Shared/Index.cshtml
How do I add the /Mvc folder as a location to look for MVC related goodness?
What you are looking for is called Areas. Unlike in older versions of MVC, with Core there is no formal "Areas" option when you right click on the Project. You can however create a folder and name it "Areas". Within the Controllers you can decorate each controller with [Area("myArea")] to specify the controller for within the Areas.
In the startup.cs, setup is as follows:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "areaRoute",
template: "{area:exists}/{controller}/{action}"
);
routes.MapRoute(
name: "default",
template: "{area=myArea}/{controller=Home}/{action=Index}");
});