通过 VB.NET 的流畅 APIFluent API with VB.NET

Code First 允许使用 C# 或 VB.NET 类定义模型。 还可以选择使用类和属性上的属性或使用 Fluent API 来执行其他配置。 本演练演示如何使用 VB.NET 执行 Fluent API 配置。

本页假设您基本了解 Code First。 有关 Code First 的详细信息,请参阅以下演练:

先决条件Pre-Requisites

需要至少安装 Visual Studio 2010 或 Visual Studio 2012 才能完成此演练。

如果你使用的是 Visual Studio 2010,你还需要安装NuGet

创建应用程序Create the Application

为了简单起见,我们将构建一个使用 Code First 执行数据访问的基本控制台应用程序。

  • 打开 Visual Studio
  • 文件->> 项目 。
  • 从左侧菜单和控制台应用程序选择Windows
  • 输入CodeFirstVBSample作为名称
  • 选择“确定”

定义模型Define the Model

在此步骤中,将定义表示概念模型的 VB.NET POCO 实体类型。 类不需要从任何基类派生或实现任何接口。

  • 向项目中添加一个新类,为类名输入SchoolModel
  • 将新类的内容替换为以下代码
  1. Public Class Department
  2. Public Sub New()
  3. Me.Courses = New List(Of Course)()
  4. End Sub
  5. ' Primary key
  6. Public Property DepartmentID() As Integer
  7. Public Property Name() As String
  8. Public Property Budget() As Decimal
  9. Public Property StartDate() As Date
  10. Public Property Administrator() As Integer?
  11. Public Overridable Property Courses() As ICollection(Of Course)
  12. End Class
  13. Public Class Course
  14. Public Sub New()
  15. Me.Instructors = New HashSet(Of Instructor)()
  16. End Sub
  17. ' Primary key
  18. Public Property CourseID() As Integer
  19. Public Property Title() As String
  20. Public Property Credits() As Integer
  21. ' Foreign key that does not follow the Code First convention.
  22. ' The fluent API will be used to configure DepartmentID_FK to be the foreign key for this entity.
  23. Public Property DepartmentID_FK() As Integer
  24. ' Navigation properties
  25. Public Overridable Property Department() As Department
  26. Public Overridable Property Instructors() As ICollection(Of Instructor)
  27. End Class
  28. Public Class OnlineCourse
  29. Inherits Course
  30. Public Property URL() As String
  31. End Class
  32. Partial Public Class OnsiteCourse
  33. Inherits Course
  34. Public Sub New()
  35. Details = New OnsiteCourseDetails()
  36. End Sub
  37. Public Property Details() As OnsiteCourseDetails
  38. End Class
  39. ' Complex type
  40. Public Class OnsiteCourseDetails
  41. Public Property Time() As Date
  42. Public Property Location() As String
  43. Public Property Days() As String
  44. End Class
  45. Public Class Person
  46. ' Primary key
  47. Public Property PersonID() As Integer
  48. Public Property LastName() As String
  49. Public Property FirstName() As String
  50. End Class
  51. Public Class Instructor
  52. Inherits Person
  53. Public Sub New()
  54. Me.Courses = New List(Of Course)()
  55. End Sub
  56. Public Property HireDate() As Date
  57. ' Navigation properties
  58. Private privateCourses As ICollection(Of Course)
  59. Public Overridable Property Courses() As ICollection(Of Course)
  60. Public Overridable Property OfficeAssignment() As OfficeAssignment
  61. End Class
  62. Public Class OfficeAssignment
  63. ' Primary key that does not follow the Code First convention.
  64. ' The HasKey method is used later to configure the primary key for the entity.
  65. Public Property InstructorID() As Integer
  66. Public Property Location() As String
  67. Public Property Timestamp() As Byte()
  68. ' Navigation property
  69. Public Overridable Property Instructor() As Instructor
  70. End Class

定义派生上下文Define a Derived Context

我们即将开始使用实体框架中的类型,因此我们需要添加 EntityFramework NuGet 包。

  • * * 项目–>管理 NuGet 程序包 …

备注

如果你没有 “管理 NuGet 包 … “ 选项你应安装最新版本的 NuGet

  • 选择 “联机“ 选项卡
  • 选择EntityFramework
  • 单击“安装”

现在可以定义一个派生上下文,它表示与数据库的会话,从而使我们能够查询和保存数据。 我们定义了从 DbContext 派生的上下文,并为模型中的每个类公开了类型化的 DbSet

  • 向项目中添加一个新类,为类名输入SchoolContext
  • 将新类的内容替换为以下代码
  1. Imports System.ComponentModel.DataAnnotations
  2. Imports System.ComponentModel.DataAnnotations.Schema
  3. Imports System.Data.Entity
  4. Imports System.Data.Entity.Infrastructure
  5. Imports System.Data.Entity.ModelConfiguration.Conventions
  6. Public Class SchoolContext
  7. Inherits DbContext
  8. Public Property OfficeAssignments() As DbSet(Of OfficeAssignment)
  9. Public Property Instructors() As DbSet(Of Instructor)
  10. Public Property Courses() As DbSet(Of Course)
  11. Public Property Departments() As DbSet(Of Department)
  12. Protected Overrides Sub OnModelCreating(ByVal modelBuilder As DbModelBuilder)
  13. End Sub
  14. End Class

