Entity Framework POCO (EF4): A Simple Mapping

December 15th, 2009

The latest EF4 CTP released on November 12th includes updated support for POCO’s (Plain Ol’ CLR Objects). Although we could use POCO’s in the previous CTP, we still had to create an EDMX artifact to model our entities, thus leading to a bit of duplication.

In this post I want to take a look a simple mapping scenario, and how that looks using the new ‘Code Only’ API. By using the code only API I can have complete control over my entities, giving me the most flexibility in my mapping strategies, better testability and extensibility, and ultimately greater maintainability. No generated code. No XML files. Sweet ;)

Since I am a sports fanatic I am going to use one of my favorite models, the sports team. (Hey, I could be using Orders!)

The Database Table

I will start by defining a simple table with 2 columns, Name and ID:

CREATE TABLE [dbo].[Teams](
    [ID] [bigint] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](50) NULL,
 CONSTRAINT [PK_Team] PRIMARY KEY CLUSTERED ([Id] ASC)
) ON [PRIMARY]
 
GO

 

The Entity

When creating an Entity the default convention is that the Entity property names should match the database column names exactly. I will create a Team entity which does that like so:

public class Team
{
    public long Id { get; set; }
    public string Name { get; set; }
}

The Mapping

Mapping the Entity to the table is very easy. To do so, implement a Generic class EntityConfiguration<T> and setup the map in the ctor. For the Team entity, that will look like this:

public class TeamConfiguration : EntityConfiguration<Team>
{
    public TeamConfiguration()
    {
        Property(c => c.Id).IsIdentity();
        Property(c => c.Name).HasMaxLength(50).IsRequired();
    }
}

The EntityConfiguration methods contain many overloads which provide a variety of mapping options. For now, I will leave it simple.

 

The Infrastructure

Now that I have the entity and table mapped together, I need to provide the EF infrastructure with the configuration, and then I can begin working with the Entity(ies).

Connection

A simple connection string. I felt this was worth pointing out for those that have used EF3.5. Note that this is just a simple DB connection, and does not contain the metadata parts.

<configuration>
  <connectionStrings>
    <add name="Test" connectionString="Data Source=.;Initial Catalog=Test;
        Integrated Security=True;MultipleActiveResultSets=True"/>
  </connectionStrings>
</configuration>

 

The Object Context

If you look at the CTP walkthrough you will notice the sample includes a class called BloggingModel which derives from ObjectContext. BloggingModel has properties for each ObjectSet<T>. I believe this is done so that the ContextBuilder can infer the EntitySets and build the correct metadata. To me this is an unnecessary class that I would rather not maintain. Instead, before I create the context,  I will handle the metadata registration by calling “builder.RegisterSet<T>("SetName");” (For now… see future post).  If I did not do this, the context would use the default naming convention for our EntitySet, and would expect a table named “TeamSet”.

//create builder
var builder = new ContextBuilder<ObjectContext>();
 
//add our Team set configuration
builder.Configurations.Add(new TeamConfiguration());
//keep in mind this call is to avoid custom object context
builder.RegisterSet<Team>("Teams");
 
//setup connection
var cnxString = ConfigurationManager.ConnectionStrings["Test"].ConnectionString;
var connection = new SqlConnection(cnxString);
 
//create the context
var context = builder.Create(connection);
 
//now we have a good context, and can go to work
var teams = context.CreateObjectSet<Team>();
teams.AddObject(new Team { Name = "New Orleans Saints" });
context.SaveChanges();
context.Dispose();

Aside from some of the context setup code, it’s fairly straight forward. Simple data access with a POCO entity.  check.

 

Extending the Entity with a One-to-Many Relationship

Now that I have the basic mapping down, I want to go one step further and add a one-to-many relationship. What’s a team without players?

Player Table
CREATE TABLE [dbo].[Players](
    [ID] [bigint] IDENTITY(1,1) NOT NULL,
    [Team_ID] [bigint] NOT NULL,
    [Name] [nvarchar](50) NOT NULL,
    [Position] [nvarchar](50) NOT NULL,
 CONSTRAINT [PK_Players] PRIMARY KEY CLUSTERED ([ID] ASC)
) 
 
GO
 
ALTER TABLE [dbo].[Players]  WITH CHECK ADD  CONSTRAINT [FK_Players_Teams] FOREIGN KEY([Team_ID])
REFERENCES [dbo].[Teams] ([ID])
GO
 
