MVC Routes data not available - asp.net-mvc

I have a controller called BaseController. In the BaseController, I have an Action method called Index which has some logic that involves querying the routes and building the URLs. Something on the lines of:
var link = Url.RouteUrl("myroute", new { id = 5 });
All this is well and fine until I create a controller NewController that extends the BaseController. In the constructor of NewController, I pass BaseController as a dependency.
public class NewController
{
private BaseController _baseController;
public NewController(BaseController baseController)
{
_baseController = baseController;
}
public ActionResult Index()
{
return _baseController.Index();
}
}
Reason why this was needed was because I need to override the view (some HTML and CSS changes). I didn't want to recreate the models and services and rewrite the business logic, so thought this would be the best and most time-effective approach.
Only issue is when the BaseController's Index Action is called, the Url is null obviously. Routes data is not available because the request was generated outside the base controller.
What is the best way to get around this?

Make BaseController.Index() virtual:
public class BaseController : Controller
{
public virtual ActionResult Index()
{
return View();
}
}
Then use inheritance:
public class NewController : BaseController
{
public override ActionResult Index()
{
var index = base.Index();
//do whatever
return index;
}
}

You are trying to call action method from another controller. Propably your constructor method gets baseController as a null. can you try to implement it like following
public ActionResult Index()
{
return new BaseController().Index(); // assume you call index action
}
Or you can call BaseController action from another controller like following
public ActionResult Index()
{
return RedirectToAction("Index", "Base"); // assume you call index action
}
You can also change Route url like following.
#Url.RouteUrl("myroute", new { controller = "Base", action = "Index", id = 5 })

I have another solution that requires a little bit of code design efforts.
Why don't you Abstract your business logic away from the two Controllers?
For example: RouteBuilder.cs a class that have the functions that contains the logic of building the routes.
And BaseClass.cs is a class that contains the Logic shared between the two Controllers.
Then:
public class BaseController
{
public ActionResult Index()
{``
//Instantiase BaseClass.cs and call the needed functions. Then RouteBuilder.cs and call functions.
return View();
}
}
public class NewController
{
public ActionResult Index()
{``
//Instantiase BaseClass.cs and call the needed functions.
return View();
}
}
Viola. Problem solved and clean code produced.

Related

How to declare controller action method return type?

I understand ActionResult is the base class for RedirectResult class so essentially functionality for the code below is the same.
Is there an advantage to explicitly specify the appropriate result class as a return type as opposed to the base class?
public ActionResult Index()
{
return Redirect("Home/Contact");
}
public RedirectResult Index()
{
return Redirect("Home/Contact");
}
if you need redirect quickly to another action without lost any data and this action is in the same controller , call the action as a method:
public IActionResult Index()
{
return Contact();
}
public IActionResult Contact()
{
....
}
which one from another redirections methods is better is an opinion-based question and is not allowed by SO policy

Return a View from external Class

I want to choose between multiple clients before returning a view in ASP.NET Core MVC.
So let there be a HomeController with the following code:
public class HomeController : Controller
{
public virtual IActionResult Index()
{
return View();
}
}
Now I have multiple clients, and I want to decide what view will be returned. But not at this place, so I want to write it in another file.
So my question is, is there something possible like this:
public class HomeController : Controller
{
public virtual IActionResult Index()
{
ViewChooser vc = new ViewChooser();
return vc.GetNextView();
}
}
public class ViewChooser
{
public IActionResult GetNextView()
{
// do some stuff and then..
return View("aaaa");
}
}
The class "ViewChooser" does not inherit from Controller, so I can't just write return View().
The reason why I want this to work like this is because I want to choose between multiple workflows without changing the URL. (Otherwise areas would be a possible solution for my problem.)
So if customer A calls www.myserver.com/function1 he get another functionality and view as customer B.
Any ideas? Or am I far away from the solution?
Regards
One option would be to have ViewChooser inherit from Controller. It is, after all, trying to return a view which is something a controller does.
Alternatively, just have ViewChooser return the name of the view:
public class ViewChooser
{
public string GetNextView()
{
// do some stuff and then..
return "aaaa";
}
}
And your controller can use that for its view selection:
public class HomeController : Controller
{
public virtual IActionResult Index()
{
ViewChooser vc = new ViewChooser();
return View(vc.GetNextView());
}
}
This would mean that GetNextView() must always return a valid named view, never another kind of IActionResult. But would decouple the ViewChooser from the MVC framework.
If you have fixed number of clients say "5 clients" then you can create 5 different ActionResult Methods which will return 5 different views. Afterwards you can create a custom attribute where you will write the logic for fetching the client information. You can put this custom attribute over each ActionResult method.

ASP.NET MVC - Routing Structure

