Practical .NET

What's New in Entity Framework 6 (Plus How To Upgrade!)

The latest version of the technology works fine with the Microsoft .NET Framework 4 and Visual Studio 2010. Here's some of what's new (along with how to move your applications to EF6).

Entity Framework 6 (EF6) introduces a whole bunch of changes, but the major one is the separation of EF from the Microsoft .NET Framework. EF is now a standalone package you add on a project-by-project basis through NuGet (see "Upgrading to Entity Framework 6"). This latest version of EF can be added to projects from the .NET Framework 4 (and later) and used in Visual Studio 2010 or later. That range probably includes you; if it doesn't, EF6 gives you a compelling reason to upgrade. In this column I'll look at some of the new features that work in the .NET Framework 4 (and skip what only works in the .NET Framework 4.5, saving that for later columns).

Stuff for Free
Among the reasons you should upgrade to EF6 are the features you get without any work on your part. For instance, with EF6 you should find that in applications with lots of tables and relationships defined, your context objects open faster. You should also be better insulated from dropped connections (at least, if the drop is transient and not permanent -- nothing is going to help there). EF6 also generates SQL faster from LINQ queries than before (though it's the same SQL that's being generated as in earlier versions of EF, so your actual data access won't be any faster or slower).

EF6 also eliminates an infrequent but annoying bug: Before EF6, if you had a class in your application that duplicated the name of one of your EF classes, EF would complain the entity name was ambiguous, even if the two classes were in different namespaces. EF6 now searches for its entity classes within their namespace.

Stored Procedures for CUD
EF 6 gives code-first developers something developers using the visual designer have always had: The ability to use stored procedures when you call SaveChanges to perform the updates to the tables represented by the entity classes. But don't get your hopes up: There's still nothing in code-first development like the function import the visual designer provides for using stored procedures (nor does EF Power Tools generate any support for stored procedures, at least as of Beta 4).

If your stored procedures have the right names and parameters, you can tell EF6 to use your stored procedures with just one line of code in the DbContext OnModelCreating method. The following example directs EF to use stored procedures for all updates made to a Product entity:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
  modelBuilder.Entity<Product>().MapToStoredProcedures();

This feature gives you an enormous amount of flexibility: you can do anything you want or need in the stored procedure, rather than just the plain-old updates EF provides.

However, you probably aren't going to be able to get away with that single line of code. In order for this to work, EF assumes a great deal about your stored procedures. To begin with, for my example, EF assumes I have three stored procedures called Product_Update, Product_Insert and Product_Delete. The problem is that if you're interested in this feature, you're already using stored procedures to manage your create/update/delete (CUD) activities, and these aren't the names of your stored procedures. Fortunately, you can specify the names of the stored procedures you are using with just a little more code. The following code sets the names of the CUD stored procedures to spAddProduct, spUpdateProduct and spDeleteProduct:

modelBuilder.Entity<Product>().MapToStoredProcedures(sp =>
{
  sp.Insert(proc => proc.HasName("spAddProduct"));
  sp.Update(proc => proc.HasName("spUpdateProduct"));
  sp.Delete(proc => proc.HasName("spDeleteProduct"));
});

You'll notice I configured all three CUD operations. If you use a stored procedure for any CUD operation, you must use stored procedures for all three CUD operations. This means that if you use MapToStoredProcedures and don't specify a stored procedure for the delete operation, EF6 will go looking for a stored procedure with the default name.

You can also customize the parameters passed to the stored procedures and the values returned from them. It's not unusual, for instance, for a stored procedure that inserts new rows to return the key generated by the database for the newly-added row. This is all the code needed to extend my definition to specify that my stored procedure returns a result called id that's mapped to the ProductId property on my Product object:

modelBuilder.Entity<Product>().MapToStoredProcedures(sp =>
{
  sp.Insert(proc => proc.HasName("spAddProduct")
    .Result(r => r.ProductID,"id"));

Similar code lets me customize the parameters the stored procedure is passed, accept the RowsAffected result and link to related tables. As you can imagine, this level of customization can result in a lot of code. Once you've written the code that defines a table's stored procedures, you'll want to put that code somewhere where you can integrate into any application.

Customizing Entity Framework
And, by the way, if you're not happy with the conventions and defaults EF uses, you can modify them. For example, if you know that in your database, all primary key fields are Guids with "PK" at the end of their names, you can add a new convention to EF in your OnModelCreating method with code like this:

modelBuilder.Properties<System.Guid>()
             .Where(p => p.Name.EndsWith("PK"))
             .Configure(p => p.IsKey());

This code first specifies that only Guid properties on entities are to be processed by this new convention. For those properties, the Where method filters the collection of properties to those with names ending with PK. Finally, the Configure method specifies these properties will be configured as the entity's primary key.

Intercepting Requests
In addition to specifying EF defaults and conventions, you can also massage the ADO.NET queries EF issues and the results those queries return (or cancel them or log them or …). EF6 not only lets you intercept EF-generated SQL, but also lets you modify the SQL and have EF use your modified version. You do this by adding an interception class to your context class. Code like this in the constructor for your DbContext class will do the trick:

DbInterception.Add(new MyInterceptor());

An interceptor class implements the IDbCommandInterceptor interface. Adding that interface to your class will add six methods called before and after the calls to the ADO.NET ExecuteNonQuery, ExecuteReader and ExecuteScalar methods EF makes. This example adds code to the method called before the ExecuteReader method. The code modifies the SQL statement used when retrieving rows from the Products table to ensure only items with units on hand are retrieved:

namespace NorthwindApp.Models
{
  public class MyInterceptor : IDbCommandInterceptor
  {
    public void ReaderExecuting(System.Data.Common.DbCommand command,
                                DbCommandInterceptionContext<System.Data.Common.DbDataReader> 
                                interceptionContext)
    {
      if (command.CommandText.Contains("FROM [dbo].[Products]"))
      {
        command.CommandText += " Where UnitsInstock > 0";
      }
    }

Configuring in Code
Modifying ADO.NET calls is just one of the many ways you can configure EF. Prior to EF6, most of those configuration options required you to fiddle with XML in your application's config files. EF6 now allows you to configure those options with code. All you need to do is add a class that inherits from DbConfiguration to the project holding your context class and put your configuration code in the class constructor (the DbConfiguration class provides multiple methods for configuring EF). A typical class looks like this:

public class ConfigEF : DbConfiguration
{
  public ConfigEF()
  {
    //...configuration code goes here...   
  }
}

The reason you put the configuration class in the project with your DbContext class is to allow EF6 to find the configuration class. The configuration changes you make, however, apply to all the context objects in the application, not just to the context class in the same project. You can put your configuration class in a different project from your context class provided you decorate your context class with a DbConfiguration attribute that points to your configuration class:

[DbConfigurationType(typeof(ConfigEF))]
public class NorthwindContext : DbContext
{

As an example, the following code adds my interceptor in a configuration class that could be in a different project from the context object:

public class ConfigEF : DbConfiguration
{
  public ConfigEF()
  {
    this.AddInterceptor(new MyInterceptor());
  }
}

Because the configuration class doesn't have to be in the same namespace as the context object, this strategy could make sense for applications that include multiple projects, each with a context class but where you want all the contexts configured the same way. Alternatively, you could use this strategy where you want to configure your context classes on a project-by-project basis with a different configuration class in each project.

And that's just the beginning. There's more in EF6 than I've mentioned here. For example, in the .NET Framework 4.5 you can take advantage of asynchronous processing to process queries on a background thread. I'm a big fan of test-driven development and EF6 provides new support for mocking EF calls. There are a lot of goodies in this version and I'll be looking at them in my next few columns.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

  • Compare New GitHub Copilot Free Plan for Visual Studio/VS Code to Paid Plans

    The free plan restricts the number of completions, chat requests and access to AI models, being suitable for occasional users and small projects.

  • Diving Deep into .NET MAUI

    Ever since someone figured out that fiddling bits results in source code, developers have sought one codebase for all types of apps on all platforms, with Microsoft's latest attempt to further that effort being .NET MAUI.

  • Copilot AI Boosts Abound in New VS Code v1.96

    Microsoft improved on its new "Copilot Edit" functionality in the latest release of Visual Studio Code, v1.96, its open-source based code editor that has become the most popular in the world according to many surveys.

  • AdaBoost Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the AdaBoost.R2 algorithm for regression problems (where the goal is to predict a single numeric value). The implementation follows the original source research paper closely, so you can use it as a guide for customization for specific scenarios.

  • Versioning and Documenting ASP.NET Core Services

    Building an API with ASP.NET Core is only half the job. If your API is going to live more than one release cycle, you're going to need to version it. If you have other people building clients for it, you're going to need to document it.

Subscribe on YouTube