My project structure is like:
Controllers/ArticlesController.cs
Controllers/CommentsController.cs
Views/Articles/Read.aspx
Read.aspx takes a parameter say "output", which is the details of the article by id and its comments, passed from ArticlesController.cs
Now I want to write then read the comment:: write() & Read() funct in CommentsController.cs
For reading the article with its comments, I want to call Views/Articles/Read.aspx from CommentsController.cs by passing output parameter from CommentsController.cs
How can I do this?
UPDATE
Code Here:
public class CommentsController : AppController
{
public ActionResult write()
{
//some code
commentRepository.Add(comment);
commentRepository.Save();
//works fine till here, Data saved in db
return RedirectToAction("Read", new { article = comment.article_id });
}
public ActionResult Read(int article)
{
ArticleRepository ar = new ArticleRepository();
var output = ar.Find(article);
//Now I want to redirect to Articles/Read.aspx with output parameter.
return View("Articles/Read", new { article = comment.article_id });
}
}
public class ArticlesController : AppController
{
public ActionResult Read(int article)
{
var output = articleRepository.Find(article);
//This Displays article data in Articles/Read.aspx
return View(output);
}
}
To directly answer your question if you want to return a view that belongs to another controller you simply have to specify the name of the view and its folder name.
public class CommentsController : Controller
{
public ActionResult Index()
{
return View("../Articles/Index", model );
}
}
and
public class ArticlesController : Controller
{
public ActionResult Index()
{
return View();
}
}
Also, you're talking about using a read and write method from one controller in another. I think you should directly access those methods through a model rather than calling into another controller as the other controller probably returns html.
You can move you read.aspx view to Shared folder. It is standard way in such circumstances
I'm not really sure if I got your question right. Maybe something like
public class CommentsController : Controller
{
[HttpPost]
public ActionResult WriteComment(CommentModel comment)
{
// Do the basic model validation and other stuff
try
{
if (ModelState.IsValid )
{
// Insert the model to database like:
db.Comments.Add(comment);
db.SaveChanges();
// Pass the comment's article id to the read action
return RedirectToAction("Read", "Articles", new {id = comment.ArticleID});
}
}
catch ( Exception e )
{
throw e;
}
// Something went wrong
return View(comment);
}
}
public class ArticlesController : Controller
{
// id is the id of the article
public ActionResult Read(int id)
{
// Get the article from database by id
var model = db.Articles.Find(id);
// Return the view
return View(model);
}
}
It is explained pretty well here: Display a view from another controller in ASP.NET MVC
To quote #Womp:
By default, ASP.NET MVC checks first in \Views\[Controller_Dir]\,
but after that, if it doesn't find the view, it checks in \Views\Shared.
ASP MVC's idea is "convention over configuration" which means moving the view to the shared folder is the way to go in such cases.
Related
I have two controllers:
AaController have a view named Index and a database context that we can call for dbContextAa
RrController also has its own database, which we can call for dbContextRr and an action method GetData ()
What I wonder is; how do I do if I want to take the data from the method GetData () and display it in the index view.
Previously, I tested with Temp Data and Sessions without any result. I had no value at all in the view from RrDatabase.
If I understand it right, I have to use Dependency Injections.
So here I have created dependency injection (not sure if it is correct)
AaController:
public class AaController : Controller
{
private RrController rc;
public AaController(RrController rr, AaContext aaContext)
{
rc = rr;
_aaContext = aaContext;
}
public IActionResult Index()
{
var dataFromRr= rs.GetData();
ViewBag.rrData = dataFromRr;
return View();
}
RrController:
public class RrController : Controller
{
private readonly RrContext _rrContext;
public RrController(RrContext rrContext)
{
_rrContext = rrContext;
}
public IActionResult GetData()
{
var data = _rrContext.RrData.Count(x => x.Pid == x.Pid);
return RedirectToAction("Index", "Aa", data );
}
Instead of the value of the view I get Microsoft.AspNetCore.Mvc.ViewResult.
How do I solve this?
Because GetData method returns an IActionResult type response(specifically MVC action result, RedirectResult).
Ideally what you should be doing is, extract the code which gives you the relevant data to a method in a separate class, which you can use in multiple places as needed (ControllerA and ControllerR).
Let's create an interface and a class to implement it.
public interface IRService
{
int GetRDataCount(int pId);
}
public class RService : IRService
{
private RrContext _rrContext;
public RService (RrContext rrContext)
{
this._rrContext=rrContext;
}
public int GetRDataCount(int pId)
{
return _rrContext.RrData.Count(x => x.Pid == pId);
}
}
You can inject IRService implementation to your controllers and call the GetRDataCount method to get the int value.
public class AaController : Controller
{
private IRService rService;
public AaController(IRService rService)
{
this.rService=rService;
}
public IActionResult Index()
{
int countVal = this.rService.GetRDataCount(234); // pass actual PId
ViewBag.rrData = countVal;
return View();
}
}
Make sure to add the IRservice to RService dependency mapping in your Startup class.
Now wherever you want to get the RData count for a specific pId, you can inject an IRservice implementation and use it (Do it in your RController as well)
There are a few different approaches depending on the structure of your projects and how (and if) they can communicate with one another.
What I wonder is; how do I do if I want to take the data from the
method GetData () and display it in the index view.
Your GetData() method is actually attempting to return a View on it's own, however it looks like what you want to do is actually just return the data (i.e. the count) and pass that to your other action:
public int GetData()
{
var data = _rrContext.RrData.Count(x => x.Pid == x.Pid);
// This will pass a parameter that contains your count
return RedirectToAction("Index", "Aa", new { count = data });
}
You need to actually pass your data to the View as a "model". This can easy be done by simply decorating your View with the type of data that you are going to be passing in:
public IActionResult Index(int count)
{
// The count parameter will have the current result of your previous call in it
return View(count);
}
Then you would just need to tell your actual View how you wanted to present the data (i.e. what type it was) via the #model directive at the top of the view:
#model int
There were #Model records found.
Consider A Service
A better implementation would be to consider using a service that decouples your logic from any one specific controller. This would allow you to simply instantiate the service and call it from wherever it was needed as Shyju's answer indicates.
I am writing an application using ASP.NET MVC 5 using c#. I have a need to add a global menu on the upper right hand side of the application. I was advised other SO to use action with ChildActionOnly attribute.
So here is what I have done.
I created a BaseController like this
public class BaseController : Controller
{
[ChildActionOnly]
public ActionResult ClientsMenu()
{
using (SomeContext db = new SomeContext())
{
return PartialView(db.Database.SqlQuery<Client>("SELECT * FROM clients").ToList());
}
}
}
Then I inherited all my controllers from BaseController like so
public class TasksController : BaseController
{
public ActionResult Index(int ClientId)
{
...
return View();
}
public ActionResult Show(int SurveyId)
{
...
return View();
}
}
To render the ClientsMenu in my layout I added the following code
#Html.Action("ClientsMenu", "Menus")
Now when I run my application I get the following error
The controller for path '/Tasks/Index' was not found or does not implement IController.
When I remove #Html.Action("ClientsMenu", "Menus") from the layout everything works fine but the global menu does not show of course.
What can I do to resolve this issue?
Updated
Here is what I have done after the feedback I got from the comments below
public class TasksController : Controller
{
[ChildActionOnly]
public ActionResult ClientsMenu()
{
using (SomeContext db = new SomeContext())
{
return PartialView(db.Database.SqlQuery<Client>("SELECT * FROM clients").ToList());
}
}
public ActionResult Index(int ClientId)
{
...
return View();
}
public ActionResult Show(int SurveyId)
{
...
return View();
}
}
but still the same error
Take ClientMenus Action out of the BaseController and put it into its own controller MenusController. You can then call that controller from your Views.
#Html.Action("ClientsMenu", "Menus")
In your example you don't have a MenusContoller which is what #Html.Action("ClientsMenu", "Menus") is looking for.
The Phil Haacked - Html.RenderAction and Html.Action article linked to by the other post provided a good example for you to follow.
I have a target to create a nested architecture of view/controller. For example:
we have few areas in webpage. All this areas managed by its own controller. In another words I need to create independend pages and then join them in one page.
Please see the image:
So is it possible and where I can read about it? Thanks.
Yes you can achieve this by simply calling
#Html.Action("ActionName", "ControllerName");
Example:
in your case you can do like this
Controller1
public ActionResult View1()
{
return View("View1");
}
Controller2
public ActionResult View2()
{
return View("View2");
}
Controller3
public ActionResult View3()
{
return View("View3");
}
Calling in main page:
#Html.Action("View1", "Controller1");
#Html.Action("View2", "Controller2");
#Html.Action("View2", "Controller2");
call these in different section of main page wherever you want.
You can use #Html.Action() to render child views from different controllers
public class FirstController : Controller
{
public ActionResult Index()
{
return View();
}
}
public class SecondController : Controller
{
[ChildActionOnly]
public ActionResult Method1()
{
return PartialView();
}
}
public class ThirdController : Controller
{
[ChildActionOnly]
public ActionResult Method2(int ID)
{
return PartialView();
}
}
Index View (FirstController)
....
#Html.Action("Method1", "Second")
#Html.Action("Method2", "Third", new { ID = someValue })
You can also use #{ Html.RenderAction(); which is more efficient is you are generating a lot of html.
Taking first steps with ASP.NET MVC, I am trying to create a simple (and typical) article-with-comments page: under an article itself there should be a form enabling an user to post a comment to the article.
I created a partial view for the submit form and CommentController with following methods:
public ActionResult Add(int entryId);
[HttpPost]
public ActionResult Add(Comment comment);
Then, under the article in a view of HomeController:
<div class="add-comment">
#{ Html.RenderAction("Add", "Comment", new { entryId = Model.EntryId }); }
</div>
The form renders properly and the adding procedure actually works (comment gets saved into database), but after redirecting back to the article InvalidOperationException is thrown, with Html.RenderAction (the one shown above) highlited in debugger:
System.InvalidOperationException: Child actions are not allowed to perform redirect actions.
Why does it happen?
Here's the code for CommentController methods:
public ActionResult Add(int entryId)
{
var comment = new Comment { EntryId = entryId };
return PartialView(comment);
}
[HttpPost]
public ActionResult Add(Comment comment)
{
if (ModelState.IsValid)
{
comment.Date = DateTime.Now;
var entry = db.Entries.FirstOrDefault(e => e.EntryId == comment.EntryId);
if (entry != null)
{
entry.Comments.Add(comment);
db.SaveChanges();
return RedirectToAction("Show", "Home", new { id = entry.EntryId });
}
}
return PartialView(comment);
}
Or maybe should I even take a diffrent approach?
Add HttpGet on the other Add action
You should/could be using RenderPartial instead of RenderAction:
Html.RenderPartial("YourPartialView", new Comment { EntryId = Model.EntryId });
There appears to be no need to use your action method if all you are doing is instantiating a model that you already have the ID too.
I ve got 2 seperate controller sheets not just action methods..
MY First Controller:
namespace TestLokal.Controllers
{
public class BOUNCEController : Controller
{
BOUNCEDataDataContext db = new BOUNCEDataDataContext();
//
// GET: /BOUNCE/
[Authorize]
public ActionResult Index()
{
ViewData["Bouncers"] = new SelectList( db.Bouncers.Distinct(), "bouncer_id", "bouncer_name");
return View();
}
}
}
MY Second Controller:
namespace TestLokal.Controllers
{
public class DopplerController : Controller
{
//
// GET: /Doppler/
[Authorize]
public ActionResult Index()
{
elementmodel dop = new elementmodel();
ViewData["Dopplers"] = new SelectList( dop.BouncerList.Distinct(), "bouncer_id", "bouncer_name");
return View();
}
}
}
i wanna transfer data from first to second by using this model:
MY Model:
public class elementmodel
{
public IEnumerable<Bouncers> BouncerList { get; set; }
}
How can i achieve this?
You can use TempData for this.
See http://msdn.microsoft.com/en-us/library/dd394711(v=vs.90).aspx (passing data between action methods)
First of all ... are you sure you need two separate controller that has same data? In my humble opinion each controller should be responsible for handling his own group of models. If you need to have the same data in both of controllers maybe you should create some base class?
public class MyBaseController : Controller
{
//shared fields & methods for example datacontext
}
public class DopplerController : MyBaseController
{
}
public class BounceController : MyBaseController
{
}
but if I'm wrong and one of your controller just need results of work from another controller you can use Coockies, Session.
2 things come immediately to mind.
You can store the list of bouncers in the View as a JSON object
that gets POSTed off to the other controller.
You can use TempData to store it between requests. eg: http://msdn.microsoft.com/en-us/library/dd394711.aspx