View error when invoking methods from base controller in MVC 5 - c#

I have the following controllers
public class StoreController : Controller
{
public ActionResult Index()
{
var model = new SomeViewModel();
return View(model);
}
}
and
public class SofiaStoreController : StoreController
{
public ActionResult GetIndex(string city)
{
return base.Index();
}
}
When invoking the base Index method from the derived class I get this error:
The view 'getindex' or its master was not found or no view engine
supports the searched locations. The following locations were
searched:
Seems the GetIndex() method looks for a view in the derived controller's view folder by default even though no View() method is called but since there isn't such the error occurs.
Any idea why the method implicitly looks for a view and how to overcome the error ?
EDIT: After spending some time research the problem I came across these two posts: http://howtoprogram.eu/question/asp-net-c-asp-net-mvc-inherited-controller-using-base-view,2445 and http://www.davidwhitney.co.uk/Blog/2010/01/19/asp-net-mvc-view-engine-that-supports-view-path-inheritance/ Seems that controller inheritance is not that popular or straightforward decision. The solutions to my problem could be :
1. Not to use controller inheritance
2. Creating a custom view engine as shown in the second link (advanced)
3. As other people mentioned bellow - using full path to the view or RedirectToAction also could work

It does look for a view based on the Action method name you originally called. You can always override this behavior if you use the overloaded View() method that accepts the view name/path:
public class StoreController : Controller
{
public ActionResult Index(string viewName = "Index")
{
var model = new SomeViewModel();
return View(viewName, model);
}
}
public class SofiaStoreController : StoreController
{
public ActionResult GetIndex(string city)
{
return base.Index();
}
}

Related

ASP.NET MVC Attribute routing parameter issue

I have the following code
public class BooksController : Controller
{
[Route("/Books/{id?}")]
public IActionResult Index(string id)
{
return View(id);
}
}
My problem is that when I try to enter the parameter it is (as it seems) considered as controller's action so I keep getting this exception.
I need somebody to explain what am I doing wrong.
If you want to pass some parameter to a view as a string you can make this like below:
public class BooksController : Controller
{
[Route("/Books/{id?}")]
public IActionResult Index(string id)
{
if (string.IsNullOrEmpty(id))
id = "default_value";
return View((object)id);
}
}
If the string type is passing to the View() call without casting to object it will be interpreted as a view name.
And the view model data should be declared as
#model string;
<h2>#Model</h2>
Try changing the route as given below -
[Route("Books", Name = "id")]

Where to put common code in view controller View functions?

In one of the controllers, every view has a fixed preprocessing. Is there a better way of doing this, instead of the below code; such that SomeFunctionAsync works without writing that line before return View() for all functions with return View() in this controller? I also have some ajax post functions.
public async Task<ActionResult> View1()
{
await SomeFunctionAsync();
return View();
}
public async Task<ActionResult> View2()
{
await SomeFunctionAsync();
return View();
}
In other words, at the end I want to be able to write the following with having the same effect
public async Task<ActionResult> View1()
{
return View();
}
public async Task<ActionResult> View2()
{
return View();
}
If Action Filter as suggested by Varun doesnt suits you, you can try another way.
Create a parent View of all the view. In the action method for your parent view. Call this Method SomeFunctionAsync(). Thus, parent view will be called for all of yours views and the required method will be executed
You can create a base class for your controller, and have have each view in your code call a generic method. I use GetView as a method name (or you could override the existing ones).
Like so:
public class MyControllerBase : Controller {
public Task<ActionResult> GetView() {
yourCommonMethod();
return View();
}
public Task<ActionResult> GetView(string viewName) {
yourCommonMethod();
return View(viewName);
}
public Task<ActionResult> GetView(object model) {
yourCommonMethod();
return View(model);
}
public Task<ActionResult> GetView(string viewName, object model) {
yourCommonMethod();
return View(viewName, model);
}
}
Then in your controller, inherit from that:
public class MyController : MyControllerBase {
public async Task<ActionResult> View1()
{
return GetView();
}
}
If the common method is the same for all controllers and has no controller-specific dependencies, it could be that simple. However, you may want to look at using generics as well:
public class MyControllerBase<T> : Controller {
// base class stuff here based on type T's interface
}
public class MyController : MyControllerBase<MyController> {
// regular class here, sending MyController to the base
}
These are pretty much building blocks of OOP. You may do well to get a book that covers the basics of OOP and work through this type of stuff.
There are tow ways :
Use a single Action with different views like return View("View1") or retrun View("View2"), you can make if else conditions there so you will call your function at a single place.
If you want to go with your current procedure(not recommended) then you have to use Action Filter attribute and decorate it on Controller level then every action would execute your logic before execution of your Action

Can two different controllers access a single view in mvc?

