The performance of the Object Relational Mappers



In the world of Object Relational Mappers, there is one topic that have always been on the table: Performance.

We have automatic configuration, generation and mapping. We have LINQ to Entities doing a great deal of the work for us, including generating the SQL and allowing us to use the built-in CLR functionality in order to produce working queries relatively easily.

The main problem, of course, is that all of this goodness comes with a price in terms of performance. All object relational mappers produce a well-known overhead and many companies, which are developing high-performance applications, ask themselves: “Is it worth it? Aren't they too slow or too heavy?”

The answer is, "Yes, they are". But that’s why we, the engineers, are here – to make it work faster and keep the benefits measured in production time. If you just install Entity Framework and start it, you’ll certainly need to make a nice refactor sooner or later.

Auto-compiled queries



Getting straight to the point, Entity Framework 5.0 uses the so-called “auto-compiled” queries feature which compiles (read “translates”) the Entity SQL or LINQ to Entities expression tree in a pure SQL (or T-SQL) code. The question is "Are these generated queries actually effective ?".

In order to do some accurate measurements, we need to get few things straight:

Don’t look at the very first query sent. During that query, the framework “warms up” and configures the views and all other components it needs.

Next, don’t look at the first invocation of a given query. During that time, EF caches parts of the query, so the subsequent calls can be faster. This doesn’t mean the query is compiled. It’s not…yet.

Take into consideration that .NET 4.5 is an “in-place” upgrade This means that once it is installed, you will not have any indications that the new version is actually installed (no GAC folder, no 4.5 assemblies, no registry entry). You also can’t go back to .NET 4.0 so even if you target 4.0 in the project properties, it will still use the 4.5 run-time.

Few performance tests



I've created a quite slow query (few joins, dozens of filters and ternary operators) and measured its performance using .NET 4.0 with Entity Framework 4.0 (on another machine, of course), and .NET 4.5 with Entity Framework 5.0.

The results were the following (tested with 4 core CPU and 8GB of RAM, Windows 7, and SQL Server 2008):

Without auto-compiled query – 40ms

With auto-complied query – 10ms

With manual use of the CompliedQuery class – 5ms

This means that you are indeed going to get some out-of-the-box performance benefits by simply installing EF5 and .NET 4.5. But if you want faster query generation, use the CompiledQuery class instead, but be prepared to change your current DAL architecture to support this.

Unfortunately, I no longer have the sources of the examples I've used.

Enum Support



There is support for enumerations in the EF 5.0 release. In order to activate it, create a scalar property of structure Int32 in the .edmx, right-click on it, and select “Convert to Enum”.

This will create an Enum which, upon DDL generation, is translated into a simple column of type Int. This feature provides additional abstraction and integrity constraint in the DAL layer, but from a database point of view, there is no way to find out what a given value in a table really means. You will need to check the project code for that, or simply remember it.

Spatial Data Types



Another interesting feature is the support fo spatial data types in SQL server. For those who are not aware of them, these types are geography and geometry related classes which allow us to work directly with such data inside the SQL Server (like geographic locations or geometrical representation). Below are the related available CLR types. The blue boxes in the following diagram are concrete classes.

There is also a built-in spatial result view in SQL Server, which looks like this:

It is actually quite impressive. For example, if you have a table with points (longitude and latitude) of office locations, for example, you can visualize them directly in SQL Server. You can also utilize a number of functions, such as calculating the distance between two points. With Entity Framework 5.0, we can do it in C# (or any CLS supported language, for that matter). Note that there are still too few resources on the internet related to this feature, so prepare to dig if you plan to use it.

Code First now works with LocalDB



The Entity Framework Code First approach now works with LocalDB. For those of you who haven’t heard about LocalDB, think of it as a hybrid between SQL Server Express and SQL Server Compact. It runs in a child-process, rather than in a full-blown Windows service. It’s the default Visual Studio 2012 development server, and it’s meant to be light and easy to configure.

DbContext is now the default generated context



DbContext is not something new. A wrapper around ObjectContext, generated with the help of T4 templates, utilizing the convention-over-configuration principle of the POCO classes. It generates something like this:

