**演练目的:掌握复杂模型的应用程序开发。

Contoso大学校园管理系统功能包括学生、课程、教师的管理。

一、创建MVC Web应用程序

显示效果如下图,操作步骤略。

二、创建数据模型

1.创建学生实体

using System;
using System.Collections.Generic; namespace ContosoUniversity.Models
{
public class Student
{
public int StudentID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; }
}
}

StudentID属性时主键,EF默认将Id或者classnameID作为主键。

Enrollments属性是导航属性,导航属性拥有与这个实体相关联的实体。此处为学生实体拥有相关的所有课程注册实体,如在数据库中,课程注册表中张三有三条记录,那么导航属性就会有3条Enrollment行。创建导航属性后,数据库数据表将自动生成外键。如果导航属性能包含多个实体(如一对多,多对多),类型必须为集合,比如Icollection。

导航属性通常定义成virtual,这样就可以好好利用EF的lazy loading(延迟加载)功能。延迟加载是指暂时不需要该数据,不用在当前马上加载,而可以推迟到使用它时再加载。延迟加载是一种很重要的数据访问特性,可以有效地减少与数据源的交互(注意,这里所提的交互不是指交互次数,而是指交互的数据量),从而提升程序性能。

2.创建课程实体

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema; namespace ContosoUniversity.Models
{
public class Course
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; }
}
}

      Enrollments属性是导航属性。

CourseID考虑到可能有特殊的数据规则,使用[DatabaseGenerated(DatabaseGeneratedOption.None)],将不采用数据库自动生成的序号。

3.创建注册实体

namespace ContosoUniversity.Models
{
public enum Grade
{
A, B, C, D, F
} public class Enrollment
{
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; } public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
}

Grade属性是一个enum枚举类型,?表示该属性可以为空。

StudentID属性是一个外键,相应的导航属性是Student。一个注册实体和一个学生实体相关联,所以导航属性拥有一个Student实体,而不是之前的ICollection集合。

CourseID同理。

4.创建数据库上下文

创建一个DAL(Data Access Layer)文件夹,在文件夹中新建SchoolContext.cs类。

using ContosoUniversity.Models;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions; namespace ContosoUniversity.DAL
{
public class SchoolContext : DbContext
{
public DbSet<Student> Students { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Course> Courses { get; set; }
}
}

这段代码为每个实体集合,创建了一个DbSet属性。在EF技术中,一个实体集合对应一张数据库表,一个实体对应表中的一行。

修改Web.config文件。

<add name="SchoolContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=ContosoUniversity;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\ContosoUniversity.mdf" providerName="System.Data.SqlClient" />

默认情况下,EF会寻找DbContext类名字的连接字符串名字。在这个连接字符串中,你已经在App_Data文件夹下添加了一个名字为ContosoUniversity.mdf的LocalDB数据库。如果没有指定连接字符串,EF会为你新建一个。
5.开启Code First Migrations

(1)打开Package Manager控制台

(2)输入命令Enable-Migrations -ContextTypeName SchoolContext

Configuration类包含一个Seed方法,可以在数据库创建时插入一些模型数据,作为测试数据。

namespace ContosoUniversity.Migrations
{
using System;
using System.Collections.Generic;
using System.Data.Entity.Migrations;
using System.Linq;
using ContosoUniversity.Models; internal sealed class Configuration : DbMigrationsConfiguration<ContosoUniversity.DAL.SchoolContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
} protected override void Seed(ContosoUniversity.DAL.SchoolContext context)
{
var students = new List<Student>
{
new Student { FirstMidName = "Carson", LastName = "Alexander",
EnrollmentDate = DateTime.Parse("2010-09-01") },
new Student { FirstMidName = "Meredith", LastName = "Alonso",
EnrollmentDate = DateTime.Parse("2012-09-01") },
new Student { FirstMidName = "Arturo", LastName = "Anand",
EnrollmentDate = DateTime.Parse("2013-09-01") },
new Student { FirstMidName = "Gytis", LastName = "Barzdukas",
EnrollmentDate = DateTime.Parse("2012-09-01") },
new Student { FirstMidName = "Yan", LastName = "Li",
EnrollmentDate = DateTime.Parse("2012-09-01") },
new Student { FirstMidName = "Peggy", LastName = "Justice",
EnrollmentDate = DateTime.Parse("2011-09-01") },
new Student { FirstMidName = "Laura", LastName = "Norman",
EnrollmentDate = DateTime.Parse("2013-09-01") },
new Student { FirstMidName = "Nino", LastName = "Olivetto",
EnrollmentDate = DateTime.Parse("2005-08-11") }
};
students.ForEach(s => context.Students.AddOrUpdate(p => p.LastName, s));
context.SaveChanges(); var courses = new List<Course>
{
new Course {CourseID = , Title = "Chemistry", Credits = , },
new Course {CourseID = , Title = "Microeconomics", Credits = , },
new Course {CourseID = , Title = "Macroeconomics", Credits = , },
new Course {CourseID = , Title = "Calculus", Credits = , },
new Course {CourseID = , Title = "Trigonometry", Credits = , },
new Course {CourseID = , Title = "Composition", Credits = , },
new Course {CourseID = , Title = "Literature", Credits = , }
};
courses.ForEach(s => context.Courses.AddOrUpdate(p => p.Title, s));
context.SaveChanges(); var enrollments = new List<Enrollment>
{
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alexander").StudentID,
CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID,
Grade = Grade.A
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alexander").StudentID,
CourseID = courses.Single(c => c.Title == "Microeconomics" ).CourseID,
Grade = Grade.C
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alexander").StudentID,
CourseID = courses.Single(c => c.Title == "Macroeconomics" ).CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alonso").StudentID,
CourseID = courses.Single(c => c.Title == "Calculus" ).CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alonso").StudentID,
CourseID = courses.Single(c => c.Title == "Trigonometry" ).CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alonso").StudentID,
CourseID = courses.Single(c => c.Title == "Composition" ).CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Anand").StudentID,
CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Anand").StudentID,
CourseID = courses.Single(c => c.Title == "Microeconomics").CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Barzdukas").StudentID,
CourseID = courses.Single(c => c.Title == "Chemistry").CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Li").StudentID,
CourseID = courses.Single(c => c.Title == "Composition").CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Justice").StudentID,
CourseID = courses.Single(c => c.Title == "Literature").CourseID,
Grade = Grade.B
}
}; foreach (Enrollment e in enrollments)
{
var enrollmentInDataBase = context.Enrollments.Where(
s =>
s.Student.StudentID == e.StudentID &&
s.Course.CourseID == e.CourseID).SingleOrDefault();
if (enrollmentInDataBase == null)
{
context.Enrollments.Add(e);
}
}
context.SaveChanges();
}
}
}

