Routing issue in Asp.net MVC (404 Error) - c#

Hello People i have a list of Customers (with Name and Id) that i have hardcoded in my Customers Controller as: Click to view Customer Controller i am sending list to a view named Customer to display all the three customers with their names as links using following code :
#model IEnumerable<App.Models.Customer>
#{
ViewBag.Title = "Customer";
}
#foreach (var customer in Model)
{
<li>
#Html.ActionLink(customer.Name, "Details", "Customers", new { id = customer.Id }, null)
</li>
}
It displays list of customers but problem is when i click any customer name(Which is a link) it gives a HTTP 404 error as :Click to see Error details
i have made a Details method in Costumers Controller as
[HttpPost]
public ActionResult Details(int id)
{
var customer = GetCustomers().SingleOrDefault(x => x.Id == id);
if (customer == null)
{
return HttpNotFound();
}
return View(customer);
}
that recieves id of the customer name being clicked and simply displays its name again but it doesnt works and gives 404 error i have debugged the code and what i have found that control never reached the details Method in Controller, is the issue in ActionLink or what ?
Help is highly appreciated thank you

The method should be HttpGet:
[HttpGet]
public ActionResult Details(int id)
{
var customer = GetCustomers().SingleOrDefault(x => x.Id == id);
if (customer == null)
{
return HttpNotFound();
}
return View(customer);
}

Related

.NET core MVC that uses the EF Framework - Pass the data from one controller another

I am doing the first time Web App development using the ASP.NET Core MVC with the EF. I have two tables Customer and Inventory in the SQL Server database.
I did the Scaffold-DbContext and generated the models/controllers and views for both the tables. My requirement is that from the Customer index page on the each customer on the grid I added a select button, when this select button is clicked I need to pass the selected customer data to the inventory controller and show the customer select on top of the inventory index page and below I showing the grid with the inventory table.
I am using TempData here. On the customer table I added the select button
<td>
<a asp-action="passToInventory" asp-route-id="#item.CustomerId">Select</a>
</td>
CustomersController:
public async Task<IActionResult> passToInventory(int? id)
{
if (id == null)
{
return NotFound();
}
Customer customer_data = await _context.Customers.FindAsync(id);
TempData["custdata"] = customer_data;
return RedirectToAction("Index", "Inventories");
}
Now on the InventoriesController in the Index method, I made the change like to access the TempData passed
public async Task<IActionResult> Index()
{
Customer cust_data = TempData["custdata"] as Customer;
return View(await _context.Inventories.ToListAsync());
}
I tried to create a model class so I can use both the customer and the inventory list on the Inventory Index page like below:
public class CustomerInventoryCollectionDataModel
{
public Customer CustomerData { get; set; }
public List<Inventory> Inventorys { get; set; }
}
I couldn't get the data retrieved on the Index page of the Inventory
#model IEnumerable<JAXSurplusMouseApp.Models.CustomerInventoryCollectionDataModel>
//show the selected customer data from the previous page
unable to access the customer data here and show it
//table for the Inventory list
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Inventorys.QuantityAvailable)
</td>
</tr>
}
Please suggest how I can retrieve both the models in the same view and show them in the same page. Any help is greatly appreciated
action
public async Task<IActionResult> Index()
{
Customer custData = TempData["custdata"] as Customer;
var inventories =await _context.Inventories.ToListAsync();
var model= new CustomerInventoryCollectionDataModel
{
CustomerData = custData,
Inventorys =inventories
};
return View(model);
}
view
#model JAXSurplusMouseApp.Models.CustomerInventoryCollectionDataModel
//show the selected customer data from the previous page
// using #Model.CustData
#Html.DisplayFor(model => model.CustData.Name)
....and so on
//table for the Inventory list
<tbody>
#foreach (var item in Model.Inventorys)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.QuantityAvailable)
</td>
</tr>
}
but I am afraid you will need to serialize customer , so it seems more efficient for me to use this
public async Task<IActionResult> passToInventory(int? id)
{
if (id == null)
{
return NotFound();
}
return RedirectToAction("Index", "Inventories",new { id });
}
action
public async Task<IActionResult> Index(int? id)
{
if(id==null) return ...error
Customer custData = await _context.Customers.FindAsync(id);
var inventories =await _context.Inventories.ToListAsync();
var model= new CustomerInventoryCollectionDataModel
{
CustomerData = custData,
Inventorys =inventories
};
return View(model);
}
or if you need to use TempData customer for some reason, you can use this in the code
var customerData = await _context.Customers.FindAsync(id);
TempData["custdata"] =
System.Text.Json.JsonSerializer.Serialize( customerData;)
//index
Customer custData = System.Text.Json.JsonSerializer.Deserialize<Customer> (
TempData["custdata"].ToString());
PS
I don't know maybe you have more code in passToInventory action then it is posted, but the way it looks now IMHO you could go immediately to index
<td>
<a asp-action="Index" asp-controller="Inventories" asp-route-id="#item.CustomerId">Select</a>
</td>

