Feature CTP Walkthrough: Code Only for the Entity Framework (Updated)

Published 22 June 09 02:54 PM | dpblogs 

This post covers Code Only improvements in the CTP2 release for VS 2010 Beta2. This walkthrough shows how you can change the default model like specifying property facets and navigation property inverses as well change the default mapping by changing the default inheritance strategy and table and column names. You can learn more about the CTP2 Code-Only improvements from our blog post.

Steps 1 to 9 cover getting started with the project. If you are familiar with the earlier CTP of Code Only and want to see some of the improvements in CTP2, please jump to Step 10.

1) Create a Console Application called "CodeOnlyWalkthru":

Untitled

2) Add a new Project to the "CodeOnlyWalkThru" solution:

Untitled

 

3) Choose 'Class Library' and call the library "Blogging":

Untitled

Our Blogging library will consist of the following classes

Untitled

 

4) Add the required classes in the Blogging project:

Right click on the project and add a class called “Blog” and then paste this code into the class

public class Blog
{
public Blog(){}

public int ID { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public User Owner { get; set;}
public ICollection<Post> Posts { get; set; }

}

In the Blog class right click on the reference to the non-existent Post and class choose “Generate > Class” from the context menu. Paste the code below into the generated class

public class Post
{
public Post() { }
public int ID { get; set; }
public string Title { get; set; }
public string Body { get; set; }
public string PermaUrl { get; set; }
public DateTime Created { get; set; }
public DateTime? Posted { get; set; }
public User Author { get; set; }
public User Poster { get; set; }
public Blog Blog { get; set; }
public ICollection<Comment> Comments { get; set; }
public int BlogID { get; set; }
}

 

In a similar fashion, add the Comment class and paste this code into the class

public class Comment
{
public Comment() {}
public int ID { get; set; }
public string Title { get; set; }
public string Body { get; set; }
public Person Author { get; set; }
public Post Post { get; set; }
public DateTime Created { get; set; }
public DateTime? Posted { get; set; }
}

Add the Person class and then paste this code into the class

public class Person
{
public int ID { get; set; }
public string Firstname { get; set; }
public string Surname { get; set; }
public string EmailAddress { get; set; }
public ICollection<Comment> Comments { get;set ;}
}

Right click on the project and add a class called “User” and then paste this code into the class

public class User : Person
{
public string Password { get; set; }
public ICollection<Blog> Blogs { get; set; }
public ICollection<Post> AuthoredPosts { get; set;}
public ICollection<Post> PostedPosts { get; set; }
}

Your project should now look like this:

Untitled

We are keeping these blogging classes in a separate project so they are compiled into an assembly that has no dependencies on the Entity Framework. The Blogging assembly is therefore persistence ignorant, which is very important for some developers. The persistence aware code lives in a separate assembly which references the persistence ignorant assembly.

6) In the "CodeOnlyWalkThru" Project add references to the Blogging Project, System.Data.Entity and Microsoft.Data.Entity.Ctp.

7) In the "CodeOnlyWalkThru" project add a new class called "BloggingModel" and paste this code in the class:

 

public class BloggingModel : ObjectContext
{
public BloggingModel(EntityConnection connection)
: base(connection)
{
DefaultContainerName = "BloggingModel";
}

public IObjectSet<Blog> Blogs
{
get { return base.CreateObjectSet<Blog>(); }
}
public IObjectSet<Person> People
{
get { return base.CreateObjectSet<Person>(); }
}
public IObjectSet<Comment> Comments
{
get { return base.CreateObjectSet<Comment>(); }
}
public IObjectSet<Post> Posts
{
get { return base.CreateObjectSet<Post>(); }
}
}
}

 

 

 

 

Since this class extends ObjectContext, it represents the shape of your model and acts as the gateway to your database. We added a constructor that takes an EntityConnection, which is a wrapper around the real database connection and the Entity Framework metadata, and pass it to this constructor when we ask it to create a new instance of our BloggingModel.

8) Now create BloggingDemo class and paste the code below:

public static void Run()
{

var builder = new ContextBuilder<BloggingModel>();

RegisterConfigurations(builder);


var connection = new SqlConnection(DB_CONN);

using (var ctx = builder.Create(connection))
{
if (ctx.DatabaseExists())
ctx.DeleteDatabase();
ctx.CreateDatabase();

var EfDesign = new Blog
{
Name = "EF Design",
Url = "http://blogs.msdn.com/efdesign/",
Owner = new User
{
ID = 1,
Firstname = "Johnny",
Surname = "Miller",
EmailAddress = "johnnyM@hotmail.com",
Password = "Viking"
}
};

ctx.Blogs.AddObject(EfDesign);

var post = new Post
{
Title = "Hello",
Blog = EfDesign,
PermaUrl = EfDesign.Url + "/2009/08/Hello",
Body = "....",
Author = EfDesign.Owner,
Poster = EfDesign.Owner,
Created = DateTime.Today,
Posted = DateTime.Today,
};

ctx.Posts.AddObject(post);

var comment = new Comment
{
Title = "RE:" + post.Title,
Body = "Welcome to the world of blogging Johnny...",
Created = DateTime.Now,
Posted = DateTime.Now,
Post = post,
Author = new Person
{
ID = 2,
Firstname = "Vincent",
Surname = "Chase",
EmailAddress = "vinny@hotmail.com",
}
};

ctx.Comments.AddObject(comment);
ctx.SaveChanges();

Blog blog = ctx.Blogs.Single();
foreach (var entry in blog.Posts)
{
Console.WriteLine(entry.Title);
Console.WriteLine(entry.Author.Firstname);
}
}


}

This code creates “Blogging” database and also creates a blog entry, a post and a comment. We are also creating a ContextBuilder which infers the Conceptual Model, Storage Model and Mapping. It uses that metadata plus the SqlConnection you passed in to create an EntityConnection, and finally it constructs the context(BloggingModel) by passing in the EntityConnection to the constructor.

The interesting method is RegisterConfigurations. This method uses the improvements in the CTP which allows us to customize the model as well as the mapping.

9) Paste the code for the RegisterConfigurations method:

static void RegisterConfigurations(ContextBuilder<BloggingModel> builder)
{
builder.Configurations.Add(new CommentConfiguration());
builder.Configurations.Add(new BlogConfiguration());
builder.Configurations.Add(new PostConfiguration());
builder.Configurations.Add(new PersonConfiguration());
builder.Configurations.Add(new UserConfiguration());

}

Note that we have created classes to encapsulate the configuration for each type. Each configuration class derives from EntityConfiguration<TEntity> and the constructor contains all the configuration logic. This is the preferred approach as it encapsulates and makes the code easier to read.

10) Add CommentConfiguration class to the project and paste the below code:

 

 

class CommentConfiguration : EntityConfiguration<Comment>
{
public CommentConfiguration()
{
Property(c => c.ID).IsIdentity();
Property(c => c.Title).HasMaxLength(103).IsRequired();
Property(c => c.Body).IsRequired();
// 1 to * relationships
Relationship(c => c.Author).IsRequired();
Relationship(c => c.Post).IsRequired();

//Register some inverses
Relationship(c => c.Post).FromProperty(p => p.Comments);
Relationship(c => c.Author).FromProperty(u => u.Comments);
}
}

We have defined this class to contain the entire configuration for the comment class. We want to ensure the Primary Key is store generated which we do using IsIdentity(). We also specify additional property facets like specifying the Maxlength on the title and Body, Author and Post are required.

We also register inverses here. We are indicating that Comment.Post is the other end of the Post.Comments relationship. Adding comment1 to the post1.Comments collection has the same effect as setting the comment1.Post to post1.

We specify a similar inverse relationship between Comment.Author and Author.comments.

11) Add BlogConfiguration class to the project and paste the code below:

public BlogConfiguration()
{
Property(b => b.ID).IsIdentity();
Property(b => b.Name).HasMaxLength(100).IsRequired();
Relationship(b => b.Owner).IsRequired();

//Register some inverses
Relationship(b => b.Owner).FromProperty(u => u.Blogs);
Relationship(b => b.Posts).FromProperty(p => p.Blog);
}
}

We are indicating that the ID is an identity column and other property facets.

We are also specifying inverse relationships between the Blog.Owner and Owner.Blogs and between Blog.Posts and Post.Blog.

12)Add PostConfiguration class and paste the code below:

public PostConfiguration()
{
// Make the PK store generated
Property(p => p.ID).IsIdentity();
// Convert some '0..1 to *' relationships into '1 to *'
Relationship(p => p.Author).IsRequired();
Relationship(p => p.Blog).IsRequired();
Relationship(p => p.Poster).IsRequired();
// Setup some facets
Property(p => p.Body).IsRequired();
Property(p => p.PermaUrl).HasMaxLength(200);
Property(p => p.Title).HasMaxLength(100);
// Register some Inverses
Relationship(p => p.Author).FromProperty(u => u.AuthoredPosts);
Relationship(p => p.Comments).FromProperty(c => c.Post);
Relationship(p => p.Poster).FromProperty(p => p.PostedPosts);

//BlogID is a FK property and Blog is a navigation property backed by this FK
Relationship(p => p.Blog).FromProperty(b => b.Posts).HasConstraint((p, b) => p.BlogID == b.ID);


}
}

 

Code-Only in CTP2 allows assigning Foreign Key property with a navigation property, which is what we are doing at the end of the constructor. This says Post.Blog and Blog.Posts are inverses and that Post.BlogID and Blog.ID must be the same , which implies Post.BlogID is a FK property.

13) Add the PersonConfiguration Class and paste the code below:

public PersonConfiguration()
{
Property(p => p.ID).IsIdentity();
Property(p => p.Firstname).HasMaxLength(100);
Property(p => p.Surname).HasMaxLength(100);
Property(p => p.EmailAddress).HasMaxLength(200);

MapHierarchy(
p => new
{
pid = p.ID,
email = p.EmailAddress,
fn = p.Firstname,
ln = p.Surname,
}
).ToTable("People");

}

}

Apart from specifying some property facets, we are also specifying the inheritance hierarchy. Code Only by convention follows TPH which generates a table per hierarchy. This would have resulted in both Person and User being stored in the same table.

If we want to instead have Person and Table stored in different tables, we can specify that using MapHierarchy. We are also changing the default column names to email, fn, ln instead of EmailAddress, FirstName and SurName. Finally we indicate that we want all of this to be stored in a “People” table.

14) Add the UserConfiguration class and paste the code below:

class UserConfiguration : EntityConfiguration<User>
{
public UserConfiguration()
{
Property(u => u.Password).HasMaxLength(15).IsRequired();

Relationship(u => u.AuthoredPosts).FromProperty(p => p.Author);
Relationship(u => u.PostedPosts).FromProperty(p => p.Poster);

MapHierarchy(
u => EntityMap.Row(
EntityMap.Column(u.ID, " u i d"),
EntityMap.Column(u.Password)
)
).ToTable("Users");
}
}

Here we want the specify a column name with spaces in it , so we are using the EntityMap to specify the “u i d” column name. EntityMap provides an alternate mapping syntax when we need to override CLR limitations.

We are also specifying some property facets for Password and some inverse relationships.

15) Call BloggingDemo.Run(). Paste the code into Program.cs

class Program
{
static void Main(string[] args)
{
BloggingDemo.Run();
}
}

Summary:

In this walkthrough we have covered some of the improvements in Code Only that allow us to make more fine grained control over our model and also customize our mappings. We looked at how to specify property facets and also relationship inverses and Foreign Keys.

We changed the default inheritance strategy of TPH to TPT also specified the Table and Column names instead of the default generated names.

Some of the features that are in the CTP2 but not covered by the WalkThrough include Complex Types, Join Table Mapping and Entity Splitting. Please refer to the blog post on how to use these features.

We are looking forward to hearing your feedback on new Code Only Improvements.

Filed under:
Attachment(s): CodeOnlyWalkThru.zip

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# Entity Framework CTP with Code Only &ndash; Now Available &laquo; Framework Madness! said on June 22, 2009 7:59 PM:

PingBack from http://leriksen71.wordpress.com/2009/06/22/entity-framework-ctp-with-code-only-now-available/

# Mikael Henriksson said on June 23, 2009 6:17 AM:

Finally! Huge step in the right direction. This is ultimately what I have wished for x-mas the last couple of years. I suppose I will miss the mappings, hierarchy and complex types but hey. You will be able to make that for the release version right?

# Ryan Riley said on June 24, 2009 10:19 AM:

I agree with Mikael; this is a very nice step forward.

# Mauricio Rodriguez said on June 25, 2009 12:46 AM:

How testable is the code only approch?????