Seed方法使用数据库context对象作为输入参数,使用这个对象把新的实体插入到数据库中。每一个实体类型,代码都创建了一个新的实体集合,把它们添加到相应的DbSet属性中,然后保存到数据库中。
      AddOrUpdate方法,第一个参数用来检查一个行是否已经存在,第二个参数是插入的值。

Enrollment实体并没有使用AddOrUpdate方法,而是使用循环遍历Enrollment集合,判断是不是已经存在。

(3)add-migration InitialCreate

(4)update-database

运行Up方法创建数据库,并运行Seed方法填充数据库。

三、创建Student控制器和视图

1.创建Student控制器,选择自动生成控制器代码和视图。

**Conventions

  • 实体类名字的复数,被用作数据库表名
  • 实体属性的名字,被用作列名
  • Id或ClassnameID属性,被标识为主键。

2.创建Details页面

在EnrollmentDate后面,<fieldset>之前,添加显示注册信息列表的代码。

    <div class="display-label">
@Html.LabelFor(model => model.Enrollments)
</div>
<div class="display-field">
<table>
<tr>
<th>Course Title</th>
<th>Grade</th>
</tr>
@foreach (var item in Model.Enrollments)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Course.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.Grade)
</td>
</tr>
}
</table>
</div>
</fieldset>
<p>
@Html.ActionLink("Edit", "Edit", new { id=Model.StudentID }) |
@Html.ActionLink("Back to List", "Index")
</p>

3.更新Create页面

更新HttpPost Create方法,添加try-catch和Bind attribute

[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 after DataException 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);
}

这段代码使用模型绑定添加Student实体,并保存至数据库。

ValidateAntiForgeryToken属性,帮助伪造响应跨站攻击。需要在Create视图中,表单里面添加语句“@Html.AntiForgeryToken()”。

Bind属性用来保护过度提交。例如,假设Student实体包含一个Secret属性,你不想通过网页更新。Exclude参数阻止你希望排除的黑名单字段。

   public class Student
{
public int StudentID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public string Secret { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; }
}