public partial class AdventureWorks2008Entities : DbContext { public AdventureWorks2008Entities() : base("name=AdventureWorks2008Entities") { } protected override void OnModelCreating(DbModelBuilder modelBuilder) { throw new UnintentionalCodeFirstException(); } public DbSet Products { get; set; } public DbSet ProductModels { get; set; } public DbSet ProductSubcategories { get; set; } public DbSet UnitMeasures { get; set; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public partial class AdventureWorks2008Entities : DbContext { public AdventureWorks2008Entities ( ) : base ( "name=AdventureWorks2008Entities" ) { } protected override void OnModelCreating ( DbModelBuilder modelBuilder ) { throw new UnintentionalCodeFirstException ( ) ; } public DbSet Products { get ; set ; } public DbSet ProductModels { get ; set ; } public DbSet ProductSubcategories { get ; set ; } public DbSet UnitMeasures { get ; set ; } }

And the entities look like this:

public partial class ProductSubcategory { public ProductSubcategory() { this.Products = new HashSet(); } public int ProductSubcategoryID { get; set; } public int ProductCategoryID { get; set; } public string Name { get; set; } public System.Guid rowguid { get; set; } public System.DateTime ModifiedDate { get; set; } public virtual ICollection Products { get; set; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public partial class ProductSubcategory { public ProductSubcategory ( ) { this . Products = new HashSet ( ) ; } public int ProductSubcategoryID { get ; set ; } public int ProductCategoryID { get ; set ; } public string Name { get ; set ; } public System . Guid rowguid { get ; set ; } public System . DateTime ModifiedDate { get ; set ; } public virtual ICollection Products { get ; set ; } }

As you can see, we are talking about pure POCO classes here, but that doesn’t mean that we are actually working with these simple classes at run-time. In fact, dynamic proxies are being generated every time you use the class, enabling lazy initialization and change tracking.

Multiple diagrams per model



The new Visual Studio 2012 designer supports multiple diagrams per model. We all know how big models are maintained. Huge databases can quickly become unmanageable without the use of the proper tools.

Here is a screenshot of the standard designer screen in Visual Studio 2012. You can see multiple diagrams on the upper right-hand corner. On the context menu you can select “Move to new Diagram” or “Include” related, which will include all entities with relations to the selected one. You can also change the color if you want.

Table-valued functions



Table-valued functions are also not new in SQL Server, but until now there was no built-in support in Entity Framework. For those of you who are not familiar with these type of functions, they are somewhat of a hybrid between a view and a stored procedure. They support procedural code inside parameters, but can be called from a SELECT statement. They return a single value, but this value is actually a table. This means that it can be chained in a LINQ-to-Entities query.

Here is a simple DDL script for a table-valued function in SQL:

CREATE FUNCTION [dbo].[GetCategories]() RETURNS TABLE RETURN SELECT [CategoryID], ,[CategoryName] ,[Description] ,[Picture] FROM [dbo].[Categories] 1 2 3 4 5 6 7 8 CREATE FUNCTION [ dbo ] . [ GetCategories ] ( ) RETURNS TABLE RETURN SELECT [ CategoryID ] , , [ CategoryName ] , [ Description ] , [ Picture ] FROM [ dbo ] . [ Categories ]

which can be used in LINQ-to-Entities like this:

var query = from c in context.GetCategoriesDirect() select c; 1 2 var query = from c in context . GetCategoriesDirect ( ) select c ;

Batch import of stored procedures



The last feature I want to mention is the ability to import a batch of stored procedures/functions into the .edmx model. Not much to write about here as it’s a matter of click-and-go.

What about Entity Framework 6.0?



Actually, the alpha version of Entity Framework 6.0 is now available in NuGet. You can download it from here if you like.

Here is a list of pre-announced features:

Task-based Async – Allowing EF to take advantage of .NET 4.5 async support with async queries, updates, etc.

Stored Procedures & Functions in Code First – Allow mapping to stored procs and database functions using the Code First APIs.

Custom Code First Conventions – Allowing custom conventions to be written and registered with Code First.

That is all from me. I hope you learned something new today. ;)

Kosta Hristov ( 34 Posts Hi there ! My name is Kosta Hristov and I currently live in London, England. I've been working as a software engineer for the past 6 years on different mobile, desktop and web IT projects. I started this blog almost one year ago with the idea of helping developers from all around the world in their day to day programming tasks, sharing knowledge on various topics. If you find my articles interesting and you want to know more about me, feel free to contact me via the social links below. ;)

Like the article ? Share it ! ;)