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.
Related
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've got an ASP.NET MVC4 project with standard controllers and views. I have to different master pages I use, depending on a global variable I can reach out and get based on the Request.Url.Host. I've written the code below but it is getting kind of bulky to put in every controller. I've gotten it pretty short but was hoping for a suggestion to make it much cleaner.
private ActionResult IndexBase(string year)
{
var data = null; // real data here for model
var localConfig = LocalConfig.GetLocalValues(Request.Url.Host, null, year);
ViewResult view = localConfig.EventType == "svcc"
? View("Index", "~/Views/Shared/_Layout.cshtml", data)
: View("Index", "~/Views/Shared/_LayoutConf.cshtml", data);
return view;
}
I don't know if this solution works for you, but I would solve it with ViewModel's and a common base controller.
One of the nice things with Layouts is you can pass a base ViewModel with the properties common to all your pages (the users name, for example). In your case, you could store the path to the Layout.
First, the base class every ViewModel derives from:
public class MasterViewModel
{
public string Name { get; set; }
public string Layout { get; set; }
}
I prefer to use a 1:1 mapping of ViewModels to Views. That is, each action gets it's own ViewModel. For example: HomeIndexViewModel for /Home/Index, ProfileEditViewModel for /Profile/Edit, etc.
public class HomeIndexViewModel : MasterViewModel
{
// properties you need for /Home/Index
}
To simplify creating the ViewModels, I add a generic method on a base controller that handles setting all these the common properties:
public class BaseController : Controller
{
protected T CreateViewModel<T>() where T : MasterViewModel, new()
{
User user = db.GetUser(User.Identity.Name);
var localConfig = LocalConfig.GetLocalValues(Request.Url.Host, null, year);
return new T()
{
Name = user.Name,
Layout = localConfig.EventType == "svcc" ? "~/Views/Shared/_Layout.cshtml"
: "~/Views/Shared/_LayoutConf.cshtml"
}
}
}
And finally, just use CreateViewModel() in each of your Actions and things should work:
public class HomeController : BaseController
{
public ActionResult Index()
{
HomeIndexViewModel viewModel = CreateViewModel<HomeIndexViewModel>();
return View(viewModel);
}
}
Inside the Views, you can just set
#model HomeIndexViewModel
#{
Layout = Model.Layout;
}
There's no need to duplicate the path anywhere, and changing the logic on which Layout to show requires you only change it in one place.
I have a project that has a 'core' version, and a 'customised' version.
They are separate projects.
'customised' inherits functionality from 'core' and in some case overrides methods.
For example:
I have a user model that looks like this:
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
Then, in a separate assembly,
public class User : Core.User
{
public string CustomProperty { get; set; }
}
I then have a controller (in my 'core' assembly)
public class UserController : Controller
{
[HttpPost]
public ActionResult SaveUser(User user)
{
}
}
In my other project, I have a UserController that inherits from Core.UserController:
public class UserController : Core.UserController
{
[HttpPost]
public ActionResult SaveUser(Custom.User user)
{
}
}
Obviously, in my Global.asax I have the controller namespaces mapped
However, when I hit the SaveUser method, I get
The current request for action SaveUser on controller type
UserController is ambiguous between the following action methods
While I understand the problem, is there any way around this?
In a nutshell:
I want to use Core.UserController methods most of the time, but in this instance, I need to use my Custom.UserController SaveUser method (since it takes my Custom.User type)
Polymorphism?
public class UserController : Controller
{
[HttpPost]
public virtual ActionResult SaveUser(User user)
{
}
}
public class UserController : Core.UserController
{
[HttpPost]
public override ActionResult SaveUser(User user)
{
var customUser = user as Custom.User;
if(customUser != null)
{
//Your code here ...
}
}
}
Another possible workaround if the polymorphism solution doesn't work or isn't acceptable, would be to either rename your UserController or its action method to something slightly different:
public class CustomUserController : Core.UserController
{
[HttpPost]
public ActionResult SaveUser(Custom.User user)
{
}
}
public class UserController : Core.UserController
{
[HttpPost]
public ActionResult SaveCustomUser(Custom.User user)
{
}
}
If you wanted to keep the routes consistent with the other project, you would just have to create a custom route for this.
I encountered the same problem in my own project today and came across your post.
In my case, while I didn't want to alter the way the core controller's logic functioned, I was able to make changes to its code, and thus its modifier keywords. After adding virtual to the base controller's actions, and override to my derived controller's actions. The original controller's actions still function, my derived controller uses my customized actions, no more ambiguous errors.
I realize you may not be able to modify your Core controller, and if this is the case, then you need to differentiate your actions using some other means. Action name, parameters or some other solution such as a custom implementation of ActionMethodSelectorAttribute. That was my first attempt at this problem, but before I got too far down that path of how to implement it, I discovered the virtual/override solution. So I don't have code to share on that route unfortunately.
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.
I edited my whole question, so do not wonder :)
Well, I want to have an ActionResult that takes domain model data and some additional parameters, i.e page index and page size for paging a list. It decide itself if it returns a PartialViewResult or a ViewResult depending on the kind of web request (ajax request or not).
The reffered data shall be mapped automatically by using an IMappingService, which is responsible for transforming any domain model data into a view model.
The MappingService uses AutoMapper for simplicity.
MappingActionResult:
public abstract class MappingActionResult : ActionResult
{
public static IMappingService MappingService;
}
BaseHybridViewResult:
public abstract class BaseHybridViewResult : MappingActionResult
{
public const string defaultViewName = "Grid";
public string ViewNameForAjaxRequest { get; set; }
public object ViewModel { get; set; }
public override void ExecuteResult(ControllerContext context)
{
if (context == null) throw new ArgumentNullException("context");
var usePartial = ShouldUsePartial(context);
ActionResult res = GetInnerViewResult(usePartial);
res.ExecuteResult(context);
}
private ActionResult GetInnerViewResult(bool usePartial)
{
ViewDataDictionary viewDataDictionary = new ViewDataDictionary(ViewModel);
if (String.IsNullOrEmpty(ViewNameForAjaxRequest))
{
ViewNameForAjaxRequest = defaultViewName;
}
if (usePartial)
{
return new PartialViewResult { ViewData = viewDataDictionary, ViewName = ViewNameForAjaxRequest };
}
return new ViewResult { ViewData = viewDataDictionary };
}
private static bool ShouldUsePartial(ControllerContext context)
{
return context.HttpContext.Request.IsAjaxRequest();
}
}
AutoMappedHybridViewResult:
public class AutoMappedHybridViewResult<TSourceElement, TDestinationElement> : BaseHybridViewResult
{
public AutoMappedHybridViewResult(PagedList<TSourceElement> pagedList)
{
ViewModel = MappingService.MapToViewModelPagedList<TSourceElement, TDestinationElement>(pagedList);
}
public AutoMappedHybridViewResult(PagedList<TSourceElement> pagedList, string viewNameForAjaxRequest)
{
ViewNameForAjaxRequest = viewNameForAjaxRequest;
ViewModel = MappingService.MapToViewModelPagedList<TSourceElement, TDestinationElement>(pagedList);
}
public AutoMappedHybridViewResult(TSourceElement model)
{
ViewModel = MappingService.Map<TSourceElement, TDestinationElement>(model);
}
public AutoMappedHybridViewResult(TSourceElement model, string viewNameForAjaxRequest)
{
ViewNameForAjaxRequest = viewNameForAjaxRequest;
ViewModel = MappingService.Map<TSourceElement, TDestinationElement>(model);
}
}
Usage in controller:
public ActionResult Index(int page = 1)
{
return new AutoMappedHybridViewResult<TeamEmployee, TeamEmployeeForm>(_teamEmployeeRepository.GetPagedEmployees(page, PageSize));
}
So as you can see the IMappingService is hidden. The controller should not know anything about the IMappingService interface, when AutoMappedHybridViewResult is used.
Is the MappingActionResult with the static IMappingServer appropriate or am I violating the DI principle?
I think a better design is to have a ViewResultFactory that depends on IMappingService, then you can inject that into your controller. Then you call it like so:
public class MyController : Controller
{
IViewResultFactory _viewResultFactory;
ITeamEmployeeRepository _teamEmployeeRepository;
public MyController(IViewResultFactory viewResultFactory)
{
_viewResultFactory = viewResultFactory;
}
public ActionResult MyAction(int page, int pageSize)
{
return
_viewResultFactory.GetResult<TeamEmployee, TeamEmployeeForm>(
_teamEmployeeRepository.GetPagedEmployees(page, pageSize));
}
}
The implementation would like this (you would need to create overloads for each of your HybridViewResult constructors):
public HybridViewResult<TSourceElement, TDestinationElement> GetResult<TSourceElement, TDestinationElement>(PagedList<TSourceElement> pagedList)
{
return new HybridViewResult<TSourceElement, TDestinationElement>(_mappingService, pagedList);
}
That way you hide the implementation from your controllers, and you don't have to depend on the container.
There are a few different points that you could inject IMappingService. http://codeclimber.net.nz/archive/2009/04/08/13-asp.net-mvc-extensibility-points-you-have-to-know.aspx is a good site for help in picking the appropriate extensibility points for .NET MVC.
If you want to stick with having this functionality be a derived ActionResult, then I think you could put the dependency in the ActionInvoker if you want to, but the Controller makes more sense to me. If you don't want the IMappingService in the Controller, you could always wrap it in a HybridViewResultFactory, and access that object in the Controller. In that case your shortcut methods would look like:
public HybridViewResult<TSourceElement, TDestinationElement> AutoMappedHybridView<TSourceElement,TDestinationElement>(PagedList<TSourceElement> pagedList, string viewNameForAjaxRequest)
{
HybridViewResultFactory.Create<TSourceElement, TDestinationElement>(pagedList, viewNameForAjaxRequest);
}
etc.
I'm not sure why you need to use an ActionResult, but if there is no reason that makes it explicitly necessary, you could create a HybridViewModel class and a HybridViewModelBinder class that is injected with the mapping service dependency.
I am assuming you want to use constructor injection, but if you have the StructureMap dependency in your UI assembly, you could access a static dependency resolver class (like Clowers said).
This question would be easier to give a definite answer to if I understood why you using an ActionResult.
It seems like you are using the action result to handle two functionalities that do not necessarily go together all the time, and that could be used separately. Also, there is not a clear indication that it needs to be in an ActionResult.
Presumably, you could (a) leverage the Automapper functionality for results other than html (ViewResult) output, and (b) you could leverage the functionality of auto-detecting ajax requests without needing to automap the model.
It seems to me like the automapping of the view model could be used to inject the view model into the controller action directly, thus removing the controller's dependency on the IMappingService. What you would need is a ModelBinder class to be injected with your IMappingService (the implementation of which I assume contains a repository or datastore type dependency).
Here is a good article explaining how to leverage model binders: http://odetocode.com/blogs/scott/archive/2009/04/27/6-tips-for-asp-net-mvc-model-binding.aspx.
Then you can overwrite the DefaultModelBinder in the classes that need to be Automapped as follows:
public ActionResult DoItLikeThis([AutoMap(typeof(MyDomainModelClass))]MyViewModelClass viewModel){
//controller action logic
}
Now, regarding the HybridViewResult, I would suggest that you handle this with an Action Filter instead. So, you could just use ActionResult or ViewResultBase as the Result type of your action method and decorate it with an action filter, i.e.:
[AutoSelectViewResult]
public ViewResultBase AndDoThisLikeSo(){
//controller action logic
}
I think overall this will be a much better solution than coupling these two functionalities to an ActionResult.