Redirection error - c#

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.

Related

How to prevent HttpGet method being called?

So i have a regular method and a HttpGet method:
//Create a new note
public ActionResult EditNote()
{
return View();
}
//Edit a selected note
[HttpGet]
public ActionResult EditNote(int id)
{
var model = NotesProcessor.LoadNote(id);
return View(model);
}
They both use the same views page, but only the HttpGet method will fill up the page with details since the user will be editing an existing note here.
So the first method should open up a page that is not filled with data.
My issue is that i don't know how to call the non HttpGet method from my views page since it will automatically call the HttpGet method and the page will give me an error:
The parameters dictionary contains a null entry for parameter 'id'
This is how I'm trying to call the regular method: (Which worked fine before adding the other one)
#Html.ActionLink("Create New", "EditNote")
And this is for the HttpGet method:
#Html.ActionLink("Edit", "EditNote", new { id = Model.Id })
Honestly i thought it would detect the non overloaded syntax and call the right method but it doesn't.
I could make another views page for creating a blank note but that's not very 'DRY'...
What should i do?
You can rename the method with no parameters:
//Create a new note
public ActionResult CreateNote()
{
return View("EditNote");
}
//Edit a selected note
[HttpGet]
public ActionResult EditNote(int id)
{
var model = NotesProcessor.LoadNote(id);
return View(model);
}
And then in your view:
#Html.ActionLink("Create New", "CreateNote")
#Html.ActionLink("Create New", "EditNote", new { id = Model.Id })
Try adding HttpGetAttribute with route template without parameters:
[HttpGet("[controller]/[action]")]
public ActionResult EditNote()
{
return View();
}
But I would argue that it is worth considering renaming EditNode() into CreateNote()

How to call controller method and pass the object from ajax call in ASP.Net MVC

I am in a learning process and working on ASP.net MVC 5 project. So, I have a model view which has other model views in it.
Parent Model View
public class FullConfigMV
{
some properties here
public ElementStyleMV Page { get; set; }
}
Now below is what I want to achieve.
Make an ajax call
once the ajax call hits the controller function set some values
now from there I need to pass that object to another controller action which will return it's view.
Step 1.
<script>
$(document).ready(function()
{
$("#tan").change(function()
{
alert(this.value);
$.ajax({
type: 'POST',
url: '/Settings/GetAvailableLocationsFor',
data: { accountId: 28462, groupId: 35},
success: function (data) {
//Whatever
},
error: function () {
DisplayError('Failed to load the data.');
}
});
});
});
</script>
After step 1 my breakpoint hits at
public ActionResult GetAvailableLocationsFor(int accountId, int groupId)
{
FullConfigMV configData = SetLoader.GetDSettings(accountId, groupId);
Utils.SiteCss = configData.CssStyles();
// NOW AT THIS PLACE I WANT TO CALL ANOTHER CONTROLLER FUNCTION
// AND PASS THE `configData Obj`
return View();
}
I know we have something like
return RedirectToAction("Index","Home");
BUT HOW TO PASS THE config Obj
The controller function that I want to call is in
Home Controller and the action name is Index
public ActionResult Index(FullConfigMV data)
{
return View();
}
If the requirement seems weird then kindly bear/humor me on this.
EDIT
After the solution suggested "use TempData " But the problem is I have two index action in my controller. I want the SECOND Index Action to get hit. But the first one is getting hit.
First:
public ActionResult Index()
{
}
Second
[HttpPost]
public ActionResult Index(FullConfigMV data)
{
}
Code used is
public ActionResult GetAvailableLocationsFor(int accountId, int groupId)
{
FullConfigMV configData = SetLoader.GetDSettings(accountId, groupId);
SimUtils.SiteCss = configData.CssStyles();
TempData["config"] = configData;
return RedirectToAction("Index", "Home");
}
You can achieve this using TempData. From https://msdn.microsoft.com/en-us/library/system.web.mvc.tempdatadictionary(v=vs.118).aspx:
"A typical use for a TempDataDictionary object is to pass data from an action method when it redirects to another action method."
There's plenty of documentation out there but simply use
TempData["config"] = configData;
In your first actionresult, and retrieve it in your Home> Index with
var configData = TempData["config"] as FullConfigMV;
I am not 100% on this, but you might be able to pass that as a route.. if not then use TempData.
Example:
public ActionResult GetAvailableLocationsFor(int accountId, int groupId)
{
FullConfigMV configData = SetLoader.GetDSettings(accountId, groupId);
Utils.SiteCss = configData.CssStyles();
return RedirectToAction("Index","Home", new { data = configData});
}
Or if above doesn't work:
public ActionResult GetAvailableLocationsFor(int accountId, int groupId)
{
FullConfigMV configData = SetLoader.GetDSettings(accountId, groupId);
Utils.SiteCss = configData.CssStyles();
TempData["config"] = configData;
return RedirectToAction("Index","Home");
}
// Index ActionResult
public ActionResult Index()
{
if (TempData["config"] != null)
{
FullConfigMV variableName = (FullConfigMV)TempData["config"];
...
return View(/*whatever you want here*/);
}
return View();
}
Let me know if that helps
For your question
After the solution suggested "use TempData " But the problem is I have two index action in my controller. I want the SECOND Index Action to get hit. But the first one is getting hit what you can do is based on some data which you can set in TempData you can call return Index(data) from your first index method, you can get data through Tempdata or some other variable. This will call you second index method through first index method.

