我正在尝试学习MVC,我正在跟随位于here的Contoso大学教程
我已成功构建了一个包含学生,课程和注册数据库的项目。目前我有3个型号,
和2个控制器
目前,我的路由是您使用MVC项目获得的默认路由。
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
这适用于根据StudentID
获取学生详细信息所以网址
http://localhost:49706/Student/Details/1
这很好用,但我试图更深入地了解路由。我想创建一个新视图,根据最后一个名称而不是学生ID显示学生详细信息。是的,我知道在现实世界中,这不是一个好主意,因为姓氏不是唯一的,但这对于这个演示很好,因为我的数据库不会变大,我知道所有姓氏目前都是独一无二的。
我做的第一件事是在我的StudentController中创建一个新的动作结果
public ActionResult Grab(string studentName)
{
if(studentName == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Student student = db.Students.Find(studentName);
if(student == null)
{
return HttpNotFound();
}
return View(student);
}
我做的第二件事是右击“Grab”并添加了一个视图。我将视图名称称为“Grab”。将模板设置为“列表”。将Model Class设置为'Student(ContosoUniversity.Models)'。和DataContextClass为'SchoolContext(ContosoUniversity.DAL)'。
我打电话给你 http://localhost:49706/Student/Grab
我得到的400错误就像我在控制器中写的一样。但是当我尝试以下网址时
http://localhost:49706/Student/Grab/Alexander
我还得到另外400个错误。我不确定我遗漏了什么。我可以帮忙吗。
这是我的数据库的样子
我想要做的就是能够输入以下网址 http://localhost:49706/Student/Grab/ {名字}
并显示具有匹配姓氏的学生的详细信息。
感谢您的时间
修改
更新了ActionResult Grab
public ActionResult Grab(string studentName)
{
if(studentName == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Student student = db.Students.Where(student => student.LastName.Contains(studentName)).FirstOrDefault();
if (student == null)
{
return HttpNotFound();
}
return View(student);
}
整个学生管理员
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using ContosoUniversity.DAL;
using ContosoUniversity.Models;
namespace ContosoUniversity.Controllers
{
public class StudentController : Controller
{
private SchoolContext db = new SchoolContext();
// GET: Student
public ActionResult Index()
{
return View(db.Students.ToList());
}
// GET: Student/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Student student = db.Students.Find(id);
if (student == null)
{
return HttpNotFound();
}
return View(student);
}
public ActionResult Grab(string studentName)
{
if(studentName == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Student student = db.Students.Where(student => student.LastName.Contains(studentName)).FirstOrDefault();
if (student == null)
{
return HttpNotFound();
}
return View(student);
}
// GET: Student/Create
public ActionResult Create()
{
return View();
}
// POST: Student/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "LastName, FirstMidName, EnrollmentDate")]Student student)
{
try
{
if (ModelState.IsValid)
{
db.Students.Add(student);
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch (DataException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
return View(student);
}
// GET: Student/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Student student = db.Students.Find(id);
if (student == null)
{
return HttpNotFound();
}
return View(student);
}
// POST: Student/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public ActionResult EditPost(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var studentToUpdate = db.Students.Find(id);
if (TryUpdateModel(studentToUpdate, "",
new string[] { "LastName", "FirstMidName", "EnrollmentDate" }))
{
try
{
db.SaveChanges();
return RedirectToAction("Index");
}
catch (DataException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
}
}
return View(studentToUpdate);
}
// GET: Student/Delete/5
public ActionResult Delete(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Student student = db.Students.Find(id);
if (student == null)
{
return HttpNotFound();
}
return View(student);
}
// POST: Student/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
Student student = db.Students.Find(id);
db.Students.Remove(student);
db.SaveChanges();
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
}
}
感谢到目前为止的建议我现在有一个页面返回。但是,没有加载任何数据。这是一张照片
以下是视图的代码
@model ContosoUniversity.Models.Student
@{
ViewBag.Title = "Grab";
}
<h2>Grab</h2>
<p>
@Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
@Html.DisplayNameFor(model => model.LastName)
</th>
<th>
@Html.DisplayNameFor(model => model.FirstMidName)
</th>
<th>
@Html.DisplayNameFor(model => model.EnrollmentDate)
</th>
<th></th>
</tr>
</table>
答案 0 :(得分:2)
你的问题是路由。当您请求http://localhost:49706/Student/Grab/{LastName}
时,参数studentName
正在解析为null,因此您的BadRequestResponse。
您的网址中的模式与默认的ASP.NET MVC路由模式匹配,但它将无法绑定参数的值,因此MVC将正确命中控制器并执行操作,但参数将为null。
您有几个选择:
映射路线
您可以将其添加到路由配置文件:RouteConfig.cs
目录中的App_Start
:
//Add your own route
routes.MapRoute(
name: null,
url: "{controller}/{action}/{studentName}",
defaults: new { controller = "Student", action = "Grab", id = UrlParameter.Optional }
);
此调用应在调用映射默认路由之前进行。现在,您将能够从路径数据中获取studentName参数的值。
更改网址
您可以通过请求将您的页面添加为新路线来调用相同的操作:
http://localhost:49706/Student/Grab?studentName={LastName}
这里将有效地处理参数。
关于从数据库中提取学生,如果只想匹配完整的姓氏,我会在操作中使用此代码:
public ActionResult Grab(string studentName)
{
if (studentName == null)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
var student = db.Students.FirstOrDefault(x => x.LastName.ToLower() == studentName.ToLower());
if (student == null)
return HttpNotFound();
return View(student);
}
让我知道它对你有用。
希望这有帮助!
答案 1 :(得分:0)
你的第一个错误:
db.Students.Find(studentName); // This one expects an student object not string
所以你需要使用Where
linq扩展名,这样你就可以用这样的姓氏过滤你的学生表:
Student student = db.Students.Where(student => student.LastName.Contains(studentName)).FirstOrDefault();
// Im using contains instead of equals to match substrings
答案 2 :(得分:0)
我假设你是从Index
视图中尝试这样做的。所以因为你的Grab
ActionResult期待一个名为studentName
的参数(可能想要重命名为与lastName对应的内容)出于可维护性的目的).. Index
视图上的操作链接需要如下所示:
@Html.ActionLink("Details by Last Name", "Grab", "Student", new{studentName = item.LastName}, null)
// I assume `item` because if it's in your Index view you are looping to get each user in their own row..
既然你明白使用一个人的姓是一个坏主意,因为它不是唯一的...但在这个例子中它是..没有必要使用.Contain()
方法..你正在寻找一个人,因此您应该使用.SingleOrDefault()
。
public ActionResult Grab(string studentName)
{
// in my experience.. when you're checking a string variable to see if
// it is null.. use string.IsNullOrWhiteSpace.. because even though what
// you're using checks for null.. it doesn't check for white space..
if(studentName == null) // so change this to if(string.IsNullOrWhiteSpace(studentName))
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
Student student = db.Students.SingleOrDefault(student => student.LastName.ToLower() == studentName.ToLower()); // convert the comparison strings to all lower case.. hence the the `.ToLower()` methods for more reliable comparison
if (student == null)
return HttpNotFound();
return View(student);
}
希望这有帮助。