Playing with Entity Framework Code Only
After making EF Prof work with EF Code Only, I decided that I might take a look at how Code Only actually work from the perspective of the application developer. I am working on my own solution based on the following posts:
But since I don’t like to just read things, and I hate walkthroughs, I decided to take that into a slightly different path. In order to do that, I decided to set myself the following goal:
- Create a ToDo application with the following entities:
- User
- Actions (inheritance)
- ToDo
- Reminder
- Query for:
- All actions for user
- All reminders for today across all users
That isn’t really a complex system, but my intention is to get to grips with how things work. And see how much friction I encounter along the way.
We start by referencing “Microsoft.Data.Entity.Ctp” & “System.Data.Entity”
There appears to be a wide range of options to define how entities should be mapped. This include building them using a fluent interface, creating map classes or auto mapping. All in all, the code shows a remarkable similarity to Fluent NHibernate, in spirit if not in actual API.
I don’t like some of the API:
- HasRequired and HasKey for example, seems to be awkwardly named to me, especially when they are used as part of a fluent sentence. I have long advocated avoiding the attempt to create real sentences in a fluent API (StructureMap was probably the worst in this regard). Dropping the Has prefix would be just as understandable, and look better, IMO.
- Why do we have both IsRequired and HasRequired? The previous comment apply, with the addition that having two similarly named methods that appears to be doing the same thing is probably not a good idea.
But aside from that, it appears very nice.
ObjectContext vs. DbContext
I am not sure why there are two of them, but I have a very big dislike of ObjectContext, the amount of code that you have to write to make it work is just ridiculous, when you compare that to the amount of code you have to write for DbContext.
I also strongly dislike the need to pass a DbConnection to the ObjectContext. The actual management of the connection is not within the scope of the application developer. That is within the scope of the infrastructure. Messing with DbConnection in application code should be left to very special circumstances and require swearing an oath of nonmaleficence. The DbContext doesn’t require that, so that is another thing that is in favor of it.
Using the DbContext is nice:
public class ToDoContext : DbContext { private static readonly DbModel model; static ToDoContext() { var modelBuilder = new ModelBuilder(); modelBuilder.DiscoverEntitiesFromContext(typeof(ToDoContext)); modelBuilder.Entity<User>().HasKey(x => x.Username); model = modelBuilder.CreateModel(); } public ToDoContext():base(model) { } public DbSet<Action> Actions { get; set; } public DbSet<User> Users { get; set; } }
Note that we can mix & match the configuration styles, some are auto mapped, some are explicitly stated. It appears that if you fully follow the builtin conventions, you don’t even need ModelBuilder, as that will be build for you automatically.
Let us try to run things:
using(var ctx = new ToDoContext()) { ctx.Users.ToList(); }
The connection string is specified in the app.config, by defining a connection string with the name of the context.
Then I just run it, without creating a database. I expected it to fail, but it didn’t. Instead, it created the following schema:
That is a problem, DDL should never run as an implicit step. I couldn’t figure out how to disable that, though (but I didn’t look too hard). To be fair, this looks like it will only run if the database doesn’t exists (not only if the tables aren’t there). But I would still make this an explicit step.
The result of running the code is:
Now the time came to try executing my queries:
var actionsForUser = ( from action in ctx.Actions where action.User.Username == "Ayende" select action ) .ToList(); var remindersForToday = ( from reminder in ctx.Actions.OfType<Reminder>() where reminder.Date == DateTime.Today select reminder ) .ToList();
Which resulted in:
That has been a pretty brief overview of Entity Framework Code Only, but I am impressed, the whole process has been remarkably friction free, and the time to go from nothing to a working model has been extremely short.
Comments
Is it possible not to map the Id?
I too was quite struck with the initial similarity to the Fluent NHibernate API, and I'm generally quite impressed with how the product works at the moment given that this is an early preview. That said, there are a few things that stand out as missing - e.g. I couldn't find a good way to map collections without having a generic ICollection exposed as a public property on the entity class (I only spent a few minutes looking though, I may have missed something). Presumably, more features will be added with future releases.
Generally, I'm sort of vaguely hopeful that this will become a solid approach to code first / code only database development in the near future - fingers crossed...
Ayende, you put selects together or EF send it as batch? This is strange since you call ToList()
Andrey,
I am probably not the best guy to ask
Felipe,
No, EF doesn't have something similar to NH Futures
This is nothing. Wait until Microsoft.Data reaches RTW - it will blow your head away.
Glad you are enjoying it so far.
IsRequired is only exposed on members you are mapping. HasRequired is used to declare relationships (the WithX is quite cool, especially when you start doing required to required and the With method names now slightly change so you can declare which is the principal end to allow cascade on delete etc to work).
While you could argue that having two 'Required' methods are confusing they actually operate in very similar ways. i.e. define whether the underlying column supports nullability.
The Has prefix could theoretically get dropped but some chains look ugly, e.g.
Entity <customer().many(x> x.Address).Required(a => a.Customer);
[)amien
Hey Ayende, I too have been playing around with the code first stuff and find it refreshing. And I think I can help you out with a couple of points...
To stop the database creation, I think you do this:
And to get rid of that metadata table (which is used internally to work out if the schema matches the model), you can do this:
<user ().HasKey(x => x.Username);
Charles
Not looked at EF for a while, but i wonder if it still has problems with generating horribly inefficient sql.
"That is a problem, DDL should never run as an implicit step."
Having it run automatically in development is quite handy as it keeps the DB up to date with my domain. They mention somewhere including data in a set up step as well. The worry is that it could trigger accidently in production which is scary. I think I'd prefer it off by default but easy to turn on.
What I would really like to see is a bigger story around database migrations along the lines of Rails etc. What are yout thoughts on that style of working?
I've only read those two (referenced) posts u mentioned at the start of the article, Oren .. and I thought that ObjectContext was getting deprecated by the DbContext ... ??
|)amien? is that true or have a totally misread it? Or is DbContext a new wrapper over ObjectContext?
(or i still have it completely wrong for the 10secs i read those articles).
-PK-
So we have:
NHibernate
FluentNHibernate
EntityFramework
FluentEntityFramework
@Pure Krome: I'm not sure what the long-term plan is there, I'm not on the EF team any more as I moved to Xbox.
@Nnc: It's not really FluentEF as Code First doesn't have to use the Fluent API. You can use Data Annotations.
[)amien
@Damien
It is like FluentNH+NH Mapping Annotations then. EF Code First is like a both of those in same assembly.
Thanks for the quick recap. I've been looking at EF recently as I think it's reaching a maturity state.
My biggest beef with the "code first" was partially my misunderstanding of how EF interpreted code first but it centers around automatically generating the schema. When I did a spike the first thing it did was create the db. This is not how I like to develop as I want to run my code/tests a few million times before I'm ready to embark on persistence. Like you, I want DDL to be implicit not some "magic happens here". Maybe the comment Charles left will omit this but I find this cryptic at best and I will forget it. I also want control over the DDL. Maybe the initial creation is spot-on but maybe not. At least with NH I can tweak it and supplement the creation. I'm not seeing that with EF so it seems that other than a database name, there's not a whole lot of options to optimize/tweak the schema creation (or completely bypass it?).
In any case, I'm still working in prod with NH and FluentNH. It's just proven itself time and time again and never got in my way. EF is in the lab and dev boxes as we try things out. Maybe someday...
Considering that you seem to have an appreciation for document databases, I am somewhat surprised that you are unhappy with the schema being generated for you automatically.
Daniel,
No schema is different than creating a schema on the fly.
More over, my concern is that it only work the first time, the next time, you are going to get an error.
@ |)amien - cheers! (and hmm... i always thought u were on the EF team after L2S .. doh. your move slipped under my radar. What you doing on the XBox team?)
The only thing I found that will stop the automatic DDL generation is to create your own do-nothing initializer like so:
public class DoNothingInitializer : IDatabaseInitializer <database
{
}
Where the <database generic param is your DbContext class.
You need to set this as the initializer before any code runs in your DB. I put it in my DbContext's static constructor:
static Database()
{
}
I agree that automatically creating the DDL is bad. What's worse is you have to do this or something similar to stop it, but other than that I'm liking where this is going.
Comment preview