using same ActionLink call two function master and details in same Controller c#

i am new in MVC5. I am trying to load master and details data using ActionResult for retrieving master data and JsonResult for Details retrieving data single click in ActionLink.
public JsonResult getOrderDetails(int? id)
{
List<OrderDetail> OrderDetail = new List<OrderDetail>();
OrderDetail = db.OrderDetails.Where(a => a.OrderID==id).OrderBy(a => a.OrderDetialsID).ToList();
return new JsonResult { Data = OrderDetail, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
OrderMaster OrderMaster = db.OrderMasters.Find(id);
return View(OrderMaster);
}
No, this won't work. It will throw an exception explaining that a link must point to exactly one resource (a.k.a one Controller, one Action). Also, that's not how you normally think of doing it in MVC. This is not WebForms where you load the master and detail separately.
You should be doing something like this instead:
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var orderMaster = db.OrderMasters
.Where(om => om.OrderMasterId == id)
.Include(om => om.OrderDetails) // include the details here
.Single();
return View(orderMaster);
}

Fix error with route url in Asp.Net MVC 5

I have this datamodel:
so one Project has many Milestones.
What I did is this: when I go to the detail of a specific Project, I can add/create Milestones for it, like in the picture:
When I click "Create Milestone" I navigate to the View where I can create the milestone for this specific Project, when I click save, it will automatically be saved for this project. Here the HttpPost method:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateMilestone([Bind(Include = "Name,Description,FromDate,DueDate,Finished,ProjectID")] Milestone milestone, int? id)
{
if (ModelState.IsValid)
{
var forProject = db.Projects.Where(x => x.ID == id).FirstOrDefault();
if (forProject != null)
{
milestone.Project = forProject;
db.Milestones.Add(milestone);
db.SaveChanges();
return RedirectToAction("Details", forProject);
}
else
{
return HttpNotFound();
}
}
else
{
return View(milestone);
}
}
Here is a screenshot of the CreateMilestone View, and focus on the url (it's localhost:xxxxx/Projects/CreateMilestone/3002). The id parameter in the CreateMilestone method is for the Project ID, and in the url the id (3002) is also for the project.
I'm trying to make the app to navigate to the Details view of that specific Project I just added a milestone, which I do actually!
And as you see it works:
But: look at the url! Instead of being localhost:xxxxx/Projects/Details/3002 it is: http://localhost:55623/Projects/Details/3002?Milestones=System.Collections.Generic.HashSet%601%5BTheProjectManager.Models.Milestone%5D&Users=System.Collections.Generic.HashSet%601%5BTheProjectManager.Models.ApplicationUser%5D&ProjectName=Testing&Description=Testing%20data
So, how can I make the url be like: localhost:xxxxx/Projects/Details/3002 when I navigate to the details view after adding a new milestone?
UPDATE:
the Get Details:
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Project project = db.Projects.Find(id);
if (project == null)
{
return HttpNotFound();
}
return View(project);
}
and the Get CreateMilestone:
public ActionResult CreateMilestone(int? id)
{
var forProject = db.Projects.Where(x => x.ID == id).FirstOrDefault();
if (forProject != null)
{
return View();
}
else
{
return HttpNotFound();
}
}
In this line
return RedirectToAction("Details", forProject);
you are redirecting to Details action and as parameter you send "Project" object which is serialized to query string. Instead of full object you can use only id. But you also need to change Details action to accept int as parameter instead of Project class
Instead of return RedirectToAction("Details",forProject);
try this return RedirectToAction("Details", new { id = id });

Edit order details in ASP.NET MVC