用熟知 API 配置Configuring with the Fluent API

本部分演示如何使用熟知的 Api 将类型配置为表映射、属性到列的映射,以及模型中\类型的表之间的关系。 Fluent API 通过DbModelBuilder类型公开,最常用的方法是重写DbContext上的OnModelCreating方法。

  • 复制以下代码并将其添加到SchoolContext类中定义的OnModelCreating方法中。注释说明了每个映射的作用
  1. ' Configure Code First to ignore PluralizingTableName convention
  2. ' If you keep this convention then the generated tables
  3. ' will have pluralized names.
  4. modelBuilder.Conventions.Remove(Of PluralizingTableNameConvention)()
  5. ' Specifying that a Class is a Complex Type
  6. ' The model defined in this topic defines a type OnsiteCourseDetails.
  7. ' By convention, a type that has no primary key specified
  8. ' is treated as a complex type.
  9. ' There are some scenarios where Code First will not
  10. ' detect a complex type (for example, if you do have a property
  11. ' called ID, but you do not mean for it to be a primary key).
  12. ' In such cases, you would use the fluent API to
  13. ' explicitly specify that a type is a complex type.
  14. modelBuilder.ComplexType(Of OnsiteCourseDetails)()
  15. ' Mapping a CLR Entity Type to a Specific Table in the Database.
  16. ' All properties of OfficeAssignment will be mapped
  17. ' to columns in a table called t_OfficeAssignment.
  18. modelBuilder.Entity(Of OfficeAssignment)().ToTable("t_OfficeAssignment")
  19. ' Mapping the Table-Per-Hierarchy (TPH) Inheritance
  20. ' In the TPH mapping scenario, all types in an inheritance hierarchy
  21. ' are mapped to a single table.
  22. ' A discriminator column is used to identify the type of each row.
  23. ' When creating your model with Code First,
  24. ' TPH is the default strategy for the types that
  25. ' participate in the inheritance hierarchy.
  26. ' By default, the discriminator column is added
  27. ' to the table with the name Discriminator
  28. ' and the CLR type name of each type in the hierarchy
  29. ' is used for the discriminator values.
  30. ' You can modify the default behavior by using the fluent API.
  31. modelBuilder.Entity(Of Person)().
  32. Map(Of Person)(Function(t) t.Requires("Type").
  33. HasValue("Person")).
  34. Map(Of Instructor)(Function(t) t.Requires("Type").
  35. HasValue("Instructor"))
  36. ' Mapping the Table-Per-Type (TPT) Inheritance
  37. ' In the TPT mapping scenario, all types are mapped to individual tables.
  38. ' Properties that belong solely to a base type or derived type are stored
  39. ' in a table that maps to that type. Tables that map to derived types
  40. ' also store a foreign key that joins the derived table with the base table.
  41. modelBuilder.Entity(Of Course)().ToTable("Course")
  42. modelBuilder.Entity(Of OnsiteCourse)().ToTable("OnsiteCourse")
  43. modelBuilder.Entity(Of OnlineCourse)().ToTable("OnlineCourse")
  44. ' Configuring a Primary Key
  45. ' If your class defines a property whose name is ID or Id”,
  46. ' or a class name followed by “ID” or “Id”,
  47. ' the Entity Framework treats this property as a primary key by convention.
  48. ' If your property name does not follow this pattern, use the HasKey method
  49. ' to configure the primary key for the entity.
  50. modelBuilder.Entity(Of OfficeAssignment)().
  51. HasKey(Function(t) t.InstructorID)
  52. ' Specifying the Maximum Length on a Property
  53. ' In the following example, the Name property
  54. ' should be no longer than 50 characters.
  55. ' If you make the value longer than 50 characters,
  56. ' you will get a DbEntityValidationException exception.
  57. modelBuilder.Entity(Of Department)().Property(Function(t) t.Name).
  58. HasMaxLength(60)
  59. ' Configuring the Property to be Required
  60. ' In the following example, the Name property is required.
  61. ' If you do not specify the Name,
  62. ' you will get a DbEntityValidationException exception.
  63. ' The database column used to store this property will be non-nullable.
  64. modelBuilder.Entity(Of Department)().Property(Function(t) t.Name).
  65. IsRequired()
  66. ' Switching off Identity for Numeric Primary Keys
  67. ' The following example sets the DepartmentID property to
  68. ' System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.None to indicate that
  69. ' the value will not be generated by the database.
  70. modelBuilder.Entity(Of Course)().Property(Function(t) t.CourseID).
  71. HasDatabaseGeneratedOption(DatabaseGeneratedOption.None)
  72. 'Specifying NOT to Map a CLR Property to a Column in the Database
  73. modelBuilder.Entity(Of Department)().
  74. Ignore(Function(t) t.Administrator)
  75. 'Mapping a CLR Property to a Specific Column in the Database
  76. modelBuilder.Entity(Of Department)().Property(Function(t) t.Budget).
  77. HasColumnName("DepartmentBudget")
  78. 'Configuring the Data Type of a Database Column
  79. modelBuilder.Entity(Of Department)().Property(Function(t) t.Name).
  80. HasColumnType("varchar")
  81. 'Configuring Properties on a Complex Type
  82. modelBuilder.Entity(Of OnsiteCourse)().Property(Function(t) t.Details.Days).
  83. HasColumnName("Days")
  84. modelBuilder.Entity(Of OnsiteCourse)().Property(Function(t) t.Details.Location).
  85. HasColumnName("Location")
  86. modelBuilder.Entity(Of OnsiteCourse)().Property(Function(t) t.Details.Time).
  87. HasColumnName("Time")
  88. ' Map one-to-zero or one relationship
  89. ' The OfficeAssignment has the InstructorID
  90. ' property that is a primary key and a foreign key.
  91. modelBuilder.Entity(Of OfficeAssignment)().
  92. HasRequired(Function(t) t.Instructor).
  93. WithOptional(Function(t) t.OfficeAssignment)
  94. ' Configuring a Many-to-Many Relationship
  95. ' The following code configures a many-to-many relationship
  96. ' between the Course and Instructor types.
  97. ' In the following example, the default Code First conventions
  98. ' are used to create a join table.
  99. ' As a result the CourseInstructor table is created with
  100. ' Course_CourseID and Instructor_InstructorID columns.
  101. modelBuilder.Entity(Of Course)().
  102. HasMany(Function(t) t.Instructors).
  103. WithMany(Function(t) t.Courses)
  104. ' Configuring a Many-to-Many Relationship and specifying the names
  105. ' of the columns in the join table
  106. ' If you want to specify the join table name
  107. ' and the names of the columns in the table
  108. ' you need to do additional configuration by using the Map method.
  109. ' The following code generates the CourseInstructor
  110. ' table with CourseID and InstructorID columns.
  111. modelBuilder.Entity(Of Course)().
  112. HasMany(Function(t) t.Instructors).
  113. WithMany(Function(t) t.Courses).
  114. Map(Sub(m)
  115. m.ToTable("CourseInstructor")
  116. m.MapLeftKey("CourseID")
  117. m.MapRightKey("InstructorID")
  118. End Sub)
  119. ' Configuring a foreign key name that does not follow the Code First convention
  120. ' The foreign key property on the Course class is called DepartmentID_FK
  121. ' since that does not follow Code First conventions you need to explicitly specify
  122. ' that you want DepartmentID_FK to be the foreign key.
  123. modelBuilder.Entity(Of Course)().
  124. HasRequired(Function(t) t.Department).
  125. WithMany(Function(t) t.Courses).
  126. HasForeignKey(Function(t) t.DepartmentID_FK)
  127. ' Enabling Cascade Delete
  128. ' By default, if a foreign key on the dependent entity is not nullable,
  129. ' then Code First sets cascade delete on the relationship.
  130. ' If a foreign key on the dependent entity is nullable,
  131. ' Code First does not set cascade delete on the relationship,
  132. ' and when the principal is deleted the foreign key will be set to null.
  133. ' The following code configures cascade delete on the relationship.
  134. ' You can also remove the cascade delete conventions by using:
  135. ' modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>()
  136. ' and modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>().
  137. modelBuilder.Entity(Of Course)().
  138. HasRequired(Function(t) t.Department).
  139. WithMany(Function(t) t.Courses).
  140. HasForeignKey(Function(d) d.DepartmentID_FK).
  141. WillCascadeOnDelete(False)

使用模型Using the Model

让我们使用SchoolContext执行一些数据访问,以查看操作中的模型。

  • 打开定义了 Main 函数的 Module1 文件
  • 复制并粘贴以下 Module1 定义
  1. Imports System.Data.Entity
  2. Module Module1
  3. Sub Main()
  4. Using context As New SchoolContext()
  5. ' Create and save a new Department.
  6. Console.Write("Enter a name for a new Department: ")
  7. Dim name = Console.ReadLine()
  8. Dim department = New Department With { .Name = name, .StartDate = DateTime.Now }
  9. context.Departments.Add(department)
  10. context.SaveChanges()
  11. ' Display all Departments from the database ordered by name
  12. Dim departments =
  13. From d In context.Departments
  14. Order By d.Name
  15. Select d
  16. Console.WriteLine("All Departments in the database:")
  17. For Each department In departments
  18. Console.WriteLine(department.Name)
  19. Next
  20. End Using
  21. Console.WriteLine("Press any key to exit...")
  22. Console.ReadKey()
  23. End Sub
  24. End Module

你现在可以运行该应用程序并对其进行测试。

  1. Enter a name for a new Department: Computing
  2. All Departments in the database:
  3. Computing
  4. Press any key to exit...