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.

  • http://relentlessdevelopment.wordpress.com Grant C

    Looks promising. I’m liking the fluent configuration. It would be good to see model-first though – I understand this is possible with EF4, although unfortunately from the looks of things only via the designer.

  • http://elegantcode.com Tony Rasa

    If I wasn’t looking very closely, I could mistake this for Fluent NHibernate. Finally! Exciting stuff.

  • http://elegantcode.com Jarod Ferguson

    @Grant Though I have not tried to work with them other that running the walk-through, there are some extension methods which help facilitate object first development with code only.

    public static void CreateDatabase(this ObjectContext context);
    public static void DeleteDatabase(this ObjectContext context);
    public static bool DatabaseExists(this ObjectContext context);
    public static string CreateDatabaseScript(this ObjectContext context);

    Not quite nHibernate w/ Tarantino, but I think there is potential.

  • http://www.rivermoss.com Mike M

    Good Post. POCOS work well for updating and inserting it seems. For doing read only type of transactions for going to a view, I would go through a projection to a DTO(specific for the view) to improve performance. At all the companies that I have worked at with an EDM/ORM model (NHibernate/Entity Framework), there have been problems with performance by hydrating the objects, attaching to the context, and then translating them across multiple layers. There is no reason to bring back those entities and attach them to the context just to get a player name and the team name that he/she is on.

  • http://elegantcode.com Jarod Ferguson

    Agreed Tony, looks similar to fluent NH, which is great!

  • http://elegantcode.com Jarod Ferguson

    @Mike Projections are definitely outside the scope of this post, but I hear you. A projection into a ViewModel or DTO is good stuff, maybe like so:

    context.CreateObjectSet ()
    .Where(x => x.Team.Name == “Indianapolis Colts”)
    .Select(x => {new PlayerViewModel(x.Name, x.Team.Name);})

    Or using a mapper:
    context.CreateObjectSet ()
    .Where(x => x.Team.Name == “Indianapolis Colts”)
    .Select(Mapper.Map)

  • Pingback: Elegant Code » Entity Framework (EF4) Generic Repository and Unit of Work Prototype

  • isamux

    Nice introduction to the new POCO feature of EF4.
    Could you name some of the ‘quite a few common scenarios which are not yet supported’?

  • http://elegantcode.com Jarod Ferguson

    @isamux – A few issues I encountered:
    - I wanted to specify the name (or convention) of FK Id’s. , e.g TeamId. I had to change the name in the Database to “Team_ID”. In FNH you could just specify the Key name when mapping the relationship.

    - I wanted to have a private collection for the team Players, exposing only a getter for the collection, but I was only able to map using an AutoMatic property, therefore losing encapsulation on the collection.

    Perhaps these are supported, but I could figure it out.

  • Pingback: Elegant Code » Building a Unity Extension for Entity Framework POCO Configuration, Repository and Unit of Work