Entity Framework 4 "Code-First": Custom Database Schema Mapping
 
Published: 23 Jul 2010
Unedited - Community Contributed
Abstract
In this article, Scott examines the function of custom database schema mapping in Entity Framework 4 code first development. He discusses how to override the default persistence mapping rules and make use of the custom database schema with the help of various development scenarios.
by Scott Guthrie
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 30538/ 49

Introduction

Republished with Permission - Original Article

Last week I blogged about the new Entity Framework 4 “code first” development option.  The EF “code-first” option enables a pretty sweet code-centric development workflow for working with data.  It enables you to:

Develop without ever having to open a designer or define an XML mapping file

Define model objects by simply writing “plain old classes” with no base classes required

Use a “convention over configuration” approach that enables database persistence without explicitly configuring anything

In last week’s blog post I demonstrated how to use the default EF4 mapping conventions to enable database persistence.  These default conventions work very well for new applications, and enable you to avoid having to explicitly configure anything in order to map classes to/from a database. 

In today’s blog post I’m going to discuss how you can override the default persistence mapping rules, and use whatever custom database schema you want.  This is particularly useful for scenarios involving existing databases (whose schema is already defined and potentially can’t be changed) as well as for scenarios where you want your model shape to be different than how you want to persist it within a relational database.

Quick Recap of our NerdDinner Sample

In my blog post last week I walked through building a simple “NerdDinner” application from scratch, and demonstrated the productivity gains EF “code first” delivers when working with data. 

Below are the two model classes we created to represent data within the application.  They are “plain old CLR objects” (aka “POCO”) that only expose standard .NET data types:

image

We then created a “NerdDinners” class to help map these classes to/from a database.  “NerdDinners” derives from the DbContext class provided by the EF “code first” library and exposes two public properties:

image

We used the default EF4 “code first” conventions to enable database persistence.  This means that the “Dinners” and “RSVPs” properties on our “NerdDinners” class map to tables with the same names within our database.  Each property on our “Dinner” and “RSVP” model classes in turn map to columns within the “Dinners” and “RSVPs” tables.

Below is the database schema definition for the “Dinners” table within our database:

image

Below is the database schema definition for the “RSVPs” table within our database:

image

We did not have to configure anything in order to get this database persistence mapping with EF4 “code first” – this occurs by default simply by writing the above three classes.  No extra configuration is required.

Enabling Custom Database Persistence Mappings with EF4

EF4 “Code First” enables you to optionally override its default database persistence mapping rules, and configure alternative ways to map your classes to a database.

There are a few ways to enable this.  One of the easiest approaches is to override the “OnModelCreating” method defined on the DbContext base class:

The OnModelCreating method above will be called the first time our NerdDinners class is used within a running application, and it is passed a “ModelBuilder” object as an argument.  The ModelBuilder object can be used to customize the database persistence mapping rules of our model objects.  We’ll look at some examples of how to do this below.

EF only calls the “OnModelCreating” method once within a running application – and then automatically caches the ModelBuilder results.  This avoids the performance hit of model creation each time a NerdDinners class is instantiated, and means that you don’t have to write any custom caching logic to get great performance within your applications.

Scenario 1: Customize a Table Name

Let’s now look at a few ways we can use the OnModelCreating method to customize the database persistence of our models.  We will begin by looking at a pretty common scenario – where we want to map a model class to a database schema whose table names are different than the classes we want to map them to. 

For example, let’s assume our database uses a pattern where a “tbl” prefix is appended to the table names.  And so instead of a “Dinners” table we have a “tblDinners” table in the database: 

image

We want to still map our clean “Dinners” model class to this “tblDinners” table – and do so without having to decorate it with any data persistence attributes:

image

We can achieve this custom persistence mapping by overriding the “OnModelCreating” method within our NerdDinners context class, and specify a custom mapping rule within it like so:

image

The code within our OnModelCreating() method above uses a Fluent API design – which is a style of API design that employs method chaining to create more fluid and readable code.  We are using the ModelBuilder object to indicate that we want to map the “Dinner” class to the “tblDinners” table. 

And that is all the code we need to write.  Now our application will use the “tblDinners” table instead of the “Dinners” table anytime it queries or saves Dinner objects.  We did not have to update our Dinner or RSVP model classes at all to achieve this – they will continue to be pure POCO objects with no persistence knowledge.

Trying out the Above Change

If you downloaded the completed NerdDinner sample from my previous blog post, you can modify it to include the above custom OnModelCreating() method and then re-run it to see the custom database persistence in action.

