There have been a lot of improvements to code first since CTP3 and in fact, the team has been experimenting with changes to the core Entity Framework APIs that not only support even simpler programming with code first but that we’ll benefit from in an upcoming release of .NET. (No, I don’t know if there will be a service pack and when…)
I have a code first sample in my book, Programming Entity Framework 2nd Edition, that is coming out in August and was lucky with the timing of the CTP4 release because I was able to sneak in an overhaul of that section before the book heads to the printer.
In revising my code sample for that section I was very happy with so many of the API & Code First improvements.
**But first a clarification is important. The version of Entity Framework that is in .NET 4/VS2010 is the current shipping, supported version that you can use in production. What’s in the CTP is simply an early look at what’s coming as it evolves giving us a chance to play with it. This is not something you can deploy and it is also going to evolve with future CTP versions.**
So with that in mind, I went to town on the CTP3 sample.
The first thing that I benefited from was the new stripped down easy access versions of the ObjectContext and ObjectSet.
The new classes, DbContext and DbSet have the same essential functions but don’t expose you to the entire feature set of their big brother & sister. Not everyone needs all of those features. The are not derived from ObjectContext and ObjectSet however, but provide easy access to the full featured versions through a property e.g., DbContext.ObjectContext and DbSet.ObjectSet.
Along with this simplification are simplified terms.
Where ObjectSet has AddObject
context.Customers.AddObject(myCust) – note that Customers in this case is the ObjectSet)
DbSet simply uses Add
context.Customers.Add(MyCust) –>now I’m using Customers as a DbSet).
Another nice trick is how DbSets are created.
ObjectSet has an internal constructor and does not have a parameterless constructor,so you need a backing variable
ObjectSet<Customer> Customers;
and then you need to execute the CreateObjectSet method to create the ObjectSet:
context.CreateObjectSet<Customer>(“Customers”)
DbSet has a constructor so you can use an auto-implemented property
public DbSet<Customer> Customers { get; set; }
Code First gets way easier
It’s true. I cut out gobs of code and moved code to where it belongs.
Code first uses convention over configuration. It looks at your classes and does its best job of inferring a model and a database schema from it (if you are starting from scratch). Your classes don’t always provide enough metadata so you can provide more info to code first through additional configurations. Initially those configurations were programmatic only but now you can also use attributes in your classes (“data annotations”).
Here are two examples.
In my model I have a class called ConferenceTrack. It has an identity property called TrackId. Code first convention looks for “Id” or class name + “Id” as an identity but TrackId doesn’t fit this pattern so I have to tell EF that this is my identity key.
I can do that using code first’s ModelBuilder (formerly called ContextBuilder):
modelBuilder.Entity<ConferenceTrack>().HasKey(ct => ct.TrackId);
In CTP3, I had to execute from the same code that instantiates the context. Bad bad. Now there is an OnModelCreating method that I can use and put that configuration inside that method. The method lives in the context class. I don’t have to call it. The context is smart enough to run it for me.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ConferenceTrack>().HasKey(ct => ct.TrackId);
}
Alternatively, I can configure this key right in my class as an attribute of the TrackId property.
[Key]
public int TrackId { get; set; }
Definitely simpler. I prefer to keep all of that EF related stuff in the context class so will most likely continue to use the fluent configuration rather than the data annotations.
Boy oh Boy did Relationships Get Easier
In my domain classes, I have a bunch of relationships defined through properties.
E.g.
ConferenceTrack has
public ICollection<Session> Sessions { get; set; } (a one-to-many relationship)
Session has
public ConferenceTrack ConferenceTrack { get; set; } (a many to one relationship)
public ICollection<Speaker> Speakers { get; set; } (a many to many relationship)
In CTP3 I had a whole bunch of configurations defined (that were hard to construct and hard to read) so that the model would understand my intent with these relationships. CTP4 is now smart enough to grok my intent based on the domain class properties. And if there’s something that I want that doesn’t follow the convention, then I can add a configuration.
So I removed *all* of the configuration code that described the relationships.
That made me happy.
And EF/Code First figured it all out.
Based on my classes and the single configuration to define the TrackId as the key for conferences, it created this database to persist my data into
It worked out all of the relationships. Notice the Sessions_Speakers that it created for the many to many relationship.
Also, I have a class in my domain that is called Workshop and inherits from Session. By default Code First assumes Table Per Hierarchy. It created a discriminator column in the Sessions table which I need to use another configuration for to change its name to IsWorkshop.
There’s more to love about this CTP. You can get coding details from theEntity Framework Design and EF team blog’s newest posts and download the CTP here.
As I’m learning more about domain driven development and as code first evolves, I’m getting more excited about this upcoming feature of Entity Framework
Sign up for my newsletter so you don't miss my conference & Pluralsight course announcements!
noticed you did a video on pluralsight on POCOs for EF a few days ago. Did this video demo the new features in CTP4?
no. I did that video last week. CTP4 just came out 2 days ago 🙂 But am thinking about it. Not sure if P/S does future tech vids tho.
What is the difference in ConfTrack relationship and many-many relationship. They look defined similarly – how does it work? I tried, it didn’t create the table for me!
I wonder, what did I miss!
Thanks in advance.
@Sharad
Session class has a property for conference track that is a single item.
ConfTrack has a property that is a collection of sessions. So the classes tehmselves define a one to many rel between conftrack and session.
Now to the *:*.
Session has a property that is a collection of speakers.
Speaker has a proeryt that is a collection of sessions
That defines a many to many and EF created a join table to represent that in the db.
hth
julie
Hey Julie,
Will it do many-to-many with payload?
Here’s what I need to do…
Imagine a table of Projects, a table of Sites, and a table of Widgets.
A Project has many Sites.
A Site has many Widgets.
However, since there are a huge number of widgets, when the user sets up a new Project, they pick a small subset of Widgets for use in each of the Sites in the Project.
Seems to me that one would need a join table(Projects_Widgets) that joins to yet another join table (Sites_Projects_Widgets).
I can figure out how to model this in Code-First so that I get the results I’m looking for. any ideas?
Hi Julie,
Thanks for the blog post! I’m doing something similar with a many-to-many relationship in my model, and I wanted to do something like:
"Select all sessions that belong to Speaker A". The underlying SQL is obvious, but I had a little trouble writing the Linq query, since the Sessions_Speakers table isn’t exposed through the DataContext.
I ended up doing this:
var speakerId = 5; // assuming this value came from some selection criterion
var sessions = Sessions.Where(s => s.Speakers.Select(y => y.SpeakerId).Contains(speakerId));
Does this seem okay to you? It feels a little indirect this way, and I’m not sure if the underlying SQL statement is optimal.
Thanks for your thoughts on this.
@Thom
You might want to look at using Any.
WIthout intellisense to aid me, it could look something like
sessions.Where(s=>s.Speakers.Where(sp=>sp.SpeakerId==speakerId))
not guaranteed, but something I wolde certainly try
Also take a look at the genrated tsql for both variations. If the end results (sql) are the same, then for writing the linq query, it then becomes a matter of personal style.
Hi Julie,
Good point, thanks for that. So, for anyone reading this later, I ended up using the equivalent of:
var sessions = dc.Sessions.Where(s => s.Speakers.Any(sp => sp.SpeakerId == speakerId));
where dc is my DataContext.