📜  Entity Framework-第一个示例

📅  最后修改于: 2020-11-21 07:32:19             🧑  作者: Mango


让我们使用类定义一个非常简单的模型。我们只是在Program.cs文件中定义它们,但是在实际应用程序中,您将把类分为单独的文件和可能的单独项目。以下是我们将使用代码优先方法创建的数据模型。

使用类建模

建立模型

使用下面的学生类代码在Program.cs文件中添加以下三个类。

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
    
   public virtual ICollection Enrollments { get; set; }
}
  • ID属性将成为对应于此类的数据库表的主键列。

  • 登记属性是导航属性。导航属性包含与此实体相关的其他实体。

  • 在这种情况下,学生实体的“注册”属性将保存与该学生实体相关的所有注册实体。

  • 导航属性通常定义为虚拟的,以便它们可以利用某些Entity Framework功能,例如延迟加载。

  • 如果导航属性可以容纳多个实体(如多对多或一对多关系),则其类型必须是一个列表,可以在其中添加,删除和更新条目,例如ICollection。

以下是课程课程的实现。

public class Course {
   public int CourseID { get; set; }
   public string Title { get; set; }
   public int Credits { get; set; }
    
   public virtual ICollection Enrollments { get; set; }
}

登记属性是导航属性。课程实体可以与任意数量的注册实体相关。

以下是Enrollment类和枚举的实现。

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; }
}
  • EnrollmentID属性将为主键。

  • Grade属性是一个枚举。成绩类型声明后的问号表示成绩属性为可空。

  • 空等级与零等级不同。 Null表示等级未知或尚未分配。

  • StudentID和CourseID属性是外键,相应的导航属性是Student和Course。

  • 一个注册实体与一个学生和一个课程实体相关联,因此该资源只能容纳一个学生和课程实体。

创建数据库上下文

协调给定数据模型的Entity Framework功能的主要类是数据库上下文类,它允许查询和保存数据。您可以通过派生DbContext类并公开类型化的DbSet来创建此类。对于我们模型中的每个班级。以下是MyContext类的实现,该类派生自DbContext类。

public class MyContext : DbContext {
   public virtual DbSet Courses { get; set; }
   public virtual DbSet Enrollments { get; set; }
   public virtual DbSet Students { get; set; }
}

以下是Program.cs文件中的完整代码。

using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EFCodeFirstDemo {

   class Program {
      static void Main(string[] args) {}
   }

   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; }
   }

   public class Student {
      public int ID { get; set; }
      public string LastName { get; set; }
      public string FirstMidName { get; set; }
      public DateTime EnrollmentDate { get; set; }
        
      public virtual ICollection Enrollments { get; set; }
   }

   public class Course {
      public int CourseID { get; set; }
      public string Title { get; set; }
      public int Credits { get; set; }
        
      public virtual ICollection Enrollments { get; set; }
   }

   public class MyContext : DbContext {
      public virtual DbSet Courses { get; set; }
      public virtual DbSet Enrollments { get; set; }
      public virtual DbSet Students { get; set; }
   }

}

上面的代码是我们开始存储和检索数据所需的全部。让我们添加一些数据,然后检索它。以下是main方法中的代码。

static void Main(string[] args) {

   using (var context = new MyContext()) {
      // Create and save a new Students
      Console.WriteLine("Adding new students");

      var student = new Student {
         FirstMidName = "Alain", LastName = "Bomer", 
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
      };

      context.Students.Add(student);
        
      var student1 = new Student {
         FirstMidName = "Mark", LastName = "Upston", 
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
      };

      context.Students.Add(student1);
      context.SaveChanges();

      // Display all Students from the database
      var students = (from s in context.Students 
         orderby s.FirstMidName select s).ToList();

      Console.WriteLine("Retrieve all Students from the database:");

      foreach (var stdnt in students) {
         string name = stdnt.FirstMidName + " " + stdnt.LastName;
         Console.WriteLine("ID: {0}, Name: {1}", stdnt.ID, name);
      }
        
      Console.WriteLine("Press any key to exit...");
      Console.ReadKey();
   }
}

执行上述代码后,您将收到以下输出。

Adding new students
Retrieve all Students from the database:
ID: 1, Name: Alain Bomer
ID: 2, Name: Mark Upston
Press any key to exit...

现在想到的问题是,数据和数据库在哪里,我们在其中添加了一些数据,然后从数据库中检索了这些数据。按照约定,DbContext为您创建了一个数据库。

  • 如果本地SQL Express实例可用,则Code First已在该实例上创建数据库。

  • 如果没有SQL Express,则Code First将尝试并使用LocalDb。

  • 数据库以派生上下文的完全限定名称命名。

在我们的示例中,SQL Express实例可用,数据库名称为EFCodeFirstDemo.MyContext,如下图所示。