I have two different controllers and I want both of them to use a Common View.
Is that possible?
Thanks in advance!!!!
Yes.Mention the view full path in the View method.
public class UserController : Controller
{
public ActionResult ShowUser()
{
return View();
}
}
public class AccountController : Controller
{
public ActionResult ShowAccount()
{
return View("~/Views/User/ShowUser.cshtml");
}
}
If the name of your Views are same in both the controllers, You can keep the Common view under the Views/Shared directory and simply call the View method without any parameter. The View name should be same as the Action method name.
public class UserController : Controller
{
public ActionResult ShowUser()
{
return View();
}
}
public class AccountController : Controller
{
public ActionResult ShowUser()
{
return View();
}
}
Assuming you have a View called ShowUser.cshtml under Views/Shared folder.

Can a controller manage more than one model?

I want my controller to put modelA into viewA and modelB into viewB.
From what I know a controller can be associated with only one view using only one model.
Correct me please if I'm wrong.
A Controller-action can only produce 1 View at a time.
But it is possible to build some conditional logic into the Controller and decide which View(s) to
show.
a controller can be associated with only one view using only one model
No, a CRUD controller normally associates with 1 Model and produces List/Edit/Delete/Create Views.
So multiple Views is quite normal, so is multiple ViewModels. And a ViewModel often combines information form more than 1 Model entity.
I want my controller to put modelA into viewA and modelB into viewB.
BO classes:
class modelA{
...
}
class modelB{
...
}
Controller code:
...other using statements...
using MyProj.DAL;
using MyProj.BO;
public class MyController:Controller
{
//Create object of your Data Access Layer's MyDAL functionality
MyDAL DALobj = new MyDAL();
public ActionResult viewAList(){
modelA mobj = DALobj.FetchObjFromDB();
return View(mobj);
}
public ActionResult viewACreate(modelA newobj){
...check if modelstate is okay and tweak your model object here...
DALobj.SendDataToDB(newobj);
return RedirectToView("some other view | index");
}
public ActionResult viewBList(){
...same as viewAList() but with modelB this time...
}
public ActionResult viewBCreate(){
...same as viewACreate() but with modelB this time...
}
}//controller ends here
I guess what is asked is:
From what I know a controller can be associated with only one view
using only one model.
I translate it as :
There can be only one view consuming one model associated with a particular controller.
which I think looks like this:
public class PersonController : Controller
{
static List<Person> people = new List<Person>();
public ActionResult Index()
{
return View(people);
}
public ActionResult Details(Person person)
{
return View(person);
}
public ActionResult Create()
{
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Person person)
{
if (!ModelState.IsValid)
{
return View("Create", person);
}
people.Add(person);
return RedirectToAction("Index");
}
}
Here is one controller, four Actions, one model. So, One Controller is associated with one model, but not only view (which you can get; by removing any other two Functionalities(not functions, Create-functionality has two functions, one for GET & POST each))
Now, question arises, can you use multiple models in 1 controller?
Answer: Yes. I just showed it in first code example.
Question: Can there be two+ models in 1 controller?
Answer: yes. Create a wrapper model, put other two models in it. and BAM!
How?
(Of course. sigh!)
MODEL | BO:
public class BigObjClass{
public A a;
public B b;
}
public class A{
public int serial{get;}
public int age{get;set;}
}
public class B{
public string Name{get;}
public string Address{get;set;}
}
Now, as in very first code example, use BigObjClass's object and pass it to the views.
Please let me know if any of this makes sense to you.

View to String from another controller

i have done as Vdex suggested here:
https://stackoverflow.com/a/5801502/973485
And used the RenderPartialToString method he found. And it works perfectly like this:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Test()
{
string t = ViewToString.RenderPartialToString("Index", null, ControllerContext);
return Content(t);
}
}
But if i want to render the Home > Index from another Controller, i get:
Value cannot be null.
Parameter name: controllerContext
Like this:
public class FooController : Controller
{
public ActionResult Index()
{
string t = ViewToString.RenderPartialToString("Index", null, new HomeController().ControllerContext);
return Content(t);
}
}
Is there any way to pass a View from another Controller to a string? I have tried many different methods, and it all of them fails at the ControllerContext. Many thanks!
Update: Why i need to do this:
Imagine i have a website full of widgets, the amount of widgets on each page is dynamic, so i cannot hardcode them in my cshtml file. But in that file there are different areas defined where the widgets gets printet out. To print out these widget i have a list of IWidgetController wich contains alle the different Widgets available, and the interface sais that they need to containe a ActionResult for edit, new and view. example of widgets: CalenderController, NewsController, GalleryController and so on... So in those areas i need to print out the content of each of those Controllers. Now i could also load the URLHTML but i figured doing it from the inside would be faster... right?
Try this:
string t = ViewToString.RenderPartialToString("Index", null, this.ControllerContext);
Anyway, why do you need to convert to a string?

Categories