We enabled the automatic database creation/recreation feature within EF “code-only” with the previous blog post.  This means that when you re-run the downloaded NerdDinner application immediately after making the above OnModelCreating() code change, you’ll notice that the SQL CE database is updated to have a “tblDinners” table instead of a “Dinners” table.  This is because EF detected that our model structure changed, and so re-created the database to match our model structure.  It honored our custom OnModelCreating() mapping rule when it updated it – which is why the table is now “tblDinners” instead of “Dinners”.

Several people asked me at the end of my first blog post whether there was a way to avoid having EF auto-create the database for you.  I apparently didn’t make it clear enough that the auto-database creation/recreation support is an option you must enable (and doesn’t always happen).  You can always explicitly create your database however you want (using code, .sql deployment script, a SQL admin tool, etc) and just point your connection string at it – in which case EF won’t ever modify or create database schema.

I showed the auto-database creation feature in the first blog post mostly because I find it a useful feature to take advantage of in the early stages of a new project.  It is definitely not required, and many people will choose to never use it.

Importantly we did not have to change any of the code within the Controllers or Views of our ASP.NET MVC application.  Because our “Dinner” class did not change they were completely unaffected by the database persistence change.

Scenario 2: Customize Column/Property Mappings

Let’s now look at another common scenario – one where we want to map a model class to a database schema whose table and column names are different than the classes and properties we want to map them to. 

For example, let’s assume our “tblDinners” database table contains columns that are prefixed with “col” – and whose names are also all different than our Dinner class:

image

We still want to map our clean “Dinners” model class to this “tblDinners” table – and do so without having to decorate it with any data persistence attributes:

image

We can achieve this custom persistence by updating our “OnModelCreating” method to have a slightly richer mapping rule like so:

image

The above code uses the same .MapSingleType() and .ToTable() fluent method calls that we used in the previous scenario.  The difference is that we are also now specifying some additional column mapping rules to the MapSingleType() method.  We are doing this by passing an anonymous object that associates our table column names with the properties on our Dinner class. 

The dinner parameter we are specifying with the lambda expression is strongly-typed – which means you get intellisense and compile-time checking for the “dinner.” properties within the VS code editor.  You also get refactoring support within Visual Studio – which means that anytime you rename one of the properties on the Dinner class - you can use Visual Studio’s refactoring support to automatically update your mapping rules within the above context menu (no manual code steps required). 

Scenario 3: Splitting a Table Across Multiple Types

Relational tables within a database are often structured differently than how you want to design your object-oriented model classes.  What might be persisted as one large table within a database is sometimes best expressed across multiple related classes from a pure object-oriented perspective – and often you want the ability to split or shred tables across multiple objects related to a single entity.

For example, instead of a single “colAddr” column for our address, let’s assume our “tblDinners” database table uses multiple columns to represent the “address” of our event:

image

Rather than surface these address columns as 4 separate properties on our “Dinner” model class, we might instead want to encapsulate them within an “Address” class and have our “Dinner” class exposes it as a property like so:

image

Notice above how we’ve simply defined an “Address” class that has 4 public properties, and the “Dinner” class references it simply by exposing a public “Address” property.  Our model classes are pure POCO with no persistence knowledge.

We can update our “OnModelCreating” method to support a mapping of this hierarchical class structure to a single table in the database using a rule like so:

image

Notice how we are using the same mapping approach we used in the previous example – where we map table column names to strongly-typed properties on our model object.  We are simply extending this approach to support complex sub-properties as well.  The only new concept above is that we are also calling modelBuilder.ComplexType<Address>() to register our Address as a type that we can use within mapping expressions.

And that is all we have to write to enable table shredding across multiple objects.

Download an Updated NerdDinner Sample with Custom Database Persistence Rules

You can download an updated version of the NerdDinner sample here.  It requires VS 2010 (or the free Visual Web Developer 2010 Express).

You must download and install SQL CE 4 on your machine for the above sample to work.  You can download the EF Code-First library here.  Neither of these downloads will impact your machine.

Summary

The CTP4 release of the "EF Code-First" functionality provides a pretty nice code-centric way to work with data.  It brings with it a lot of productivity, as well as a lot of power.  Hopefully these two blog posts provides a glimpse of some of the possibilities it provides. 

You can download the CTP4 release of EF Code-First here.  To learn even more about "EF Code-First" check out these blog posts by the ADO.NET team:

EF CTP4 Announcement Blog Post

EF CTP4 Productivity Enhancements Blog Post

EF CTP4 Code First Walkthrough Blog Post

DataAnnotations and Code First

Default conventions with Code First

Scott Hanselman’s Walkthrough Post about CTP4

Hope this helps,

Scott

Follow me at: twitter.com/scottgu

Resources



User Comments

No comments posted yet.

Product Spotlight
Product Spotlight 





Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-03-29 7:14:23 AM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search