Click here to Skip to main content
15,867,453 members
Articles / Database Development / SQL Server / SQL Server 2008R2

Create Primary Key using Entity Framework Code First

Rate me:
Please Sign up or sign in to vote.
4.92/5 (25 votes)
4 Sep 2014CPOL9 min read 210.1K   1.1K   33   10
Explore the Entity Framework Code First convention and configuration for creating primary key

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.

Listed below are three different ways to create primary key using Code First:

  1. Convention: Property with name "Id" or property with name {class name} + "Id" (both are case insensitive)
  2. Data Annotation: [Key] attribute
  3. 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 Visual Studio, click File -> New -> Project. Alternatively, you can click the "New Project..." link from the Visual Studio Start Page (View -> Start Page). Either ways, 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".

    Image 1

  2. Add "EntityFramework" nuget package to the Console Application.
    1. If you want to install from Package Manager Console (View -> Other Windows -> Package Manager Console) use command:
      Install-Package EntityFramework
    2. 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".

    Image 2

  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:

    C#
    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.

    C++
    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:

    XML
    <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 problems in setting up the connection strings, you can refer to this link and this link.

    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.
    C#
    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:

    C#
    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.
    C#
    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 that 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 happen 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:

Image 3

The SQL for the Students table is:

C#
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:

C#
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.

Image 4

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. Either ways, 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 properties, Id and StudentId as follows:

C#
public class Student
{
   public int Id { get; set; }
   public int StudentId { get; set; }
}

Here, both Id and StudentId matches the convention for creating the primary 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.

Image 5

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:

C#
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 takes 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 identify a record in Passports table. The class looks like:

C#
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:

C#
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:

C#
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.

Image 6

The SQL for Passports table is:

SQL
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:

C#
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:

C#
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.

Image 7

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:

C#
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:

Image 8

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):

  1. Computed: The database generates a value when a row is inserted or updated.
  2. Identity: The database generates a value when a row is inserted.
  3. None: The database does not generate values.

Using Data Annotation

C#
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }

Using Fluent API

C#
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 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.

History

  • 5th September, 2014: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
India India
I have developed web applications using ASP.NET Web Forms, MVC, Web API, Entity Framework, SQL Server, LINQ, C#, jQuery, BootStrap UI and few other related libraries and tools.

Apart from programming, I love to watch cricket matches.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Humayun Kabir Mamun2-Mar-15 22:38
Humayun Kabir Mamun2-Mar-15 22:38 
QuestionNice Job except no ORM for student and passport (composite key) Pin
Member 769299316-Dec-14 21:09
Member 769299316-Dec-14 21:09 
QuestionGood One Pin
Shemeemsha (ഷെമീംഷ)16-Sep-14 23:16
Shemeemsha (ഷെമീംഷ)16-Sep-14 23:16 
AnswerRe: Good One Pin
Dukhabandhu Sahoo17-Sep-14 2:33
professionalDukhabandhu Sahoo17-Sep-14 2:33 
GeneralMy vote of 4 Pin
M Rayhan5-Sep-14 1:39
M Rayhan5-Sep-14 1:39 
GeneralRe: My vote of 4 Pin
Dukhabandhu Sahoo22-Sep-14 14:28
professionalDukhabandhu Sahoo22-Sep-14 14:28 
Questionkeep going. Pin
Smrutiranjan Parida5-Sep-14 1:21
professionalSmrutiranjan Parida5-Sep-14 1:21 
AnswerRe: keep going. Pin
Dukhabandhu Sahoo5-Sep-14 1:24
professionalDukhabandhu Sahoo5-Sep-14 1:24 
GeneralMy vote of 5 Pin
Humayun Kabir Mamun4-Sep-14 19:43
Humayun Kabir Mamun4-Sep-14 19:43 
GeneralRe: My vote of 5 Pin
Dukhabandhu Sahoo22-Sep-14 14:28
professionalDukhabandhu Sahoo22-Sep-14 14:28 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.