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

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. WinRT Toolkit 介绍--Control篇

    WinRT toolkit是组针对Windows Runtime XAML开发的一系列Control,extension和helper类.它和Windows Phone Toolkit一样,也是由很多 ...

  2. .net Windows服务程序和安装程序制作图解

    最近项目中用到window服务程序,以前没接触过,比较陌生,花了两天的时间学习了下,写了个简单的服务,但在制作安装程序的时候,参照网上很多资料,却都制作不成功,可能是开发环境或项目配置的不同,这里把自 ...

  3. linux cmd: netstat

    每天一个linux命令:netstat  http://www.cnblogs.com/peida/archive/2013/03/08/2949194.html netstat命令用于显示与IP.T ...

  4. c++实现二分查找

    简要描述: 二分查找又称折半查找,优点是比较次数少,查找速度快,平均性能好:其缺点是要求待查表为有序表,且插入删除 困难. 条件:查找的数组必须要为有序数组. 二分查找的过程剩简要描述如下图: 二种实 ...

  5. Python之路Day17

    算法:冒泡排序.插入排序.快速排序.堆排序 冒泡排序 #! /usr/bin/env python # -*- coding: utf-8 -*- # __author__ = "Q1mi& ...

  6. Visual Studio 2013 Xamarin for iOS 环境搭建

    原文:Visual Studio 2013 Xamarin for iOS 环境搭建 一.Mac安装Xamarin.iOS 1,我的Mac 环境:OSX 10.10.3.Xcode 6.3.2 (使用 ...

  7. 告别IE给我们的web开发带来的困扰(使用chrome frame v8引擎)

    茶爸爸个人微信:benyzhous,公众号:cha-baba欢迎骚扰 由于客户所有机器必须使用IE6浏览器,导致我们在开发项目过程中遇到非常多的样式与性能问题,在偶然的一次使用360软件管家搜索chr ...

  8. pycurl,Python cURL library

    pycurl — A Python interface to the cURL library Pycurl包是一个libcurl的Python接口.pycurl已经成功的在Python2.2到Pyt ...

  9. IT第六天 - eclipse快捷操作、万年历项目的编写、菱形的打印输出、代码简化

    IT第六天 上午 小项目 1.程序提前结束的退出标志 2.登录用户的模拟,给出适当的提示信息 3.根据要求,寻找规律,然后编写程序 Eclipse的使用 1.快捷键的使用 下午 中小项目 1.九九乘法 ...

  10. Windows failed to start.界面下修复win8引导

    首先要保证 系统本身是没有问题的 不是在装机的时候出现这种情况 那么可以按照以下方法来进行 首先要在另外一台电脑上将win8刻进u盘 启动时以u盘为第一启动项启动 进入win8装机界面 点击左下角的修 ...