In the previous tutorials you worked with a simple data model that
was composed of three entities. In this tutorial you'll add more
entities and relationships and make use of data annotation attributes to
control the behavior of your model classes.
When you're finished, the entity classes will make up the completed data model that's shown in the following illustration:
In Models\Student.cs, add a
Run the Student Index page again and notice that times are no longer displayed for the enrollment dates. The same will be true if you run the other Student pages.
Run the Create page, enter two names longer than 50 characters, and click Create to see the error messages. (You'll have to enter a valid date in order to get past the date validation.)
It's a good idea to always specify the maximum length for string properties. If you don't, when Code First creates the database, the corresponding columns will have the maximum length allowed for strings in the database, which would be an inefficient database table structure.
The
Add the column name attribute to the
In the Properties window, you'll also notice that the name-related fields are defined as 50 characters in length, thanks to the
In most cases, you can also make mapping changes using method calls, as you'll see later in this tutorial.
In the following sections you'll make more use of data annotations attributes as you expand the
Create Models\Instructor.cs, replacing the existing code with the following code:
An instructor can teach any number of courses, so
Create Models\OfficeAssignment.cs, replacing the existing code with the following code:
In Models\Course.cs, replace the code you added earlier with the following code:
Create Models\Department.cs, replacing the existing code with the following code:
In Models\Student.cs, replace the code you added earlier with the following code:
In Models\Enrollment.cs, replace the code you added earlier with the following code:
The following illustration shows what these relationships look like in an entity diagram. (This diagram was generated using the Entity Framework designer; creating the diagram isn't part of the tutorial, it's just being used here as an illustration.)
Each relationship line has a 1 at one end and an asterisk (*) at the other, indicating a one-to-many relationship.
If the
A join table is required in the database, however, as shown in the following database diagram:
The Entity Framework automatically creates the
Besides the many-to-many relationship lines (* to *) and the one-to-many relationship lines (1 to *), you can see here the one-to-zero-or-one relationship line (1 to 0..1) between the
Replace the code in DAL\SchoolContext.cs with the following code:
The page looks the same as it did before, but behind the scenes the database has been re-created.
If you don't see the Student Index page and instead you get an error that indicates that the School.sdf file is in use (see the following illustration), you need to reopen Server Explorer and close the connection to the database. Then try displaying the Student Index page again.
After viewing the Student Index page, open the database in Server Explorer as you did earlier, and expand the Tables node to see that all of the tables have been created.
Besides
Right-click the
You now have a more complex data model and corresponding database. In the following tutorial you'll learn more about different ways to access related data.
When you're finished, the entity classes will make up the completed data model that's shown in the following illustration:
Using Attributes to Control Formatting, Validation, and Database Mapping
In this section you'll see examples of attributes you can add to model classes to specify formatting, validation, and database mapping. Then in the following sections you'll create the completeSchool
data model by adding attributes to the classes you already created and
creating new classes for the remaining entity types in the model.The DisplayFormat Attribute
For student enrollment dates, all of the web pages currently display the time along with the date, although all you care about for this field is the date. By using data annotation attributes, you can make one code change that will fix the display format everywhere. To see an example of that, you'll add an attribute to theEnrollmentDate
property in the Student
class.In Models\Student.cs, add a
using
statement for the System.ComponentModel.DataAnnotations
namespace and add a DisplayFormat
attribute to the EnrollmentDate
property, as shown in the following example:using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace ContosoUniversity.Models { public class Student { public int StudentID { get; set; } public string LastName { get; set; } public string FirstMidName { get; set; } [DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)] public DateTime EnrollmentDate { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } } }The format string specifies that only a short date should be displayed for this property. The
ApplyFormatInEditMode
setting specifies that this formatting should also be applied when the
value is displayed in a text box for editing. (You might not want that
for some fields — for example, for currency values, you might not want
the currency symbol in the text box for editing.)Run the Student Index page again and notice that times are no longer displayed for the enrollment dates. The same will be true if you run the other Student pages.
The MaxLength Attribute
You can also specify data validation rules and messages using attributes. Suppose you want to ensure that users don't enter more than 50 characters for a name. To add this limitation, addRange
attributes to the LastName
and FirstMidName
properties, as shown in the following example:using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace ContosoUniversity.Models { public class Student { public int StudentID { get; set; } [MaxLength(50)] public string LastName { get; set; } [MaxLength(50, ErrorMessage = "First name cannot be longer than 50 characters.")] public string FirstMidName { get; set; } [DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)] public DateTime EnrollmentDate { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } } }If a user attempts to enter a last name that's too long, a default error message will be displayed. If a long first name is entered, the custom error message you specified will be displayed.
Run the Create page, enter two names longer than 50 characters, and click Create to see the error messages. (You'll have to enter a valid date in order to get past the date validation.)
It's a good idea to always specify the maximum length for string properties. If you don't, when Code First creates the database, the corresponding columns will have the maximum length allowed for strings in the database, which would be an inefficient database table structure.
The Column Attribute
You can also use attributes to control how your classes and properties are mapped to the database. Suppose you had used the nameFirstMidName
for the first-name field because the field might also contain a middle name. But you want the database column to be named FirstName
,
because users who will be writing ad-hoc queries against the database
are accustomed to that name. To make this mapping, you can use the Column
attribute.The
Column
attribute specifies that when the database is created, the column of the Student
table that maps to the FirstMidName
property will be named FirstName
. In other words, when your code refers to Student.FirstMidName
, the data will come from or be updated in the FirstName
column of the Student
table. (If you don't specify column names, they are assumed to be the same as property names.)Add the column name attribute to the
FirstMidName
property, as shown in the following example: [Column("FirstName")] public string FirstMidName { get; set; }Run the Student Index page again and you see that nothing has changed. (You can't just run the site and view the home page; you have to select the Student Index page because that causes the database to be accessed, which causes the database to be automatically dropped and re-created.) However, if you open the database in Server Explorer as you did earlier, you can expand the
Student
table to see that the column name is FirstName
. In the Properties window, you'll also notice that the name-related fields are defined as 50 characters in length, thanks to the
MaxLength
attributes you added earlier.In most cases, you can also make mapping changes using method calls, as you'll see later in this tutorial.
In the following sections you'll make more use of data annotations attributes as you expand the
School
data model. In each section you'll create a class for an entity or modify a class that you created in the first tutorial.
Note If you try to compile before you finish creating all of these entity classes, you might get compiler errors.
Creating the Instructor Entity
Create Models\Instructor.cs, replacing the existing code with the following code:
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace ContosoUniversity.Models { public class Instructor { public Int32 InstructorID { get; set; } [Required(ErrorMessage = "Last name is required.")] [Display(Name="Last Name")] [MaxLength(50)] public string LastName { get; set; } [Required(ErrorMessage = "First name is required.")] [Column("FirstName")] [Display(Name = "First Name")] [MaxLength(50)] public string FirstMidName { get; set; } [DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)] [Required(ErrorMessage = "Hire date is required.")] [Display(Name = "Hire Date")] public DateTime? HireDate { get; set; } public string FullName { get { return LastName + ", " + FirstMidName; } } public virtual ICollection<Course> Courses { get; set; } public virtual OfficeAssignment OfficeAssignment { get; set; } } }Notice that several properties are the same in the
Student
and Instructor
entities. In the Implementing Inheritance tutorial later in this series, you'll refactor using inheritance to eliminate this redundancy.The Required and Display Attributes
The attributes on theLastName
property specify that
it's a required field, that the caption for the text box should be "Last
Name" (instead of the property name, which would be "LastName" with no
space), and that the value can't be longer than 50 characters.[Required(ErrorMessage = "Last name is required.")] [Display(Name="Last Name")] [MaxLength(50)] public string LastName { get; set; }
The FullName Calculated Property
FullName
is a calculated property that returns a value that's created by concatenating two other properties. Therefore it has only a get
accessor, and no FullName
column will be generated in the database.public string FullName { get { return LastName + ", " + FirstMidName; } }
The Courses and OfficeAssignment Navigation Properties
TheCourses
and OfficeAssignment
properties are navigation properties. As was explained earlier, they are typically defined as virtual
so that they can take advantage of an Entity Framework feature called
lazy loading. In addition, if a navigation property can hold multiple
entities, its type must be ICollection
. An instructor can teach any number of courses, so
Courses
is defined as a collection of Course
entities. On the other hand, an instructor can only have one office, so OfficeAssignment
is defined as a single OfficeAssignment
entity (which may be null if no office is assigned).public virtual ICollection<Course> Courses { get; set; } public virtual OfficeAssignment OfficeAssignment { get; set; }
Creating the OfficeAssignment Entity
Create Models\OfficeAssignment.cs, replacing the existing code with the following code:
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace ContosoUniversity.Models { public class OfficeAssignment { [Key] public int InstructorID { get; set; } [MaxLength(50)] [Display(Name = "Office Location")] public string Location { get; set; } public virtual Instructor Instructor { get; set; } } }
The Key Attribute
There's a one-to-zero-or-one relationship between theInstructor
and the OfficeAssignment
entities. An office assignment only exists in relation to the
instructor it's assigned to, and therefore its primary key is also its
foreign key to the Instructor
entity. But the Entity Framework can't automatically recognize InstructorID
as the primary key of this entity because its name doesn't follow the ID
or classnameID
naming convention. Therefore, the Key
attribute is used to identify it as the key:[Key] public int InstructorID { get; set; }You can also use the
Key
attribute if the entity does have its own primary key but you want to name the property something different than classnameID
or ID
. The Instructor Navigation Property
TheInstructor
entity has a nullable OfficeAssignment
navigation property (because an instructor might not have an office assignment), and the OfficeAssignment
entity has a non-nullable Instructor
navigation property (because an office assignment can't exist without an instructor). When an Instructor
entity has a related OfficeAssignment
entity, each entity will have a reference to the other one in its navigation property.Modifying the Course Entity
In Models\Course.cs, replace the code you added earlier with the following code:
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace ContosoUniversity.Models { public class Course { [DatabaseGenerated(DatabaseGeneratedOption.None)] [Display(Name = "Number")] public int CourseID { get; set; } [Required(ErrorMessage = "Title is required.")] [MaxLength(50)] public string Title { get; set; } [Required(ErrorMessage = "Number of credits is required.")] [Range(0,5,ErrorMessage="Number of credits must be between 0 and 5.")] public int Credits { get; set; } [Display(Name = "Department")] public int DepartmentID { get; set; } public virtual Department Department { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } public virtual ICollection<Instructor> Instructors { get; set; } } }
The DatabaseGenerated Attribute
TheDatabaseGenerated
attribute with the None
parameter on the CourseID
property specifies that primary key values are provided by the user rather than generated by the database.[DatabaseGenerated(DatabaseGeneratedOption.None)] [Display(Name = "Number")] public int CourseID { get; set; }By default, the Entity Framework assumes that primary key values are generated by the database. That's what you want in most scenarios. However, for
Course
entities, you'll use a user-specified
course number such as a 1000 series for one department, a 2000 series
for another department, and so on.Foreign Key and Navigation Properties
The foreign key properties and navigation properties in theCourse
entity reflect the following relationships:- A course is assigned to one department, so there's a
DepartmentID
foreign key and aDepartment
navigation property:public int DepartmentID { get; set; } public virtual Department Department { get; set; }
- A course can have any number of students enrolled in it, so there's an
Enrollments
navigation property:public virtual ICollection<Enrollment> Enrollments { get; set; }
- A course may be taught by multiple instructors, so there's an
Instructors
navigation property:public virtual ICollection<Instructor> Instructors { get; set; }
Creating the Department Entity
Create Models\Department.cs, replacing the existing code with the following code:
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace ContosoUniversity.Models { public class Department { public int DepartmentID { get; set; } [Required(ErrorMessage = "Department name is required.")] [MaxLength(50)] public string Name { get; set; } [DisplayFormat(DataFormatString="{0:c}")] [Required(ErrorMessage = "Budget is required.")] [Column(TypeName="money")] public decimal? Budget { get; set; } [DisplayFormat(DataFormatString="{0:d}", ApplyFormatInEditMode=true)] [Required(ErrorMessage = "Start date is required.")] public DateTime StartDate { get; set; } [Display(Name="Administrator")] public int? InstructorID { get; set; } public virtual Instructor Administrator { get; set; } public virtual ICollection<Course> Courses { get; set; } } }
The Column Attribute
Earlier you used theColumn
attribute to change column name mapping. In the code for the Department
entity, the Column
attribute is being used to change SQL data type mapping so that the column will be defined using the SQL Server money
type in the database:[Column(TypeName="money")] public decimal? Budget { get; set; }This is normally not required, because the Entity Framework chooses the appropriate SQL Server data type based on the CLR type that you define for the property. The CLR
decimal
type would normally map to a SQL Server decimal
type. But in this case you know that the column will be holding currency amounts, and the money
data type is more appropriate for that.Foreign Key and Navigation Properties
The foreign key and navigation properties reflect the following relationships:- A department may or may not have an administrator, and an administrator is always an instructor. Therefore the
InstructorID
property is included as the foreign key to theInstructor
entity, and a question mark is added after theint
type designation to mark the property as nullable. The navigation property is namedAdministrator
but holds anInstructor
entity:public int? InstructorID { get; set; } public virtual Instructor Administrator { get; set; }
- A department may have many courses, so there's a
Courses
navigation property:public virtual ICollection<Course> Courses { get; set; }
Note By convention, the Entity
Framework enables cascade delete for non-nullable foreign keys and for
many-to-many relationships. This can result in circular cascade delete
rules, which will cause an exception when your initializer code runs.
For example, if you didn't define the
Department.InstructorID
property as nullable, you'd get the following exception message when
the initializer runs: "The referential relationship will result in a
cyclical reference that's not allowed."Modifying the Student Entity
In Models\Student.cs, replace the code you added earlier with the following code:
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace ContosoUniversity.Models { public class Student { public int StudentID { get; set; } [Required(ErrorMessage = "Last name is required.")] [Display(Name="Last Name")] [MaxLength(50)] public string LastName { get; set; } [Required(ErrorMessage = "First name is required.")] [Column("FirstName")] [Display(Name = "First Name")] [MaxLength(50)] public string FirstMidName { get; set; } [Required(ErrorMessage = "Enrollment date is required.")] [DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)] [Display(Name = "Enrollment Date")] public DateTime? EnrollmentDate { get; set; } public string FullName { get { return LastName + ", " + FirstMidName; } } public virtual ICollection<Enrollment> Enrollments { get; set; } } }This code just adds attributes that you've now already seen in the other classes.
Modifying the Enrollment Entity
In Models\Enrollment.cs, replace the code you added earlier with the following code:
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace ContosoUniversity.Models { public class Enrollment { public int EnrollmentID { get; set; } public int CourseID { get; set; } public int StudentID { get; set; } [DisplayFormat(DataFormatString="{0:#.#}",ApplyFormatInEditMode=true,
NullDisplayText="No grade")] public decimal? Grade { get; set; } public virtual Course Course { get; set; } public virtual Student Student { get; set; } } }
Foreign Key and Navigation Properties
The foreign key properties and navigation properties reflect the following relationships:- An enrollment record is for a single course, so there's a
CourseID
foreign key property and aCourse
navigation property:public int CourseID { get; set; } public virtual Course Course { get; set; }
- An enrollment record is for a single student, so there's a
StudentID
foreign key property and aStudent
navigation property:public int StudentID { get; set; } public virtual Student Student { get; set; }
Many-to-Many Relationships
There's a many-to-many relationship between theStudent
and Course
entities, and the Enrollment
entity corresponds to a many-to-many join table with payload in the database. This means that the Enrollment
table contains additional data besides foreign keys for the joined tables (in this case, a primary key and a Grade
property). The following illustration shows what these relationships look like in an entity diagram. (This diagram was generated using the Entity Framework designer; creating the diagram isn't part of the tutorial, it's just being used here as an illustration.)
Each relationship line has a 1 at one end and an asterisk (*) at the other, indicating a one-to-many relationship.
If the
Enrollment
table didn't include grade information, it would only need to contain the two foreign keys CourseID
and StudentID
. In that case, it would correspond to a many-to-many join table without payload (or a pure join table) in the database, and you wouldn't have to create a model class for it at all. The Instructor
and Course
entities have that kind of many-to-many relationship, and as you can see, there is no entity class between them:A join table is required in the database, however, as shown in the following database diagram:
The Entity Framework automatically creates the
CourseInstructor
table, and you read and update it indirectly by reading and updating the Instructor.Courses
and Course.Instructors
navigation properties.The DisplayFormat Attribute
TheDisplayFormat
attribute on the Grade
property specifies how the data will be formatted:[DisplayFormat(DataFormatString="{0:#.#}",ApplyFormatInEditMode=true,
NullDisplayText="No grade")] public decimal? Grade { get; set; }
- The grade displays as two digits separated by a period — for example, "3.5" or "4.0".
- The grade is also displayed this way in edit mode (in a text box).
- If there's no grade (the question mark after
decimal
indicates that the property is nullable), the text "No grade" is displayed.
Entity Diagram Showing Relationships
The following illustration shows the diagram that the Entity Framework Database First designer creates for the School model.Besides the many-to-many relationship lines (* to *) and the one-to-many relationship lines (1 to *), you can see here the one-to-zero-or-one relationship line (1 to 0..1) between the
Instructor
and OfficeAssignment
entities and the zero-or-one-to-many relationship line (0..1 to *) between the Instructor and Department entities.Customizing the Database Context
Next you'll add the new entities to theSchoolContext
class and customize some of the mapping using fluent API calls. (The API
is "fluent" because it's often used by stringing a series of method
calls together into a single statement.) In some cases you need to use
methods rather than attributes because there's no attribute for a
particular function. In other cases you can choose to use a method when
both methods and attributes are available. (Some people prefer not to
use attributes.) Replace the code in DAL\SchoolContext.cs with the following code:
using System; using System.Collections.Generic; using System.Data.Entity; using ContosoUniversity.Models; using System.Data.Entity.ModelConfiguration.Conventions; namespace ContosoUniversity.Models { public class SchoolContext : DbContext { public DbSet<Course> Courses { get; set; } public DbSet<Department> Departments { get; set; } public DbSet<Enrollment> Enrollments { get; set; } public DbSet<Instructor> Instructors { get; set; } public DbSet<Student> Students { get; set; } public DbSet<OfficeAssignment> OfficeAssignments { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); modelBuilder.Entity<Instructor>() .HasOptional(p => p.OfficeAssignment).WithRequired(p => p.Instructor); modelBuilder.Entity<Course>() .HasMany(c => c.Instructors).WithMany(i => i.Courses) .Map(t => t.MapLeftKey("CourseID") .MapRightKey("InstructorID") .ToTable("CourseInstructor")); modelBuilder.Entity<Department>() .HasOptional(x => x.Administrator); } } }The new statements in the
OnModelCreating
method specify the following relationships:- A one-to-zero-or-one relationship between the
Instructor
andOfficeAssignment
entities:modelBuilder.Entity<Instructor>() .HasOptional(p => p.OfficeAssignment).WithRequired(p => p.Instructor);
- A many-to-many relationship between the
Instructor
andCourse
entities. The code specifies the table and column names for the join table. Code First can configure the many-to-many relationship for you without this code, but if you don't call it, you will get default names such asInstructorInstructorID
for theInstructorID
column.modelBuilder.Entity<Course>() .HasMany(c => c.Instructors).WithMany(i => i.Courses) .Map(t => t.MapLeftKey("CourseID") .MapRightKey("InstructorID") .ToTable("CourseInstructor"));
- A zero-or-one-to-many relationship between the Instructor and Department
tables. In other words, a department may or may not have an instructor
assigned to it as administrator; the assigned administrator is
represented by the
Department.Administrator
navigation property:modelBuilder.Entity<Department>() .HasOptional(x => x.Administrator);
Initializing the Database with Test Data
Earlier you created DAL\SchoolInitializer.cs to initialize your database with test data. Now replace the code in that file with the following code in order to provide test data for the new entities you've created.using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Data.Entity; using ContosoUniversity.Models; namespace ContosoUniversity.DAL{ public class SchoolInitializer : DropCreateDatabaseIfModelChanges<SchoolContext> { protected override void Seed(SchoolContext context) { var students = new List<Student> { new Student { FirstMidName = "Carson",
LastName = "Alexander", EnrollmentDate = DateTime.Parse("2005-09-01") }, new Student { FirstMidName = "Meredith",
LastName = "Alonso", EnrollmentDate = DateTime.Parse("2002-09-01") }, new Student { FirstMidName = "Arturo",
LastName = "Anand", EnrollmentDate = DateTime.Parse("2003-09-01") }, new Student { FirstMidName = "Gytis",
LastName = "Barzdukas", EnrollmentDate = DateTime.Parse("2002-09-01") }, new Student { FirstMidName = "Yan",
LastName = "Li", EnrollmentDate = DateTime.Parse("2002-09-01") }, new Student { FirstMidName = "Peggy",
LastName = "Justice", EnrollmentDate = DateTime.Parse("2001-09-01") }, new Student { FirstMidName = "Laura",
LastName = "Norman", EnrollmentDate = DateTime.Parse("2003-09-01") }, new Student { FirstMidName = "Nino",
LastName = "Olivetto", EnrollmentDate = DateTime.Parse("2005-09-01") } }; students.ForEach(s => context.Students.Add(s)); context.SaveChanges(); var instructors = new List<Instructor> { new Instructor { FirstMidName = "Kim",
LastName = "Abercrombie", HireDate = DateTime.Parse("1995-03-11") }, new Instructor { FirstMidName = "Fadi",
LastName = "Fakhouri", HireDate = DateTime.Parse("2002-07-06") }, new Instructor { FirstMidName = "Roger",
LastName = "Harui", HireDate = DateTime.Parse("1998-07-01") }, new Instructor { FirstMidName = "Candace",
LastName = "Kapoor", HireDate = DateTime.Parse("2001-01-15") }, new Instructor { FirstMidName = "Roger",
LastName = "Zheng", HireDate = DateTime.Parse("2004-02-12") } }; instructors.ForEach(s => context.Instructors.Add(s)); context.SaveChanges(); var departments = new List<Department> { new Department { Name = "English", Budget = 350000,
StartDate = DateTime.Parse("2007-09-01"), InstructorID = 1 }, new Department { Name = "Mathematics", Budget = 100000,
StartDate = DateTime.Parse("2007-09-01"), InstructorID = 2 }, new Department { Name = "Engineering", Budget = 350000,
StartDate = DateTime.Parse("2007-09-01"), InstructorID = 3 }, new Department { Name = "Economics", Budget = 100000,
StartDate = DateTime.Parse("2007-09-01"), InstructorID = 4 } }; departments.ForEach(s => context.Departments.Add(s)); context.SaveChanges(); var courses = new List<Course> { new Course { CourseID = 1050, Title = "Chemistry",
Credits = 3, DepartmentID = 3, Instructors = new List<Instructor>() }, new Course { CourseID = 4022, Title = "Microeconomics",
Credits = 3, DepartmentID = 4, Instructors = new List<Instructor>() }, new Course { CourseID = 4041, Title = "Macroeconomics",
Credits = 3, DepartmentID = 4, Instructors = new List<Instructor>() }, new Course { CourseID = 1045, Title = "Calculus",
Credits = 4, DepartmentID = 2, Instructors = new List<Instructor>() }, new Course { CourseID = 3141, Title = "Trigonometry",
Credits = 4, DepartmentID = 2, Instructors = new List<Instructor>() }, new Course { CourseID = 2021, Title = "Composition",
Credits = 3, DepartmentID = 1, Instructors = new List<Instructor>() }, new Course { CourseID = 2042, Title = "Literature",
Credits = 4, DepartmentID = 1, Instructors = new List<Instructor>() } }; courses.ForEach(s => context.Courses.Add(s)); context.SaveChanges(); courses[0].Instructors.Add(instructors[0]); courses[0].Instructors.Add(instructors[1]); courses[1].Instructors.Add(instructors[2]); courses[2].Instructors.Add(instructors[2]); courses[3].Instructors.Add(instructors[3]); courses[4].Instructors.Add(instructors[3]); courses[5].Instructors.Add(instructors[3]); courses[6].Instructors.Add(instructors[3]); context.SaveChanges(); var enrollments = new List<Enrollment> { new Enrollment { StudentID = 1, CourseID = 1050, Grade = 1 }, new Enrollment { StudentID = 1, CourseID = 4022, Grade = 3 }, new Enrollment { StudentID = 1, CourseID = 4041, Grade = 1 }, new Enrollment { StudentID = 2, CourseID = 1045, Grade = 2 }, new Enrollment { StudentID = 2, CourseID = 3141, Grade = 4 }, new Enrollment { StudentID = 2, CourseID = 2021, Grade = 4 }, new Enrollment { StudentID = 3, CourseID = 1050 }, new Enrollment { StudentID = 4, CourseID = 1050, }, new Enrollment { StudentID = 4, CourseID = 4022, Grade = 4 }, new Enrollment { StudentID = 5, CourseID = 4041, Grade = 3 }, new Enrollment { StudentID = 6, CourseID = 1045 }, new Enrollment { StudentID = 7, CourseID = 3141, Grade = 2 }, }; enrollments.ForEach(s => context.Enrollments.Add(s)); context.SaveChanges(); var officeAssignments = new List<OfficeAssignment> { new OfficeAssignment { InstructorID = 1, Location = "Smith 17" }, new OfficeAssignment { InstructorID = 2, Location = "Gowan 27" }, new OfficeAssignment { InstructorID = 3, Location = "Thompson 304" }, }; officeAssignments.ForEach(s => context.OfficeAssignments.Add(s)); context.SaveChanges(); } } }As you saw in the first tutorial, most of this code simply creates new entity objects and loads sample data into properties as required for testing. However, notice how the
Course
entity, which has a many-to-many relationship with the Instructor
entity, is handled:var courses = new List { new Course { CourseID = 1050, Title = "Chemistry", Credits = 3,
DepartmentID = 3, Instructors = new List() }, ... }; courses.ForEach(s => context.Courses.Add(s)); context.SaveChanges(); courses[0].Instructors.Add(instructors[0]); ... context.SaveChanges();When you create a
Course
object, you initialize the Instructors
navigation property as an empty collection using the code Instructors = new List()
. This makes it possible to add Instructor
entities that are related to this Course
by using the Instructors.Add
method. If you didn't create an empty list, you wouldn't be able to add these relationships, because the Instructors
property would be null and wouldn't have an Add
method.
Note Remember that when you deploy an application
to a production web server, you must remove any code you've added to
seed the database.
Dropping and Re-Creating the Database
Now run the site and select the Student Index page.The page looks the same as it did before, but behind the scenes the database has been re-created.
If you don't see the Student Index page and instead you get an error that indicates that the School.sdf file is in use (see the following illustration), you need to reopen Server Explorer and close the connection to the database. Then try displaying the Student Index page again.
After viewing the Student Index page, open the database in Server Explorer as you did earlier, and expand the Tables node to see that all of the tables have been created.
Besides
EdmMetadata
, you see one table you didn't create a model class for: CourseInstructor
. As explained earlier, this is a join table for the many-to-many relationship between the Instructor
and Course
entities.Right-click the
CourseInstructor
table and select Show Table Data to verify that it has data in it as a result of the Instructor
entities you added to the Course.Instructors
navigation property.You now have a more complex data model and corresponding database. In the following tutorial you'll learn more about different ways to access related data.
No comments:
Post a Comment