Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / .NET

A Gentle Introduction to Entity Framework 6 from an NHibernate Perspective

4.00/5 (1 vote)
28 Apr 2014CPOL6 min read 8.2K  
A gentle introduction to Entity framework 6 from an NHibernate perspective

If you have been using Hibernate/NHibernate for a while before the introduction of its fluent API, you are used to a certain workflow. You define your entity classes. Then, you create a hibernate configuration file that defines mappings between the database tables and your entity classes. And that was about it.

With the Entity Framework 6, henceforth referred to as EF6, you can have several approaches: Database first, Model first or Code first. While each of these approaches cater to a different type of audience, they also present an immediate decision point of entry to the beginner, possibly hindering adoption and setting the stage ripe for a rough ride of perceptions whereby every aspect of the technology seems to appear overly engineered or complicated.

This blog attempts a gentle introduction to the Entity Framework. It is especially catered towards those developers who are used to the Hibernate workflow or similar. Such a workflow corresponds to the Entity Framework Code First approach. Along the way, commonalities and differences between both frameworks, within the context of what we are trying to achieve, will be highlighted.

Our Objective

Create, read and update entities in a database using the Entity Framework.

Tools

If you like a hands-on approach and will like to code along, you will need the following tools:

  1. Visual Studio 2012 (any edition)
  2. SQL Server Management Studio Express 2012 available at http://www.microsoft.com/en-us/download/details.aspx?id=29062
  3. Adventure Works database, available at http://msftdbprodsamples.codeplex.com/releases/view/55330
  4. Entity Framework 6, available via NuGet

Procedure

The overall procedure can be summarized as follows:

  1. Define entity classes that are representative of the database tables.
  2. Create an Entity Framework database context that derives from System.Data.Entity.DBContext, add a DbSet for every entity definition you want to query from the database.
  3. Creating mapping classes that correlate properties in your entity model to columns in your database tables.
    Create your Entity Framework configuration in app.config/web.config.
  4. Add required configuration to your application’s configuration file for the Entity Framework.

Please note that we will not be using any automatic code generation features of the Entity Framework. We choose to do everything manually, fully acknowledging that certain aspects of the workflow such as mapping each column type to a respective CLR type, are faster done automated. However, just like making the 24 hour drive versus the 5 hour flight between Ottawa, Ontario, Canada to a place lie Sandestin, Florida, the former option gives you an opportunity to experience America’s landscape in its wonder. Whereas, the latter does not, quickly taking you from one comfort station to another, with no thrills or nothing to write home about. Doing things manually also gives us an opportunity to gain a better understanding of the framework by solving each potential problem as they arise.

Entity Framework 6 Default Behavior

Before proceeding, it is worth noting certain default behaviors and assumptions exhibited by EF6, unless it is told otherwise:

  • Behavior: Pluralize a model to find corresponding table in database
  • Behavior: Map a property in the model against a table column using the property name
  • Behavior: Create a database to represent your Entity if one does not exist
  • Assumption: Each entity has a primary key called Id or name of Entity’s type concatenated with “Id” defined in the corresponding table

As far as I know, Hibernate does not make any such assumptions, forcing you to explicitly create the context it will use to connect to the database, retrieve and map these entities.

Step 1: The Employee Class

This class is represented in HumanResources.Employee table of AdventureWorks2012.

Look at the table, schema, I manually created the following class:

C#
public class Employee
    {
        public int Id { get; set; }
        public string NationalIDNumber { get; set; }
        public string LoginID { get; set; }
        public short OrganizationLevel { get; set; }
        public string JobTitle { get; set; }
        public DateTime BirthDate { get; set; }
        public char MaritalStatus { get; set; }
        public char Gender { get; set; }
        public DateTime HireDate { get; set; }
        public bool IsSalaried { get; set; }
        public short VacationHours { get; set; }
        public short SickLeaveHours { get; set; }
        public bool IsCurrent { get; set; }
        public DateTime ModifiedDate { get; set; }

        public override string ToString()
        {
            return string.Format("Employee Id {0} Login Id: {1}", Id, LoginID);
        }
    }

To keep things simple, columns OrganizationalMode and rowguid are not represented in the model as I could not readily figure out a corresponding CLR data type.

Step 2: Creating Mappings to Deal with Model-table Discrepancies

Since there is typically a discrepancy between our model and its associated table in the database, we need to create a type mapping.

