I have a separate project in my solution that contains some Controllers and compiled views.
I use those controllers for base classes to other controllers in my MVC application and the views are compiled using RazorGenerator.
Lets say B is Base Controller with non abstract action method SomeAction that returns View("_MyView"). _MyView.cshtml is compiled using RazorGenerator.
Lets say controller A inherits B but doesn't override SomeAction.
I've tried to make another view "~/Views/A/_MyView.cshtml" to override the default one, but it doesn't work. My question is how can I accomplish this?
ADDITIONAL INFO
1) I know that the views by default are searched in that order in those paths
"~/Views/{1}/{0}.cshtml",
"~/Views/{1}/{0}.vbhtml",
"~/Views/Shared/{0}.cshtml",
"~/Views/Shared/{0}.vbhtml"
2) Putting "~/Views/Shared/_MyView.cshtml" does override the view, but not only for controller A, but also for every other controller that inherits
B
3) Overriding SomeAction to return base.SomeAction() doesn't work
UPDATE
I have found similar question here, but doing the suggestion nothing happened
RazorGenerator Issues
I have posted my own issue here
Thank you in advance!
So far my only workaround is to install RazorGenerator on the consumer app and to also set the view _MyView.cshtml as being RazorGenerated. RazorGenator then picks up the correct view.
Another note for other visitors is not to compound the wrong view confusion with the route going to the base controller instead of the the consumer controller. In solving this issue earlier to being able to figure out the actual wrong view was being served by the right controller as the OP and I have an issue with. I have code in my base application_start that removes route duplicates.
Anyone else hitting this issue you need to update the RazorGeneratorMvcStart.cs to set PreemptPhysicalFiles = false in the master project. By default this is not the case and the views in the master project with take priority:
var engine = new PrecompiledMvcEngine(typeof(RazorGeneratorMvcStart).Assembly)
{
UsePhysicalViewsIfNewer = HttpContext.Current.Request.IsLocal,
PreemptPhysicalFiles = false
};
ViewEngines.Engines.Add(engine);
This file is App_Start\RazorGeneratorMvcStart.cs. It is also important to Add the engine rather than Insert it. The default is to insert at position 0.
Note: I hit this issue when updating NuGet packages, it seems that the file gets overritten, resetting this back to the default behaviour.
Related
I am really struggling with changing my mindset from the #html.Action way of doing things to this new Component.Invoke way of life.
So to outline my structure. I have a Controller called "Contract" with the standard Index IActionResult.
In there I have a partial view that is a form, which I would have normally have just loaded from the index.cshtml using the following:
if(Something = my value){
#Html.Action("p_NewContract", new { id = x })
}
Which always worked.
For what I can read, I now need to make a new folder outside my nicely structured view and partial view folders under each Controller. The folder I HAVE to create is "Shared/Components/p_NewContract.cshtml"
And then in that folder, I need to create a .cs file with the postfix of ViewComponent (p_NewContractViewComponent.cs) And somehow the runtime will know those two things belong together?
In that CS file I then add [ViewComponent(Name = "p_NewContract")] before the public class and then after all that I can add to my Index.cshtml
#await Component.InvokeAsync("p_NewContract", new { id= 2})
I have done all of this, no sign of it working. Am I missing something fundamental, is there a way I can get my old Html.Action back? It was so much easier.
Any assistance to help my frustration would be great!
Caz
There's two parts to a view component: your class that derives from ViewComponent and a view that component should return. By convention, your view component classes actually go in /ViewComponents, while again by convention, your view goes into Views/Shared/Components/{component name}/Default.cshtml.
That's just convention, though. The same as how Razor automatically finds views based on controller/action name. In truth, the classes can go wherever you like. They are brought in via reflection, so as long as it's somewhere in the assembly or a referenced assembly, it will be found. The views likewise can be moved wherever you like, you just have to tell it where to find them, since it won't be able to do it automatically based on convention anymore. See: https://learn.microsoft.com/en-us/aspnet/core/mvc/views/view-components#view-search-path
I am looking to offload some controller functions in my MVC 5 project to a single function. Both views being passed in inherit from the base view.
CompetitionsViewModel:CompRegisterViewModel
So far, this appears to work:
private async Task<bool> RegisterUser(RegisterViewModel model, string returnUrl = "")
{...}
Calling code:
bool passed = await RegisterUser(model);
A few questions on this, though:
Is this the proper method of going about this when one of the controllers uses a view that is inherited from another view, and you want a separated function for this?
Is there anything I should be concerned about using async in this function? - Will model errors (ModelState.AddModelError) work as expected within this function? Everything appears to work on the surface, but I do have a ton of custom JQuery handling extensive error correction prior to this point.
Even though this is an async process, will the model still be passed as ref (less of a concern right now, but just for future knowledge).
Basically what is happening here is I have a cart that has the ability to register. You could also register independently from the cart, so I use a shared view for registration. As such, both parent views implement the child views, so inheritance of the view model seemed like the most intelligent route.
The options I see on the table are: recreate the process in both controller functions (duplicated code? ..maintenance nightmare), create an interface (having to update in multiple places when adding a field/changing a field?), or do what I did - one single function that handles both, while the calling methods handle their non-inherited functionality.
Do you see a fourth, better option, or does my solution seem acceptable?
The answer to this question is as follows:
Still unsure about best practice, but it does work fine.
as for whether this is considered good practice or not, I would find it very bad practice to duplicate code, so take it for what it is.
It works fine - AddModelError, etc. works.
Objects are passed by reference in normal circumstances, so I'm assuming "yes" for now. Will update if I run into a problem.
I have been doing ASP.NET for a while, and have done very little in MVC (in general), but very new at the ASP.NET MVC framework, as well as the correct terminology on a few things.
Here is my example (actual application I am working on is different, but this is a public example I can use) - I want to create a simpler version of Redmine, but in .net.
I want to list issues, in such a way that if I go to example.org/issues, I see a list of all issues (similar to http://www.redmine.org/issues), across all projects.
But if I go to the project, as in example.org/project/issues, I see just the issues for that project (similar to http://www.redmine.org/projects/redmine/issues). This would be the same for example.org/project2/issues, etc.
What is the correct term for this? I would assume that the developers didn't rewrite the 'issues' code in two places, that they reused this code.
Since I don't know the fully correct term for this, it is hard to find good examples in the ASP.NET MVC world that I can start with. So the second part of this, is what would be an example that I could look at in ASP.NET MVC, and how should this look in Visual Studio to me?
There would also be several other things under /project/, like settings, details, logs, etc, similar on how one would navigate http://www.redmine.org/projects/redmine/.
Finally, what if projects was not on the top level? As in, what if my application was more like:
example.org/
example.org/projects/
example.org/projects/project1
example.org/projects/project1/issues
example.org/projects/project2
example.org/dashboard/
Set up one controller called whatever you'd like (I'll use RedMineController).
In that controller you'll have a single action method named ListIssues. Accept a parameter named ProjectName:
public ActionResult ListIssues(string projectName) {}
Lastly create two routes in your global.asax.cs:
routes.MapRoute(
"Issues Root",
"issues",
new { controller = "RedMine", action = "ListIssues" }
);
routes.MapRoute(
"Project Issues",
"projects/{projectName}/issues",
new { controller = "RedMine", action = "ListIssues" }
);
In your ListIssues action, check if projectName == null, and if so get all issues, otherwise get specific ones. The first route will pass null, the second will pass what is in the URL. Don't forget to throw a 404 if the project name in the URL is invalid.
Hope this helps!
well though not a proper ans. but you can do what you want easily by just reversing the way you are doing them.. ie
instead of doing www.example.org/project/issues do www.example.org/issues/projector just www.example.org/issues
this way you can easily make a issues controller and everything in the url after issues can be this string by which you would be able to determine the resource for which you need to list all the issues. an example route for it can be
"{issues}/{*resource}",new{controller="issues"}
So I have a controller called EmployeeController, and all the Views are in /Employee.
I'd like to create a route so that the EmployeeController will use /Employees and /Employees/Add instead of /Employee and /Employee/Add.
I keep finding articles about how to change the route to go to different actions, but I couldn't find any way to do this.
I think you're confusing Views with Routes. ASP.NET MVC relies a lot on convention, and in this example it takes the controller component of the route an applies it to find the controller. You can define a new route:
routes.MapRoute("Employees", "employees/{action}", new {
controller = "Employee",
action = "Index" });
Actually, there are 2 different questions:
First one is about routes mapping and here I'd agree on simple solution suggested by Matthew Abbott and Bugai13.
Second one is about "Views" folder conventions and View files resolution. If you want some custom logic about that you may inherit ViewResult and change the way it finds the appropriate View file. You can also dive deeper into framework and tweak the way View is found and instantiated by creating your own IViewEngine or customizing one of those already existing.
It seems though, that all you need is first thing - just provide more specific route mappings with URL pattern like employees/{action} and you're done.
Why not just rename EmployeeController to EmployeesController? Then you don't have to mess with the route.
Of course you will then have to change your Views\Employee folder to Views\Employees as well.
I just converted a my controller to asyncController in asp.net mvc 2. (ie: spliting action method About into AboutAsync and AboutCompleted). However, I am getting a resource not found error.
My route table has not change and the only reason I can think of is because I am mvcextension project. Since it does it's own wiring of controllerFactory and creates instance from the IoC container, does it need to wire up the AsyncHttpHandler has well? Can anyone drop some hints?
thanks in advance.
A few notes for things to check for, in order:
Make sure your controller subclasses AsyncController rather than simply Controller.
The URL to hit HomeController::AboutAsync()/AboutCompleted() should be /Home/About (the Async isn't part of the URL)
If you're using a custom invoker, it must subclass AsyncControllerActionInvoker (or implement IAsyncControllerActionInvoker) rather than subclass ControllerActionInvoker directly
Make sure that you're hooking the MvcRouteHandler up to Routing (which should be the default behavior of MapRoute). If you're using a custom IRouteHandler, make sure that its GetHttpHandler() method is returning an MvcHandler. (Note - you should not subclass MvcHandler.)