All, In my application design, Some the actions of Controller classes will call the same method firstly whenever it was called. the code snippet looks like below . please review it .thanks.
public class Controller1 : Controller
{
public ActionResult Action1()
{
FunctionA(); //This function must be called firstly.This function is defined somewhere.
return View("a1");
}
public ActionResult Action2()
{
FunctionA();
return View("a2");
}
}
public class Controller2 : Controller
{
public ActionResult Action3()
{
FunctionA();
return View("a3");
}
public ActionResult Action4()
{
FunctionB();
return View("a4");
}
}
In current case , Action1,Action2 in Controller1 and Action3 in Controller2 will call the same FunctionA() , Based on the DRY theory, It is better to define a base controller or method something , I don't know how to make it in Asp.net MVC4. Please help me .thanks.
Only you can answer this question because essentially the answer is dependant on your design.
If your FunctionA method is specific to that controller then you could introduce a base class. However, you could alternatively use a helper class or even just an extension method (if it's generic enough) to achieve your DRY architecture.
In terms of how to call it in your action, if it must be run before the action is executed then I would suggest introducing a custom ActionFilterAttribute which you can use to decorate each action e.g.
public class Controller1 : Controller
{
[FunctionAAttribute]
public ActionResult Action1()
{
return View("a1");
}
[FunctionAAttribute]
public ActionResult Action2()
{
return View("a2");
}
}
Or if it has to be run before every action on that particular controller, just decorate the controller e.g.
[FunctionAAttribute]
public class Controller1 : Controller
{
public ActionResult Action1()
{
return View("a1");
}
...
}
It's not quite as simple as that. If they simply share some code it doesn't necessarily mean they should have a common base class that implements that functionality. You're thinking about details in code to drive your interface.
I can't answer the question without knowing more about the specifics, but as long as the code they are all using is in one place, you're halfway in the right direction.
Related
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.
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.
I created a custom action filter to perform logging for auditing trails.I added my logging code to public override void OnActionExecuting(ActionExecutingContext filterContext).
My question is, how do I pass my EF dbContext to this method? I'd like to write a single action filter and re-use it on other development projects without changing the dbcontext for every project.
If this isn't a recommended practice, what should I do?
If I were doing such a thing, I think I would be implementing a generic audit logging method in the service layer that already is aware of the data context and pass on an audit model to it. This way, if there's ever a need to log different parts of the application (that may not even be related to controllers), you don't have to re-implement anything.
Alternatively if you want to stick to just controllers you could perhaps make an interface for the data context
public interface IDataContext<T> where T : DbContext
{
T DataContext { get; }
}
Create a BaseController that implements in along with System.Web.Mvc.Controller
public class BaseController : Controller, IDataContext<YourDbContextClass>
{
public YourDbContextClass DataContext { get { return new YourDbContextClass(); } }
}
You can use this base class on controllers and reach the context via DataContext, but for logging, you can create a new class with your overridden method
public class AuditController : BaseController
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// whatever you do inside here
// DataContext.LaDiDa
}
}
And then inherit your stuff from it
public class HomeController : AuditController
{
public ActionResult Index()
{
return View();
}
}
Although this isn't the most straight forward solution, you could lose the interface all together and do everything in the BaseController, but I'm just throwing stuff on the board in case you see something that would work for you.
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.
Since asp.net mvc has changed a lot since November, does anyone have a solution to this question:
Resolve FilterAttributes On Controller And Action
Phil said an ActionFilter on a controller is just shorthand for applying the attribute to all action methods of the controller, and it is true, if I put the same ActionFilter attribute on the controller and on an action method, it will run twice. But this doesn't seem like natural behavior since the compiler won't even let you put the same attribute directly on a method multiple times.
A filter can take precedence over another filter by specifing the Order property on each filter. For example...
[MyFilter(Order=2)]
public class MyController : Controller
{
[MyFilter(Order=1)]
public ActionResult MyAction()
{
//...
}
}
In this example the filter on the action method would execute before the filer on the controller.
HTH
I found one way to do it by "cheating" a bit with the ordering, inheritance and the AttributeUsage parameter
First, define your ActionFilter for the controller
[AttributeUsage(AttributeTargets.Class)]
public class FilterController : ActionFilterAttribute
{
public FilterController()
{
this.Order = 2;
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (!filterContext.HttpContext.Items.Contains("WeAlreadyWentThroughThis"))
{
// do our thing
filterContext.HttpContext.Items.Add("WeAlreadyWentThroughThis", "yep");
base.OnActionExecuted(filterContext);
}
}
}
Then inherit the class for your action attribute
[AttributeUsage(AttributeTargets.Method)]
public class FilterAction : FilterController
{
public FilterAction()
{
this.Order = 1;
}
}
It's far from perfect since you have to rely on HttpContext and two classes (though you could use namespaces to name both classes the same). But you get compiler-enforced check of the attribute scope for class or action and you won't forget an order parameter when typing the code.