15 Dec
2009

Entity Framework POCO (EF4): A Simple Mapping

Category:UncategorizedTag: :

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.

Find me

RSS
Facebook
Twitter
LinkedIn
SOCIALICON
SOCIALICON

Disclaimer

The opinions and content expressed here are my own and not those of my employer.