4.更新HttpPost Edit方法

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(
[Bind(Include = "StudentID, LastName, FirstMidName, EnrollmentDate")]
Student student)
{
try
{
if (ModelState.IsValid)
{
db.Entry(student).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch (DataException /* dex */)
{
//Log the error (uncomment dex variable name after DataException 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);
}

Entity状态有Added、Unchanged、Modified、Delete、Detached。
5.更新Delete页面

public ActionResult Delete(bool? saveChangesError=false, int id = )
{
if (saveChangesError.GetValueOrDefault())
{
ViewBag.ErrorMessage = "Delete failed. Try again, and if the problem persists see your system administrator.";
}
Student student = db.Students.Find(id);
if (student == null)
{
return HttpNotFound();
}
return View(student);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Delete(int id)
{
try
{
Student student = db.Students.Find(id);
db.Students.Remove(student);
db.SaveChanges();
}
catch (DataException/* dex */)
{
// uncomment dex and log error.
return RedirectToAction("Delete", new { id = id, saveChangesError = true });
}
return RedirectToAction("Index");
}

在Views\Student\Delete.cshtml页面中,添加错误消息。

<h2>Delete</h2>
<p class="error">@ViewBag.ErrorMessage</p>
<h3>Are you sure you want to delete this?</h3>

演练5-1:Contoso大学校园管理1的更多相关文章

  1. 演练5-2:Contoso大学校园管理2

    一.添加列标题排序功能 我们将增加Student/Index页面的功能,为列标题添加超链接,用户可以点击列标题对那一列进行排序. 1.修改Index方法 public ActionResult Ind ...

  2. 演练5-3:Contoso大学校园管理系统3

    在前面的教程中,我们使用了一个简单的数据模型,包括三个数据实体.在这个教程汇中,我们将添加更多的实体和关系,按照特定的格式和验证规则等自定义数据模型. Contoso大学校园管理系统的数据模型如下. ...

  3. 演练5-5:Contoso大学校园管理系统5

    Contoso University示例网站演示如何使用Entity Framework 5创建ASP.NET MVC 4应用程序. Entity Framework有三种处理数据的方式:  Data ...

  4. 演练5-6:Contoso大学校园管理系统6

    在上一次的教程中,我们处理了关联数据问题.这个教程演示如何处理并发问题.你将使用Department实体创建一个页面,这个页面在支持编辑和删除的同时,还可以处理并发错误.下面的截图演示了Index页面 ...

  5. 演练5-7:Contoso大学校园管理系统(实现继承)

    ***操作视频下载:1     *** 在上一次教程中,你已经能够处理并发异常.这个教程将会展示如何在数据模型中实现继承. 在面向对象的程序设计中,你可以通过继承来清除冗余的代码.在这个教程中,你将要 ...

  6. 演练5-4:Contoso大学校园管理系统4

    在之前的教程中,我们已经完成了学校的数据模型.现在我们将读取和显示相关数据,请理解EF加载导航属性的方式. 一.Lazy.Eager.Explicit数据加载 使用EF为实体中的导航属性加载相关数据, ...

  7. 演练5-8:Contoso大学校园管理系统(实现存储池和工作单元模式)

    在上一次的教程中,你已经使用继承来消除在 Student 和 Instructor 实体之间的重复代码.在这个教程中,你将要看到使用存储池和工作单元模式进行增.删.改.查的一些方法.像前面的教程一样, ...

  8. Contoso 大学 - 使用 EF Code First 创建 MVC 应用,实例演练

    Contoso 大学 Web 示例应用演示了如何使用 EF 技术创建 ASP.NET MVC 应用.示例中的 Contoso 大学是虚构的.应用包括了类似学生注册.课程创建以及教师分配等功能. 这个系 ...

  9. Contoso 大学 - 8 – 实现继承

    原文 Contoso 大学 - 8 – 实现继承 By Tom Dykstra, Tom Dykstra is a Senior Programming Writer on Microsoft's W ...

随机推荐

  1. 一些常用的Intent及intent-filter的信息

    Uri Action 功能 备注 geo:latitude,longitude Intent.ACTION_VIEW 打开地图应用程序并显示指定的经纬度   geo:0,0?q=street+addr ...

  2. ASP.net体系

  3. 更新Windows Azure Web Site中的Orchard版本

    官方建议大家使用本地副本来更新 1.首先做个全站备份,这样更新好以后出问题你就很容易回滚 . Web Site 做备份很方便.把网站SCALE设置到STANDARD,然后在BACKUPS页面里面点备份 ...

  4. tasklet和工作队列

    tasklet机制和工作队列 http://blog.chinaunix.net/uid-28236237-id-3450753.html tasklet原理 http://www.kuqin.com ...

  5. Value Categories

    Value categories Three primary categories primary categories mixed special Each C++ expression (an o ...

  6. Definitions

    Definitions and ODR Definitions are declarations that fully define the entity introduced by the decl ...

  7. Oracle语句优化规则(一)

    1. 选用适合的ORACLE优化器     ORACLE的优化器共有3种:    a. RULE (基于规则)   b. COST (基于成本) c. CHOOSE (选择性)     设置缺省的优化 ...

  8. jQuery扩展extend一

    把这个扩展写在这里,以后要是忘了可以回头查看. (function(j) {// 这里的j是一个形参,表示传入的jQuery对象,j可以任意填写 j.extend({// 相当于给jQuery对象加上 ...

  9. SQL Identity自增列清零方法

    1.使用DBCC控制台命令: dbcc checkident(表名,RESEED,0) 2.truncate table 也可将当前标识值清零 但当有外键等约束时,无法truncate表 可以先禁用外 ...

  10. 设置textarea文本域不能调整大小 resize

    CSS3中新增了resize缩放属性,这个属性可以应用到任意元素.目前只有Webkit内核的浏览器才支持这个css3属性,即Google chrome和Apple safari都支持.而textare ...