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


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

          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 = 1050, Title = "Chemistry",      Credits = 3, },
                    new Course {CourseID = 4022, Title = "Microeconomics", Credits = 3, },
                    new Course {CourseID = 4041, Title = "Macroeconomics", Credits = 3, },
                    new Course {CourseID = 1045, Title = "Calculus",       Credits = 4, },
                    new Course {CourseID = 3141, Title = "Trigonometry",   Credits = 4, },
                    new Course {CourseID = 2021, Title = "Composition",    Credits = 3, },
                    new Course {CourseID = 2042, Title = "Literature",     Credits = 4, }
                };
             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 = 0)
    {
        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");
    }

          在ViewsStudentDelete.cshtml页面中,添加错误消息。

    <h2>Delete</h2>
    <p class="error">@ViewBag.ErrorMessage</p>
    <h3>Are you sure you want to delete this?</h3>
  • 相关阅读:
    浅析String.intern()方法
    com.alibaba.druid.pool.DruidPooledConnection cannot be cast to oracle.jdbc.OracleConnection 异常解决办法
    ChannelEventRunnable handle RECEIVED operation error, channel is NettyChannel解决方法
    jsch channel is not opened原因及解决
    学习地址(oraclemysqllinux)
    python中数据分析常用函数整理
    Python之使用Pandas库实现MySQL数据库的读写
    Python数据分析
    如何用sql查询出连续三个月金额大于50的记录
    【Python项目实战】Pandas:让你像写SQL一样做数据分析(一)
  • 原文地址:https://www.cnblogs.com/meetyy/p/4022278.html
Copyright © 2020-2023  润新知