I have an ASP.NET MVC 4.5 app. In this app, I have two controllers: Parent and Children. The look something like this:
[RoutePrefix("dashboard/parents")]
public partial class ParentsController : Controller
{
public ActionResult Index()
{
return View();
}
[Route("add")]
public ActionResult Add()
{
return View();
}
}
[RoutePrefix("dashboard/children")]
public partial class ChildrenController : Controller
{
public ActionResult Index()
{
return View();
}
[Route("add")]
public ActionResult Add()
{
return View();
}
}
At this time, these controllers work how I want. However, in my ChildrenController, I want to add something like an overload to the add route. In other words, I'd like for the user to be able to visit: /dashboard/parents/{parentId}/children/add. This URL would be used to add a child to a specific parent. My question is, how do I update my controllers to allow for this type of scenario?
thank you!
I think what you are looking for the is "~" to override your default routeprefix.. The following example is taken from the asp.net website which tells you how to accomplish the task.
[RoutePrefix("api/books")]
public class BooksController : ApiController
{
// GET /api/authors/1/books
[Route("~/api/authors/{authorId:int}/books")]
public IEnumerable<Book> GetByAuthor(int authorId) { ... }
// ...
}

Can I use method hiding on an ActionResult that has the same signature as the base controller?

I have 2 controllers, one inheriting the other. I need to override an ActionResult from the base controller because I need to change to code to implement pagination in the plugin for nopCommerce. However, I get an AmbiguousMatchException because of the new ActionResult.
Base Controller:
public class BaseController : Controller {
public ActionResult Category(int categoryId, CatalogPagingFilteringModel command)
{
//original action result code
}
}
Customer Controller w/ inheritance
public class CustomController : BaseController {
public new ActionResult Category(int categoryId, CatalogPagingFilteringModel command)
{
// my pagination code with different model/view
}
}
Route Info:
Here I remove the route for the base controller and add a new route to use the CustomCatalog controller.
routes.Remove(routes["OriginalCategory"]);
routes.MapLocalizedRoute(
"OriginalCategory",
"Category/{categoryId}/{SeName}",
new { controller = "CustomCatalog", action = "Category", SeName = UrlParameter.Optional },
new { categoryId = #"\d+" },
new[] { "Nop.Plugin.Common.Web.Controllers" });
I then get an AmbiguousMatchException
[AmbiguousMatchException: The current request for action 'Category' on
controller type 'CustomCatalogController' is ambiguous between the
following action methods: System.Web.Mvc.ActionResult Category(Int32,
Nop.Web.Models.Catalog.CatalogPagingFilteringModel) on type
Nop.Plugin.Common.Web.Controllers.CustomCatalogController
System.Web.Mvc.ActionResult Category(Int32,
Nop.Web.Models.Catalog.CatalogPagingFilteringModel) on type
Nop.Web.Controllers.CatalogController]
EDIT
The base controller resides in the core of the application where as the CustomController is in the plugin meaning that I cannot modify the base controller's type.
How about using virtual in the base controller and override in the derived controller instead of new?
base:
public virtual ActionResult Category(...) { }
derived:
public override ActionResult Category(...) { }
You cannot override a method which has not been declared virtual.
You can either define a new method with a different signature or you can encapsulate the functionality of the original class by maintaining a private reference to it in a wrapper class. It helps if you have an interface that you can implement from the base library (because this allows you to substitute the wrapper class where the base class that implements the same interface would be used), but you can do it without the interface, too.
// Base class source code is not modifiable
class BaseClass {
public ActionResult Category(...) {}
public ActionResult Other() {}
}
// Wrapper class can modify the behavior
class Wrapper {
private BaseClass baseClass = new BaseClass(); // Instantiate appropriately
public ActionResult Category(...) {
// do some stuff
}
public ActionResult Other() {
return baseClass.Other();
}
}

Can we call the Method of a controller from another controller in asp.net MVC?

Can we call the Method of a controller from another controller in asp.net MVC?
You could also simply redirect straight to the method like so:
public class ThisController
{
public ActionResult Index()
{
return RedirectToAction("OtherMethod", "OtherController");
}
}
Technically, yes. You can call a static method of a controller or initialize an instance of a controller to call its instance methods.
This, however, makes little sense. The methods of a controller are meant to be invoked by routing engine indirectly. If you feel the need to directly call an action method of another controller, it is a sign you need some redesign to do.
Well, there are number of ways to actually call an instance method on another controller or call a static method off that controller type:
public class ThisController {
public ActionResult Index() {
var other = new OtherController();
other.OtherMethod();
//OR
OtherController.OtherStaticMethod();
}
}
You could also redirect to to another controller, which makes more sense.
public class ThisController {
public ActionResult Index() {
return RedirectToRoute(new {controller = "Other", action = "OtherMethod"});
}
}
Or you could just refactor the common code into its own class, which makes even more sense.
public class OtherClass {
public void OtherMethod() {
//functionality
}
}
public class ThisController {
public ActionResult Index() {
var other = new OtherClass();
other.OtherMethod();
}
}
Try This.
var ctrl= new MyController();
ctrl.ControllerContext = ControllerContext;
//call action
return ctrl.Action();
As controllers are just classes: Yes, we can do it. We can do it by some of the following ways:
By directly redirecting- return RedirectToAction("MethodName", "ControllerName");
By creating object - ControllerName objController=new ControllerName();
objController.methodName(parameters)
Yes, you can call a method of another controller.
public ActionResult Index()
{
AccountController accountController = new AccountController {ControllerContext = ControllerContext};
return accountController.Index();
}
The controller is also a simple class. Only things are that its inheriting Controller Class. You can create an object of the controller, but it will not work for Routing if you want to redirect to another page.

Resources