原文:http://www.codeproject.com/Articles/813912/Create-Primary-Key-using-Entity-Framework-Code-Fir
Introduction
This article describes the effect of Entity Framework Code First convention and configuration for creating Primary Key column.
Entity Framework requires each entity to have a key. By using this key, Entity Framework keeps track of the changes that happens to that entity.
The followings are three different ways to create primary key using Code First:
Convention: Property with name "Id" or property with name {class name} + "Id" (both are case insensitive)
Data Annotation: [Key] attribute
Fluent API: Entity<T>.HasKey(p => p.PropertyName) function
Using the code
The examples given in this article are created by using Visual Studio 2013 and SQL Server 2008.
To begin with let‘s create a Console Application.
1) From the Visual Studio click File -> New -> Project. Alternatively, you can click the "New Project..." link from the Visual Studio Start Page(View -> Start Page). In either way it will bring the "New Project" window.
In the "New Project" window, from the installed templates select "Visual C#". From the middle panel, which contains list of Visual C# templates, select "Console Application".
2) Add "EntityFramework" nuget package to the Console Application.
a) If you want to install from Package Manager Console(View -> Other Windows -> Package Manager Console) use command:
Install-Package EntityFramework
b) You can install the same from the Manage Nuget Package GUI. Right click on the console project and select "Manage Nuget packages". It will bring the "Manage Nuget Packages" window. From the left side select "Online" tab and search for "EntityFramework". Choose the correct package and click "Install".
3) Add a folder called "Models" to the root of the Console Application. Add two class files Student.cs and EducationContext.cs to the "Models" folder.
The EducationContext.cs file will act as the context object for the application which will keep track of all the changes that will happen to the entities. Inherit the EducationContext class from DbContext like follows:
Hide Copy Code
public class EducationContext : DbContext
Create a parameterized constructor inside EducationContext class which takes the connections string name as the parameter. This step is used for pointing to the correct database from the "App.Config" file.
Hide Copy Code
public EducationContext() : base("EducationEntities") { }
You can replace base("EducationEntities") with base("name=EducationEntities") which does the same. It informs Code First to use the connection string where name = "EducationEntities" from the App.config file(as it is a Console Application).
Now add <connectionStrings> section to the App.config file as follows:
Hide Copy Code
<connectionStrings> <add connectionString="Server={ServeName}; Database=Education; Integrated Security=SSPI" name="EducationEntities" providerName="System.Data.SqlClient"> </add> </connectionStrings>
For my case, the {ServerName} was DUKHABANDHU-PC(computer name) as I was using the windows credentials to login to MS SQL Server in my PC. Please change it according to your system or server. If you are facing problem in setting up the connection strings you can refer to http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.connectionstring%28VS.71%29.aspx and http://www.connectionstrings.com/sql-server-2008/ links.
Here I have given the database name as "Education". You can change it to anything you like.
4) Add a property "Id" of type int inside the Student.cs file.
Hide Copy Code
public class Student { public int Id { get; set; } }
The Students class will act as an entity for the EducationContext. So create a property of type DbSet<Student> inside the EducationContext.cs file as follows:
Hide Copy Code
public class EducationContext : DbContext { public DbSet<Student> Students { get; set; } }
5) Modify the Program.cs file to drop and recreate the database everytime when the application runs. It will not be a problem here if we will drop and recreate the database as we are not going to save any important data.
Hide Copy Code
static void Main(string[] args) { Database.SetInitializer(new DropCreateDatabaseAlways<EducationContext>()); using (var context = new EducationContext()) { context.Students.Add(new Student()); context.SaveChanges(); } Console.WriteLine("Database Created!!!"); Console.ReadKey(); }
Now run the application(From Visual Studio click the "Start" button or press F5). In the SQL Server you can see there is database with name Education having two tables: _MigrationHistory and Students.
The _MigrationHistory table(previous EdmMetadata table upto EF 4.2) is used to keep track of all the changes that happens to the model. The Students table is created as we have created a property "Students" of type DbSet<Student> inside EducationContext class.
Points of Interest
1) Using Convention
The Code First primary key convention is: Property with name "Id" or {class name} + "Id" will act as the primary key for that entity.
a) In the Student.cs file there is a single property "Id" of type int. It matches with the Code First convention for creating primary key. Hence there is a "Id" column in the "Students" table which is the primary key. See the below screenshot:
The SQL for the Students table is:
Hide Copy Code
CREATE TABLE [dbo].[Students]( [Id] [int] IDENTITY(1,1) NOT NULL, CONSTRAINT [PK_dbo.Students] PRIMARY KEY CLUSTERED ( [Id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY]
b) Let‘s remove the "Id" property from the Student.cs file and add another property called "StudentId" which matches the convention {class name} + "Id" i.e "Student" + "Id". The Student class now looks like:
Hide Copy Code
public class Student { public int StudentId { get; set; } }
If you will run the application, it will create _MigrationHistory and Students tables where "StudentId" is the primary key of the Students table.
Sometimes Code First fails to drop the database and generates an exception as follows. It generally happens if some process is using the SQL Server database and during that time Code First tries to drop that database. If it happens, manually delete the database from the SQL Server.
SqlException:
Cannot drop database "Education" because it is currently in use.
To delete the database manually, Right Click on the Database and then select the "Delete" option from the context menu. Alternatively, select the database and press Delete key. In either way it will bring up the "Delete Object" window. Check the "Close existing connection" checkbox from the bottom and click "OK" button.
Note:
The convention for creating the primary key is case insensitive i.e if you will change the case of the properties to lowercase(e.g studentid), uppercase(STUDENTID) or mixedcase(e.g STuDentiD) then it will still treat that property as the primary key but the name of the column in database will be in property case. So if you will name the property like STudenTiD then in Students table it will be the primary key and the column name will be STudenTiD.
c) Let‘s modify the Student.cs file and add two propeties Id and StudentId as follows:
Hide Copy Code
public class Student { public int Id { get; set; } public int StudentId { get; set; } }
Here both Id and StudentId matches the convention for creating the primay key. Now run the application to see the effect in the database:
As shown in the below screenshot, it creates Students table with Id as the primary key of the table.
If you change the order of the properties this will not change the primary key i.e still Id will be the primary key.
2) Using [Key] Data annotation attribute
a) The [Key] attribute is used for creating primary key. It takes priority over the convention.
Let‘s add two properties Id and StudentId in Student.cs file and add [Key] attribute to StudentId property as follows:
Hide Copy Code
public class Student { public int Id { get; set; } [Key] public int StudentId { get; set; } }
If you run the application, it will create "Students" table with StudentId as the primary key. Here data annotation attribute take priority over the convention.
b) Composite Primary Key: Composite key is the combination of two or more columns which uniquely identifies a row in a table.
Let‘s add a new "Passport.cs" file to the console application with two properties PassportNumber and CountryCode. Both PassportNumber and CountryCode uniquely identifies a record in Passports table. The class looks like:
Hide Copy Code
public class Passport { [Key] public string PassportNumber { get; set; } [Key] public string CountryCode { get; set; } }
Add another property "Passports" of type DbSet<Passport> inside the EducationContext.cs file as follows:
Hide Copy Code
public class EducationContext : DbContext { public DbSet<Student> Students { get; set; } public DbSet<Passport> Passports { get; set; } public EducationContext() : base("EducationEntities") { } }
If you run the application it will throw an exception as follows:
Exception:
Unable to determine composite primary key ordering for type ‘CodeFirst.Models.Passport‘. Use the ColumnAttribute (see http://go.microsoft.com/fwlink/?LinkId=386388) or the HasKey method (see http://go.microsoft.com/fwlink/?LinkId=386387) to specify an order for composite primary keys.
It clearly says that you have to use [Column] attribute if you are creating composite key using [Key] data annotation attribute.
So let‘s modify the Passport.cs file to use [Column] attribute by specifying column order. The column order is the relative value of the columns in the table. It doesn‘t need to be index based.
Modify the Passport.cs file as follows:
Hide Copy Code
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace CodeFirst.Models { public class Passport { [Key] [Column(Order = 10)] public string PassportNumber { get; set; } [Key] [Column(Order = 20)] public string CountryCode { get; set; } } }
If you run the application, it will create Passports table with PassportNumber and CountryCode as the composite primary key.
The SQL for Passports table is:
Hide Copy Code
CREATE TABLE [dbo].[Passports]( [PassportNumber] [nvarchar](128) NOT NULL, [CountryCode] [nvarchar](128) NOT NULL, CONSTRAINT [PK_dbo.Passports] PRIMARY KEY CLUSTERED ( [PassportNumber] ASC, [CountryCode] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY]
3) Using Fluent API
Fluent API is the programming approach where the current used method provides valuable method intellisense/option for next call. The Entity Framework Code First Fluent API provides several useful methods to do the mappings. For defining the primary key it provides HasKey() method. The Fluent API takes priority over the Data Annotation attributes.
To specify the mappings using Code First Fluent API, we have to override the OnModelCreating() method. The OnModelCreating() method is called before the models are created.
a) Let‘s modify the EducationContext.cs file as follows to use the Fluent API mappings:
Hide Copy Code
public class EducationContext : DbContext { public DbSet<Student> Students { get; set; } public DbSet<Passport> Passports { get; set; } public EducationContext() : base("EducationEntities") { } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Student>().HasKey(s => s.Id); base.OnModelCreating(modelBuilder); } }
Here we have set "Id" as the primary key for the Students table.
The Student class looks like:
Hide Copy Code
public class Student { public int Id { get; set; } [Key] public int StudentId { get; set; } }
If you will run the application, it will create Students table with Id as the primary key. Please note that we have [Key] attribute assigned to the StudentId property in Student class. Here the Fluent API takes priority over data annotation.
b) Composite Primary Key using Fluent API: It is easy to create composite primary key using Code First Fluent API. Modify OnModelCreating() method inside EducationContext class as follows:
Hide Copy Code
protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Passport>().HasKey(s => new { s.PassportNumber, s.CountryCode}); base.OnModelCreating(modelBuilder); }
Here we are creating a composite primary key taking PassportNumber and CountryCode properties. If you run the application, it will create the Passports table with composite primary key as follows:
Note:
Whenever Code First creates primary key(for intergral data types like int, long etc.) by convention, data annotation or fluent API, it creates primary key column with identity turned on. You can turn off the identity by using DatabaseGeneratedOption.None enum. For primary key property whose data type is Guid, Code First doesn‘t create identity by default for that column. You can turn the identity on by using DatabaseGeneratedOption.Identity enum. Entity Framework uses DatabaseGeneratedOption to decide what to do with the value of key attribute while inserting/updating records.
DatabaseGeneratedOption enum has three members(see documentation):
Computed: The database generates a value when a row is inserted or updated.
Identity: The database generates a value when a row is inserted.
None: The database does not generate values.
Using Data Annotation:
Hide Copy Code
[DatabaseGenerated(DatabaseGeneratedOption.None)] public int Id { get; set; }
Using Fluent API:
Hide Copy Code
modelBuilder.Entity<Student>().Property(s => s.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
Conclusion
There are three different ways to create primary key using Code First. But Fluent API method is more preferable as it separates the mapping logic from the domain classes. The data annotation attributes are limited. There are certain mappings which cannot be done using data annotation attribute but can be done by using Fluent API. Fluent API has the highest priority among the three for creating the tables and columns. Before creating the model, Code First takes convention into consideration first then reads the data annotation attributes and finally reads the mappings specified by Fluent API. The order of priorities for creating primary key using Entity Framework Code First is:
Convention ---> Data Annotation ----> Fluent API (Highest Priority)
Notes to use the attached source code
The attached source code contains the final version of the walk-through. However, there are commented codes which can be used to test the Code First convention and configuration for creating primary key. Before running the application, please change the connection string according to your system or server.
The "Download source (Packages Excluded)" zipped file contains the light version of the demo where the packages are excluded. Please restore the packages if you are using this version.
License
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)