The standard way to edit a record in ASP.NET MVC is the following:
//
// GET: /Movies/Edit/5
public ActionResult Edit(int id = 0)
{
Movie movie = db.Movies.Find(id);
if (movie == null)
{
return HttpNotFound();
}
return View(movie);
}
//
// POST: /Movies/Edit/5
[HttpPost]
public ActionResult Edit(Movie movie)
{
if (ModelState.IsValid)
{
db.Entry(movie).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
}
The problem is that I need to edit order details (1 order, many details) and therefore based on two IDs (the order and the product ones). It does not work (I cannot get an OrderDetail item as action parameter). How can I solve this problem?
Thanks.
//
// GET: /Orders/EditDetails
public ActionResult EditDetails(int id, string productID)
{
OrderDetail od = GetOrderDetail(id, productID);
return View(od);
}
//
// POST: /Orders/EditDetails
[HttpPost]
public ActionResult EditDetails(OrderDetail od)
{
if (ModelState.IsValid)
{
context.Entry(od).State = EntityState.Modified;
context.SaveChanges();
return RedirectToAction("Index");
}
return View(od);
}
EDIT: Here is the code requested by Shimmy:
#using (Html.BeginForm("EditDetails", "Orders", FormMethod.Post))
{
#Html.LabelFor(m => m.quantity)
#Html.TextBoxFor(m => m.quantity)
#Html.LabelFor(m => m.productID)
#Html.DropDownListFor(m => m.productID, new SelectList((IEnumerable)ViewBag.productList, "productID", "fullProductName"))
#Html.HiddenFor(model => model.orderID)
}
Make sure the OrderDetail.Id itself as well as the OrderDetail.OrderId and the OrderDetail.MovieId properties are all present in the form as a hidden field.
In that way, when you post it back to the server you have track on what Movie and Order this OrderDetail is of, in the action.

How to pass data between controller and view in .Net MVC?

What I want is that when the schedule form is loaded for the first time (or without data passing), it shows a list of MovieName. When I click on one MovieName, its ID is sent back to controller as an input of a SQL query, then the result is passed back to the view.
Here are what I've done. But I think when I click on the ActionLink, the controller doesnt handle the data passed back as it is not HttpPost. Also, I dont know how to show the new data back in view. Please help!
ScheduleController.cs
[HttpGet]
public ActionResult Index()
{
var schedules = db.Schedules.Include(s => s.Movie)
.OrderByDescending(s => s.Movie.MovieName)
.ToList();
return View(schedules);
}
[HttpPost]
public ActionResult Index(int MovieID)
{
//return ("Clicked");
var schedules = (from s in db.Schedules
orderby s.ShowDate
select s).ToList();
return View(schedules);
}
Schedule/Index
#using Booking_Ticket_Management_System.Models;
#model IEnumerable<Schedule>
#{
ViewBag.Title = "Schedule";
}
<h2>Choose movies</h2>
#using (Html.BeginForm())
{
<div>
#foreach (Schedule schedule in #Model)
{
#Html.ActionLink(schedule.Movie.MovieName, "Index", "Schedule", new { MovieID = schedule.MovieID},null)
<br />
}
<br />
</div>
}
Ideally listing and detail view are always separate. i suggest you to make both the view separate.
[HttpGet]
public ActionResult Index()
{
var schedules = db.Schedules.Include(s => s.Movie)
.OrderByDescending(s => s.Movie.MovieName)
.ToList();
return View(schedules);
}
[HttpGet]
public ActionResult Movie(int id)
{
//return ("Clicked");
var schedules = (from s in db.Schedules
Where s.MovieId == id
orderby s.ShowDate
select s).ToList();
return View(schedules);
}
As you do not pass the movieId in your query
Try this :
[HttpPost]
public ActionResult Index(int MovieID)
{
//return ("Clicked");
var schedules = (from s in db.Schedules
where s.MovieId==MovieId
orderby s.ShowDate
select s).ToList();
return View(schedules);
}
But what I will suggest to you is to create another Action and another view to display only the movie clicked.
And as you just want to Get not to modify the value inside the Database. You have to use the [HttpGet]
[HttpGet]
public ActionResult Detail(int MovieID)
{
//return ("Clicked");
var schedule = (from s in db.Schedules
where s.MovieId==MovieId
orderby s.ShowDate
select s).FirstOrDefault();
return View(schedule);
}
And in your Detail view created
#using Booking_Ticket_Management_System.Models;
#model Schedule
In your Index view you must change the ActionLink to:
#Html.ActionLink(schedule.Movie.MovieName, "Detail", "Schedule", new { MovieID = schedule.MovieID},null

Categories

Resources