ALTER TABLE [dbo].[Players] CHECK CONSTRAINT [FK_Players_Teams]
GO

Entities and Mappings

I have added an ICollection to Team for the Players, and each Player has a 1-1 Team property. Mapping the collection is straight forward by using the “Relationship()”  method which takes my expression for the properties I am mapping too.

public class Team
{
    public Team()
    {
        Players = new Collection<Player>();
    }
    public long ID { get; set; }
    public string Name { get; set; }
    public ICollection<Player> Players { get; set; }
}
 
public class Player
{
    public long ID { get; set; }
    public string Name { get; set; }
    public string Position { get; set; }
    public Team Team { get; set; }
}
 
public class TeamConfiguration : EntityConfiguration<Team>
{
    public TeamConfiguration()
    {
        Property(c => c.ID).IsIdentity();
        Property(c => c.Name).HasMaxLength(50).IsRequired();
        // 1 to * relationships     
        Relationship(c => c.Players).IsOptional();
        //set up inverse
        Relationship(c => c.Players).FromProperty(x => x.Team);
    }
}
 
public class PlayerConfiguration : EntityConfiguration<Player>
{
    public PlayerConfiguration()
    {
        Property(c => c.ID).IsIdentity();
        Property(c => c.Name).HasMaxLength(50).IsRequired();
        Property(c => c.Position).HasMaxLength(50).IsRequired();
               
        Relationship(c => c.Team).IsRequired();
        Relationship(c => c.Team).FromProperty(x => x.Players); 
    }
}

 
Now that the relationship is defined, I can work just as I would with a simple collection:
 
var teams = context.CreateObjectSet<Team>();
var team = new Team { Name = "Indianapolis Colts" };
var player1 = new Player { Name = "Peyton Manning", Position = "QB"};
var player2 = new Player { Name = "Reggie Wayne", Position = "WR" };
 
team.Players.Add(player1);
team.Players.Add(player2);
 
teams.AddObject(team);
context.SaveChanges();
 
//create new player set to show we are pulling back from db
var players = context.CreateObjectSet<Player>().Include("Team")
                    .Where(x => x.Team.Name == "Indianapolis Colts")
                    .ToList();
players.ForEach(x => Console.WriteLine(x .Name + " - " + x.Team.Name));
 
context.Dispose();

 

And that’s it.

While I was working on this sample I bumped into quite a few common scenarios which are not yet supported. Seeing how this is an early CTP, I suppose that is to be expected. That said, I am excited to see the direction it is taking and look forward to learning more as I have time to work with it.

For more information take a look at the related EF posts on the ADO.NET team blog.

  • McSean

    I knew that the new VS2010 and EF4 come with a template to generate POCO objects. so what is the advantage that code-only option can offer me?

  • http://guild3.com Jason Grundy

    Great intro.

    I agree with Tony that it looks very Fluent NHibernate-esque. Given my experience there I can’t help but wonder if there are naming conventions that can be defined at a global level that would avoid an override for every aggregate root.

  • t

    T

  • emanuele

    Very interesting post.
    I have a question:
    can I map an entity property using a different column names with EntityConfiguration?
    and if I have a complex type in the entity (like Team.Address wich has different properties), how can I map it?

  • http://elegantcode.com Jarod Ferguson

    @mcsean – it gives you total control over your entities: Persistence ingnorance, greater testability, finer control over mapping strategies, no code generation (so it works much better with source control), and no GUI desginer so it works better with large models (There is nothing like rebuilding a model with 50-100 entities because the designer failed).

    @jason not sure I compeletely follow? Do you mean a convention so it automatically maps the properties to the columns if they are the same name? I remember seeing something that did that.

    @emanuele Yes, it handles both of those scenarios. Check out this post here: http://blogs.msdn.com/efdesign/archive/2009/10/12/code-only-further-enhancements.aspx

  • mcsean

    thanks Jarod! Does the code-only work with WCF-RIA-Services? I once read a post from a forum and someone said it doesn’t and the work around is to use presentation model. Can you please clarify that?

  • http://elegantcode.com Jarod Ferguson

    @mcsean
    I have been meaning to give this a try and get back with you, but I have not been able to make the time yet.

    I think RIA services is supposed to support POCOs ala NHibernate, so I imagine if you followed that track instead of the straight EF approach it could work. (If it doesn’t already)

    There are supposedly new RIA services drops coming soon for the 2010RC, so look for improvements there.