How to move from one controller to next in Asp.net MVC

I have a small asp.net mvc application, where I have few actionlinks in my Index.html and following code in home controllder.
When user click on Edit ActionLink, it takes the control to HomerController's "Edit" acton method (which is of type httpGet). The view for that action method is Edit.cshtml. If I do some data manipulation in that EDIT view..and try to POST that data, What should I do ? Write another Edit action method (httpPost) in same HomeController? In that case my home controller will grow bigger right?
If I need to write seperate controller for this purpose, how do I transfer the control to that controller? ( I mean how do I attach my newly created Edit controller to Edit view ?)
List<StateCity> stateCityList = new List<StateCity>();
public ActionResult Index()
{
StateCity sc1 = new StateCity() { Id = 1, StateName = "Dallas", Cities = new List<string>() { "ab", "cd" } };
StateCity sc2 = new StateCity() { Id = 2, StateName = "Austin", Cities = new List<string>() { "ef", "gh" } };
stateCityList.Add(sc1);
stateCityList.Add(sc2);
return View(stateCityList);
}
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
public ActionResult Edit(int id)
{
return View();
}
As was told already, add one more action with HttpPost attribute and your ViewModel as parameter.
Your controller won't grow if you will follow SRP principle. In my opinion HomeController shouldn't have such actions like Edit. I would move them to corresponding Controller (for example ArticleController). But HomeController would have actions like Index, About.
#Html.ActionLink("Edit Article", "Edit", "Article", new { id= 587 }, new { id = "linkId" })
Take a look here for more details: https://msdn.microsoft.com/en-us/library/dd504972(v=vs.118).aspx
You can create Action for HTTP POST using attribute:
[HttpPost]
public ActionResult Edit(parameters here){}
Here is a nice example of forms in MVC
Typically for an edit you'd have two actions.
One for loading the edit page:
[HttpGet]
public ActionResult Edit(int id)
And one for submitting the form (performing the edit):
[HttpPost]
public ActionResult Edit(YourModel model)
If you're concerned that your controller methods are getting too large, consider abstracting the logic/saving code in another layer of your application.

MVC Calling a view from a different controller

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.

How to pass a value from a controller method to an another?

I'm beginner in MVC3, and I want to get a value from an another controller's method. Here the two methods:
[HttpPost]
public ActionResult Create(TennisClub tennisclub)
{
if (ModelState.IsValid)
{
db.TennisClubs.Add(tennisclub);
db.SaveChanges();
return RedirectToAction("AssignManager");
}
return View(tennisclub);
}
[HttpPost]
public ActionResult AssignManager(Manager manager)
{
}
So, when I'm creating a new tennis club, Immediately I would like to assign a manager to it... For that I need the primary key "ID".
So my question is: How to get this ID in my "AssignManager" method ? Thanks in advance
You cannot redirect to an action decorated with the [HttpPost] attribute. That's not how a redirect works. A redirect means that you are sending a 301 HTTP status code to the client with the new Location header and the client issues a GET request to this new location.
So once you remove the [HttpPost] attribute from your AssignManager action you could pass the id as parameter:
return RedirectToAction("AssignManager", new { id = "123" });
and then:
[HttpPost]
public ActionResult AssignManager(int id)
{
}
Basically, you need to have a GET AssignManager method, too, which would have a parameter telling it to which TennisClub the manager should be assigned:
[HttpGet]
public ActionResult AssignManager(int tennisClubId)
{
// here, you will want to return AssignManager view
}
And when redirecting to AssignManager from Create, you can specify the id of TennisClub:
return RedirectToAction("AssignManager", new { tennisClubId = tennisclub.Id });
return RedirectToAction("AssignManager", new { id = tennisclub.Id });
Also you need to remove the [HttpPost] attribute from your action
public ActionResult AssignManager(int id) {
//...
}

Categories

Resources