# Jack said on June 26, 2009 2:41 AM:

Very great! It will surely save us lots of time!

# Marcelo said on June 28, 2009 3:19 AM:

Hi

Please, can add a complete sample using a generic repository and unit of work. The actual sample it is not generic.

A good sample help to learn!

# emanuele said on June 29, 2009 7:04 AM:

Very interesting.

When did you planned to release the beta version?

# Eric Hexter said on July 12, 2009 11:42 AM:

I am not sure I like having to specifiy the DefaultContainerName in the ObjectContext.  I walked through this sample with my own object model and it seemed that their is some some coupling to the name of my ObjectContext class. If so, why can't the framework infer the name and let me go on?  

I am excited to see more about this feature!

# cowgaR said on July 14, 2009 9:35 AM:

great work, reminds NHibernate with fluent mappings more and more...but couple of questions:

1. why is the "connection" passed in constructor in the Context, when all the other ORMs have it in some kind of Factory, e.g. ContextBuilder here (I'm entity framework newbie).

The factory then builds the "sessions"/"uow" e.g. Contexts in this case with this information, the other ORMs are trying to keep the session as lightweight as possible (as usually you will be creating Context per request in web apps).

2. in your Category class you should probably instantiate _products collection in a constructor,

it is much clearer solution (also found all around the web).

3. Where can I define identity strategy for the (primary) key? e.g. GUID COMB and so on?

4. Why do you need provider model? Haven't looked deeply on Entity Framework but it smells like the bad design for me. Shouldn't you reuse the providers already built in and push code-only-model (or transfer it) to an established designer model, which of course understands providers (so If I am using say sqlite one it works out of the box). Just I don't get it, when I can create table/update schema/change relationships using designer on say Oracle database, why can't I just do it with code-only, which should just be another layer.

5. I hope the listed limitation will be addressed, as they are really the barebone of what is needed, nice project for you to look at (which is much further in this) is

http://fluentnhibernate.org/

started by Mr. Gregory, and in many points it has even nicer design.

As I haven't been followint EF some questions may sound silly, but code-only approach surprised me so definitely going to play with it. Good luck, finaly the right way to do an ORM!

# Eric Hexter said on July 14, 2009 10:03 AM:

After going through the walkthrough my biggest complaint with the current bits is that the foreign key is only generated when I expose a List<foo> as a public virtual property. I would much rather create somthing like this and have EF access a private or protected member of my class:

   public class UserGroup

   {

       private List<Conference> _conference=new List<Conference>();

       public virtual  void AddConference(Conference conference)

       {

           _conference.Add(conference);

       }

       public virtual Conference[] GetAllConferences()

       {

           return _conference.ToArray();

       }

}

This way my domain object can apply rules around how conferences are removed from my UserGroup object.

This code is part of the CodeCampServer.org project that I am testing the CodeOnly features against.

# Damien Guard said on August 7, 2009 2:08 AM:

@cowgaR:

1. Not in scope for this project.

2. Personal style choice, not enforced.

3. Coming in next CTP.

4. Because the existing provider model does not support DDL for create/drop/exists database. If you don't need those then it could run with an existing provider.

5. We have more stuff coming.

[)amien

# progame said on August 24, 2009 6:40 AM:

hope to support full custom mapping for entityset info just like IQToolKit,

so i can support partial fields loading and different name convidence mapping.

# progame said on November 13, 2009 8:07 PM:

still can't include both property in entity:

public int Owner_ID { get; set; }

public User Owner { get; set; }

owner is for linq query, owner_id is for insert and data load for datatable.

class and object is not the whole world. we still need table and datable.

# xepaul said on November 14, 2009 4:01 PM:

Can you post a f# source code version please, whats the best way to deal with Expression<Func<T,TResult>> in F# ?

Thanks

# Roberto Hernandez said on November 15, 2009 5:18 PM:

How do you handle Lazy/Eager loading scenarios when using the "Code Only Model" ?

Roberto.-

# Ralf said on November 18, 2009 5:14 AM:

Would it be possible to map the Discrimator column

in TPH inheritance, i.e. make it visible in an entity

of the hierarchy, similar to foreign key mapping.

Leave a Comment

(required) 
(optional)
(required) 

  
Enter Code Here: Required

Search

This Blog

Syndication

Page view tracker