For example, the three default behaviors exhibited by EF6 will fail with the Employee model defined above because:

  1. Our corresponding table is not called Employees. Instead, it is HumanResources.Employee.
  2. Properties IsSalaried and IsCurrent do not exist in the table. Corresponding table fields are CurrentFlag and SalariedFlag.
  3. The primary key in HumanResources.Employee is BusinessEntityID and neither Id not EmployeeId.

In Hibernate, you ALWAYs create some sort of mapping either through configuration or recently via the Fluent API. In EF6, however, we only create a mapping if the default assumptions are false. I personally found this to be another decision point that could lead to some frustration but is not well documented. For the Employee model, the default assumptions are false and therefore we will need to creating a mapping.

In EF6, we will create these mappings using a fluent API. This involves creating a class that derives from System.Data.Entity.ModelConfiguration.EntityTypeConfiguration
and defining mappings within the class constructor. For the Employee class, we need to specify the proper primary fields and create the correct mapping between fields IsCurrent and IsSalaried and their corresponding database columns CurrentFlag and SalariedFlag respectively. The resulting code C# class looks like this:

C#
public class EmployeeMapping : EntityTypeConfiguration<Employee>
 {
     public EmployeeMapping()
     {
         // resolve discrepancy between default assumption on existence of Employees table
         ToTable("HumanResources.Employee");

         // to resolve discrepancy between primary key BusinessEntityID defined in database
         HasKey(a => a.Id);

         // map Emplyee.Id to table column BusinessEntityID
         Property(e => e.Id).HasColumnName("BusinessEntityID");

         // map Employee.IsCurrent to table column CurrentFlag
         Property(a => a.IsCurrent).HasColumnName("CurrentFlag");

         // map Employee.IsSalaried to table column SalariedFlag
         Property(a => a.IsSalaried).HasColumnName("SalariedFlag");
     }
 }

Step 3: Registering Type Mappings with Framework

In other to use these mappings, they must be registered with the framework. In NHibernate, we register type mappings in hibernate.cfg.xml or embed them into the assembly and let the framework resolve them automatically during runtime.

In EF6, the only way, unless someone can prove otherwise, to register these type mappings is by manually instantiating these classes in the appropriate EF database context. An EF database context is what your application uses to interact with the database. It is an instance of this class that needs to be made aware of your type mappings and other configuration information that influences the framework’s behavior. In some ways, this class is similar in to the Hibernate Session object.

To connect to our AdventureWorks2012 database, we implement the following EF6 DbContext:

C#
public class AdventureWorksDbContext: DbContext
   {
       // register repository to Employees here
       public DbSet<Employee>  Employees { get; set; }

       protected override void OnModelCreating(DbModelBuilder modelBuilder)
       {
           base.OnModelCreating(modelBuilder);

           // register Employee type mapping here
           modelBuilder.Configurations.Add(new EmployeeMapping());
       }
   }

As commented in the above file, we have registered our type mapping and created a repository to query for Employee types. Either of those lines are required to get EF6 communicating to the database.

Step 4: The Configuration

Adding EF6 via NuGet adds an app.config file with the following:

C#
<configSections>    
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, 
     EntityFramework, Version=6.0.0.0, Culture=neutral, 
     PublicKeyToken=b77a5c561934e089" requirePermission="false" />
	
<entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, 
     EntityFramework" />
    <providers>
      <provider invariantName="System.Data.SqlClient" 
       type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer"/>
    </providers>
  </entityFramework>  

This is a standard snippet required to initialize EF6 in the your app domain. Nothing interesting here. However, we have one last EF6 default behavior to override. We want the framework to use our existing AdventureWorks2012 database instead of creating a new one. To do this, we need to add a connection string entry in our app.config, ensuring its name is exactly the same as the class name of our DbContext implementation as follows:

XML
<connectionStrings>
    <add name="AdventureWorksDbContext" providerName="System.Data.SqlClient" 
     connectionString="Data Source=localhost\SQLEXPRESS;Initial Catalog=AdventureWorks2012;
     Integrated Security=SSPI" />  
....
</connectionStrings>

This was another tripping point during my learning experience. And that is it folks. You have all the required bits to perform a simple CRUD operation to AdventureWorks2012 via the Entity Framework 6.

If you have followed so far, you should be able to write a simple console application to retrieve some employees:

C#
class Program
 {
     static void Main(string[] args)
     {
         IEnumerable<Employee> employees;
         using (var context = new AdventureWorksDbContext())
         {
             employees = context.Employees.Where(e => e.IsCurrent).ToArray();
         }

         foreach (var employee in employees)
         {
             Console.WriteLine(employee);
         }

         Console.ReadLine();
     }
 }

In a future blog post, I will look at relationships and stored procedures.

License

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