SQL Express实例

  • 这些只是默认约定,并且有多种方法可以更改Code First使用的数据库。

  • 如上图所示,它已经创建了“学生”,“课程”和“注册”表,并且每个表都包含具有适当数据类型和长度的列。

  • 列名和数据类型也与相应域类的属性匹配。

数据库初始化

在上面的示例中,我们已经看到Code First自动创建一个数据库,但是如果您想更改数据库和服务器的名称,让我们看看Code First在初始化数据库时如何确定数据库名称和服务器。看下图。

数据库初始化

您可以通过以下方式定义上下文类的基本构造函数。

  • 无参数
  • 数据库名称
  • 连接字符串名称

无参数

如果如上例所示,指定上下文类的基本构造函数而没有任何参数,则实体框架将在本地SQLEXPRESS服务器中创建一个名称为{Namespace}。{Context class name}的数据库。

在上面的示例中,自动创建的数据库的名称为EFCodeFirstDemo.MyContext。如果查看该名称,则会发现EFCodeFirstDemo是名称空间,而MyContext是上下文类名称,如以下代码所示。

public class MyContext : DbContext {
   public MyContext() : base() {}

   public virtual DbSet Courses { get; set; }
   public virtual DbSet Enrollments { get; set; }
   public virtual DbSet Students { get; set; }
}

数据库名称

如果在上下文类的基本构造函数中将数据库名称作为参数传递,则Code First将再次自动创建一个数据库,但是这次名称将是在本地SQLEXPRESS数据库服务器上的基本构造函数中作为参数传递的名称。 。

在以下代码中,将MyContextDB指定为基本构造函数中的参数。如果运行您的应用程序,则将在本地SQL服务器中创建具有MyContextDB名称的数据库。

public class MyContext : DbContext {
   public MyContext() : base("MyContextDB") {}
   public virtual DbSet Courses { get; set; }
   public virtual DbSet Enrollments { get; set; }
   public virtual DbSet Students { get; set; }
}

连接字符串名称

这是告诉DbContext使用SQL Express或LocalDb以外的数据库服务器的简便方法。您可以选择将连接字符串放入app.config文件中。

  • 如果连接字符串的名称与您的上下文的名称匹配(具有或不具有名称空间限定),则当使用参数less构造函数时,DbContext将找到它。

  • 如果连接字符串名称与您的上下文名称不同,则可以通过将连接字符串名称传递给DbContext构造函数来告诉DbContext在“代码优先”模式下使用此连接。

public class MyContext : DbContext {
   public MyContext() : base("name = MyContextDB") {}
   public virtual DbSet Courses { get; set; }
   public virtual DbSet Enrollments { get; set; }
   public virtual DbSet Students { get; set; }
}
  • 在上面的代码中,上下文类连接字符串的片段被指定为基本构造函数中的参数。

  • 连接字符串名称必须以“ name =”开头,否则,它将被视为数据库名称。

  • 这种形式表明您希望在配置文件中找到连接字符串。如果找不到具有给定名称的连接字符串,则将引发异常。


   

  • app.config中的连接字符串中的数据库名称为EFMyContextDB 。 CodeFirst将在本地SQL Server上创建一个新的EFMyContextDB数据库或使用现有的EFMyContextDB数据库。

域类

到目前为止,我们只是让EF使用其默认约定来发现模型,但是有时候我们的类不遵循约定,我们需要能够执行进一步的配置。但是您可以通过配置域类为EF提供所需的信息来覆盖这些约定。有两个选项来配置您的域类-

  • 资料注解
  • 流利的API

资料注解

DataAnnotations用于配置您的类,该类将突出显示最常用的配置。许多.NET应用程序(例如ASP.NET MVC)也可以理解DataAnnotations,这些应用程序允许这些应用程序利用相同的注释进行客户端验证。

以下是学生课堂中使用的数据注释。

public class Enrollment {

   [Key]
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }

   [ForeignKey("CourseID")]
   public virtual Course Course { get; set; }

   [ForeignKey("ID")]
   public virtual Student Student { get; set; }
}

流利的API

大多数模型配置可以使用简单的数据注释完成。流畅的API是一种指定模型配置的高级方法,该模型配置涵盖了数据注释可以完成的所有工作,以及一些数据注释无法实现的高级配置。数据注释和流利的API可以一起使用。

要访问流利的API,您可以在DbContext中重写OnModelCreating方法。现在,让我们将学生表中的列名从FirstMidName重命名为FirstName,如以下代码所示。

public class MyContext : DbContext {

   protected override void OnModelCreating(DbModelBuilder modelBuilder) {
      modelBuilder.Entity().Property(s ⇒ s.FirstMidName)
         .HasColumnName("FirstName");
   }

   public virtual DbSet Courses { get; set; }
   public virtual DbSet Enrollments { get; set; }
   public virtual DbSet Students { get; set; }
}