Convention
Core to Code First is the concept of conventions—default rules that Code First will use to build a model based on your classes. For example, Entity Framework requires that a class that it will manage has a key property. Code First has a convention that if it finds a property named Id or a property with the combined name of the type name and Id (e.g., PatientId), that property will be automatically configured as the key. If it can’t find a property that matches this convention, it will throw an exception at runtime telling you that there is no key.
only defining the domain classes have nothing to do with the Entity Framework.They have no knowledge of it. That’s the beauty of working with Code First. You get
to use your own classes. This is especially beneficial if you have existing domain classes from another project. To use Code First, you start by defining a class that inherits from DbContext. One of the roles of this class, which we’ll refer to as a context, is to let Code First know about the classes that make up your model. That’s how Entity Framework will be aware of them and be able to keep track of them. This is done by exposing the domain classes through another new class introduced along with DbContext—the DbSet. Just as DbCon text is a simpler wrapper around the ObjectContext, DbSet is a wrapper around Entity Framework 4’s ObjectSet, which simplifies coding tasks for which we normally use the ObjectSet.
If you have worked with Entity Framework, you are familiar with the model that is expressed in an EDMX file that you work with in a visual designer. You may also be
aware of the fact that the EDMX file is in fact an XML file, but the designer makes it much easier to work with. The XML used to describe the model has a very specific
schema and working with the raw XML would be mind-boggling without the designer. What is not as obvious in the designer is that the XML contains more than just the description of the conceptual model that is displayed in the designer. It also has a description of database schema that the classes map to and one last bit of XML that describes how to get from the classes and properties to the tables and columns in the database. The combination of the model XML, the database schema XML, and the mapping XML are referred to as metadata.
At runtime, the Entity Framework reads the XML that describes all three parts of the XML and creates an in-memory representation of the metadata. But the in-memory metadata is not the XML; it is strongly typed objects such as EntityType, EdmProperty, and AssociationType. Entity Framework interacts with this in-memory representation of the model and schema every time it needs to interact with the database. Because there is no XML file with Code First, it creates the in-memory metadata from what it can discover in your domain classes. This is where convention and configuration
come into play. Code First has a class called the DbModelBuilder. It is the DbModel Builder that reads the classes and builds the in-memory model to the best of its ability.
Since it is also building the portion of the metadata that represents the database schema, it is able to use that to create the database. If you add configurations to help the model builder determine what the model and database schema should look like, it will read those just after it inspects the classes and incorporate that information into its understanding of what the model and database schema should look like. Figure 1-4 shows how Entity Framework can build the in-memory model from code or from an XML file maintained through the designer. Once the in-memory model is created, Entity Framework doesn’t need to know how the model was created. It can use the in-memory model to determine what the database schema should look like, build queries to access data, translate the results of queries into your objects, and persist changes to those objects back to the database.
Convention and Configuration For Property Attribute
Configuring the Key with Data Annotations
[Key] public Guid Identifier { get; set; }
Using HasKey to Configure a Key Property in the Fluent API
modelBuilder.Entity<Trip>().HasKey(t => t.Identifier)
If you are encapsulating the configurations within EntityTypeConfiguration classes, as
you learned about in Chapter 2, you begin with HasKey or this.HasKey
HasKey(t => t.Identifier)
Code First Convention and TimeStamp fields
By default, Code First does not recognize TimeStamp properties, so there is no conventional behavior. You must configure properties to get the behavior. Using Data Annotations to Configure TimeStamp Not just any property can be mapped to a timestamp database type. You must use a byte array. With that, the Data Annotation is simple: TimeStamp.
[Timestamp] public byte[] RowVersion { get; set; }
Example 3-8. INSERT combined with SELECT to return new RowVersion
exec sp_executesql N 'insert [dbo].[People]([SocialSecurityNumber], [FirstName], [LastName]) values (@0, @1, @2) select [RowVersion] from [dbo].[People] where @@ROWCOUNT > 0 and [SocialSecurityNumber] = @0', N'@0 int,@1 nvarchar(max) ,@2 nvarchar(max) ',@0=12345678,@1=N'Rowan',@2=N'Miller'
Not only does Entity Framework tell the database to perform the INSERT, but it also requests the RowVersion value back. EF will always do this with a property that is flagged for concurrency, even if it is not a timestamp value. Even more critical are the UPDATE and DELETE commands, because here is where the
concurrency check occurs. We’ve added a new method to the app, UpdatePerson, shown in Example 3-9.
Example 3-9. The UpdateTrip method
private static void UpdateTrip() { using (var context = new BreakAwayContext()) { var trip = context.Trips.FirstOrDefault(); trip.CostUSD = 750; context.SaveChanges(); } }
Example 3-10 shows the SQL executed when SaveChanges is called in the UpdateTrip
method.
Example 3-10. UPDATE that filters on original RowVersion and returns new RowVersion
exec sp_executesql N'update [dbo].[Trips] set [CostUSD] = @0 where (([Identifier] = @1) and ([RowVersion] = @2)) select [RowVersion] from [dbo].[Trips] where @@ROWCOUNT > 0 and [Identifier] = @1', N'@0 decimal(18,2),@1 uniqueidentifier,@2 binary(8)', @0=750.00,@1='D1086EFE-5C5B-405D-9F09-688981BB5B41',@2=0x0000000000001773
Notice the where predicate used to locate the trip being updated—it filters on the Identifier and the RowVersion. If someone else has modified the trip since it was retrieved by our method, the RowVersion will have changed and there will be no row that matches the filter. The UPDATE will fail and Entity Framework will throw an Optimistic ConcurrencyException.
Configuring TimeStamp/RowVersion with Fluent API
While the Data Annotation uses the term TimeStamp, the Fluent configuration uses the term RowVersion. To specify a RowVersion property, append the IsRowVersion() method to the Property. With DbModelBuilder, you configure the Property like this:
modelBuilder.Entity<Person>()
.Property(p => p.RowVersion).IsRowVersion();
Inside an EntityTypeConfiguration<T> class the configuration looks like:
Property(p => p.RowVersion).IsRowVersion();
Convention and Configuration For Relationship
Example 1:
public class Destination { public int DestinationId { get; set; } public string Name { get; set; } public string Country { get; set; } public string Description { get; set; } public byte[] Photo { get; set; } public List<Lodging> Lodgings { get; set; } } public class Lodging { public int LodgingId { get; set; } public string Name { get; set; } public bool IsResort { get; set; } public decimal MilesFromNearestAirport { get; set; } public Destination Destination { get; set; }// Same DB schema will be created without it. }
private static void CreateNewDests() { Destination dest = new Destination { Name = "dest1" }; List<Lodging> Lodgings = new List<Lodging>(); Lodging lodging1 = new Lodging() { //Destination = dest, IsResort = false, Name = "lodging1" }; Lodging lodging2 = new Lodging() { //Destination = dest, IsResort = false, Name = "lodging2" }; Lodgings.Add(lodging1); Lodgings.Add(lodging2); dest.Lodgings = Lodgings; using (var context = new VetContext()) { context.Dests.Add(dest); Console.WriteLine(context.Database.Connection.ConnectionString); int i = context.SaveChanges(); Console.WriteLine("{0} records added...", i); Console.ReadLine(); } }
//3 rows will created in 2 table.
by convention it will configure this as a one-to-many relationship. Based on this, Code First can also determine thatLodging
is the dependent end of the relationship (the end with the foreign key) and Destination
is the principal end (the end with the primary key). It therefore knows that the table Lodging
maps to will need a foreign key pointing back to the primary key of Destination
. You saw this played out in Chapter 2, where it created the Destination_DestinationId
foreign key field in the Lodgings
table.
Rules:
If your classes contain a reference and a collection navigation property, Code First assumes a one-to-many relationship.(example 1)
Code First will also assume a one-to-many relationship if your classes include a navigation property on only one side of the relationship (i.e., either the collection or the reference, but not both).(example 1 if destination remove lodging list . lodging keep the Destination property. will create same schema.)
If your classes include two collection properties, Code First will use a many-to-many relationship by default.
If your classes include two reference properties, Code First will assume a one-to-one relationship.
In the case of one-to-one relationships, you will need to provide some additional information so that Code First knows which entity is the principal and which is the dependent. You’ll see this in action a little later on in this chapter, in the Working with One-to-One Relationships section. If no foreign key property is defined in your classes, Code First will assume the relationship is optional (i.e., the one end of the relationship is actually zero-or-one as opposed to exactly-one).
In the Working with Foreign Keys section of this chapter, you will see that when you define a foreign key property in your classes, Code First uses the nullability of that property to determine if the relationship is required or optional.
Most of the multiplicity configuration needs to be done using the Fluent API. But we can use Data Annotations to specify that a relationship is required. This is as simple as placing the Required
annotation on the reference property that you want to be required. Modify Lodging
by adding the Required
annotation to the Destination
property (Example 4-3).
Example 4-3. Required annotation added to Destination property
public class Lodging { public int LodgingId { get; set; } public string Name { get; set; } public string Owner { get; set; } public bool IsResort { get; set; } public decimal MilesFromNearestAirport { get; set; } [Required] public Destination Destination { get; set; }//Will be not null }
Configuring relationships with the Fluent API can look confusing if you haven’t taken the time to understand the fundamental ideas. We’ll lead you down the path to enlightenment.
When fixing relationships with Data Annotations, you apply annotations directly to the navigation properties. It’s very different with the Fluent API, where you are literally configuring the relationship, not a property. In order to do so, you must first identify the relationship. Sometimes it’s enough to mention one end, but most often you need to describe the complete relationship.
To identify a relationship, you point to its navigation properties. Regardless of which end you begin with, this is the pattern:
Entity.Has[Multiplicity](Property).With[Multiplicity](Property)
The multiplicity can be Optional
(a property that can have a single instance or be null), Required
(a property that must have a single instance), or Many
(a property with a collection of a single type).
The Has methods are as follows:
HasOptional
HasRequired
HasMany
In most cases you will follow the Has
method with one of the following With
methods:
WithOptional
WithRequired
WithMany
Example 4-4 shows a concrete example using the existing one-to-many relationship between Destination
and Lodging
. This configuration doesn’t really do anything, because it is configuring exactly what Code First detected by convention. Later in this chapter, you will see that this approach is used to identify a relationship so that you can perform further configuration related to foreign keys and cascade delete.
Example 4-4. Specifying an optional one-to-many relationship
modelBuilder.Entity<Destination>() .HasMany(d => d.Lodgings) .WithOptional(l => l.Destination);
Example 4-5 shows what this same configuration would look like inside an Entity
Type
Configuration
class rather than directly inside OnModelCreating
.
Example 4-5. Specifying a relationship in an EntityConfiguration class
HasMany(d => d.Lodgings)
.WithOptional(l => l.Destination);
This identifies a relationship that Destination Has
. It has a Many
relationship that is defined by its property, Lodgings
. And the Lodgings
end of the relationship comes along With
a relationship (which is Optional
) toDestination
In the previous section you added some configuration to make the Lodging
to Destination
relationship required. Go ahead and remove this configuration so that we can observe the Code First conventions in action. With the configuration removed, add a DestinationId
property into the Lodging
class:
public int DestinationId { get; set; }
WHY FOREIGN KEY PROPERTIES?
It’s common when coding to want to identify a relationship with another class. For example, you may be creating a new Lodging
and want to specify which Destination
the Lodging
is associated with. If the particular destination is in memory, you can set the relationship through the navigation property:
myLodging.Destination=myDestinationInstance;
However, if the destination is not in memory, this would require you to first execute a query on the database to retrieve that destination so that you can set the property. There are times when you may not have the object in memory, but you do have access to that object’s key value. With a foreign key property, you can simply use the key value without depending on having that instance in memory:
myLodging.DestinationId=3;
Additionally, in the specific case when the Lodging
is new and you attach the pre-existing Destination
instance, there are scenarios where Entity Framework will set the Destination
’s state to Added
even though it already exists in the database. If you are only working with the foreign key, you can avoid this problem.
Entity Framework supports many-to-many relationships. Let’s see how Code First responds to a many-to-many relationship between two classes when generating a database.
If you’ve had many-to-many relationships when using the database-first strategy, you may be familiar with the fact that Entity Framework can create many-to-many mappings when the database join table contains only the primary keys of the related entities. This mapping rule is the same for Code First.
Let’s add a new Activity
class to the model. Activity
, shown in Example 4-15, will be related to the Trip
class. A Trip
can have a number of Activities
scheduled and an Activity can be scheduled for a variety of trips. Therefore Trip
and Activity
will have a many-to-many relationship.
Example 4-15. A new class, Activity
using System.ComponentModel.DataAnnotations; using System.Collections.Generic; namespace Model { public class Activity { public int ActivityId { get; set; } [Required,MaxLength(50)] public string Name { get; set; } public List<Trip> Trips { get; set; } } }
There’s a List<Trip>
in the Activity
class. Let’s also add a List<Activity>
to the Trip
class for the other end of the many-to-many relationship:
public List<Activity> Activities { get; set; }
When you run the application again, Code First will recreate the database because of the model changes. Code First convention will recognize the many-to-many relationship and build a join table in the database with the appropriate keys of the tables it’s joining. The keys are both primary keys of the join table and foreign keys pointing to the joined tables, as shown in Figure 4-10.
Code First experience
1、Create Code First Model Firstly.
public class Exam { public Exam() { this.ExamStus = new List<ExamStudent>(); } public int Id { get; set; } public string Name { get; set; } public string Address { get; set; } public virtual ICollection<ExamStudent> ExamStus { get; set; } } public class ExamStudent { public int Id { get; set; } public string StuName { get; set; } public int ExamId { get; set; } public virtual Exam Exam { get; set; } }
2、Add Entity FrameWork reference use Nuget.
3、Add ExamContext class.
public class ExamContext:DbContext { public DbSet<Exam> Exams { get; set; } }
4、Write some CRUD code sample.
static void Main(string[] args) { Database.SetInitializer(new DropCreateDatabaseIfModelChanges<ExamContext>()); CreateExam(); Console.ReadKey(); } private static void CreateExam() { var exam = new Exam { Address="wq1", Name="second" }; var stu1 = new ExamStudent() { StuName = "stu3", Exam = exam }; var stu2 = new ExamStudent() { StuName = "stu4", Exam = exam }; exam.ExamStus = new List<ExamStudent>(); exam.ExamStus.Add(stu1); exam.ExamStus.Add(stu2); using (var context = new ExamContext()) { context.Exams.Add(exam); context.SaveChanges(); } }
5、no database and no connection string . after running . a database will be created in the sql express .
6、if you want to continue to use the new created database . following these steps .
* add <connectionstring> element to app.config.
<connectionStrings> <add name="ExamContext" connectionString="Data Source=.;Initial Catalog=TestSimpleCodeFirst.ExamContext;Integrated Security=True;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" /> </connectionStrings>
* and use the setting
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<ExamContext>());
drop db when model changes.
the table relationship diagram shows there is a forgein key between exam and examstudent .
but . even you remove the constraint of these table. it does not matter .