I was excited to see StottGu’s blog regarding Code-First Development with Entity Framework 4. Personally, I don’t learn best by going through a step by step tutorial, however. It’s like learning a song in another language by rote memory. It may sound the same when I parrot it back, but don’t ask me to improvise. So I began to apply the principles in the article to the particular problem I was working on – and applying it in a TDD fashion, which is what this particular functionality was developed for.
The first thing I did was to create some classes that I would begin using – simple models. If simple model classes violate TDD, then just sue me. Hey, it was a code spike!
using System.Data.Entity;
using System.Collections.Generic;
namespace Microsoft.SnapId.DomainModel{ public class ManagementAgentModel { public int ManagementAgentModelID { get; set; } public string Name { get; set; } public virtual ServerConfigurationModel ServerConfigurationModel { get; set; } }
public class ServerConfigurationModel { public int ServerConfigurationModelID { get; set; } public string Title { get; set; } public virtual ICollection<ManagementAgentModel> ManagementAgentModels { get; set; } }
public class SnapIDs : DbContext { public DbSet<ServerConfigurationModel> ServerConfigurationModels { get; set; } public DbSet<ManagementAgentModel> ManagementAgentModels { get; set; } }
}
Then in a separate DAL layer I placed the only code that was even remotely identifiable as DB interfacing:
using System.Collections.Generic;using System.Linq;using Microsoft.SnapId.DomainModel; namespace Microsoft.SnapId.DataAccessLayer{ public class ServerConfigurationModelRepository { public ICollection<ServerConfigurationModel> GetAll() { var snap = new SnapIDs(); var serverConfigs = from s in snap.ServerConfigurationModels select s; return serverConfigs.ToList(); } public void Add(ServerConfigurationModel serverConfig) { var snap = new SnapIDs(); snap.ServerConfigurationModels.Add(serverConfig); snap.SaveChanges(); } }}
And finally, I created a simple spike to test out the functionality:
using System.Collections.Generic;using Microsoft.SnapId.DataAccessLayer;using Microsoft.SnapId.DomainModel;using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.SnapId.Tests.IntegrationTests.DataAccessLayer{ [TestClass] public class ServerConfigurationModelRepositoryTests { [TestMethod] [TestCategory("IntegrationTests")] public void Does_this_even_work() { var target = new ServerConfigurationModelRepository(); var serverConfig = new ServerConfigurationModel { Title = "Foo" }; target.Add(serverConfig); ICollection<ServerConfigurationModel> result = target.GetAll(); Assert.IsNotNull(result); } }}
Notice anything missing? There is no config file so far – no connection string. So I ran the test, completely expecting a failure. Lo and behold, however, the test passed. “OK,” I thought. “Maybe there’s some sort of internal model of the data that doesn’t complain even if there’s no database. Weird, but what more can you expect from a CTP?”
So I ran the test again, setting a breakpoint at the “GetAll()” method. I saw two results come back instead of one! Wow! Usually “surprise” is a bad thing in relation to technology (or so we’ve been taught), but this was definitely a good surprise. So I dug into the data that came back and found what was going on. Here’s an imaginary conversation that I was having with the Entity Framework 4:
Me: Ok, go run this code. I know it’s not going to work, but run it anyway.
EF4: Do you nave SqlExpress installed?
Me: Uh, yeah. I have VS 2010 installed, so that’s pretty much a given.
EF4: OK then, let me go ahead and decipher your object model and create you a new DB in your SqlExpress instance. Let’s see… I think I’ll call it the DB “Microsoft.SnapId.DomainModel.SnapIDs” after your object model.
Me: Whoa! That’s incredible! I’ll never have to deal directly with a database ever again!
EF4: Now you’re just dreaming.
Me: I can always dream.
Well, OK, it wasn’t quite that smooth. I did have an issue where I named my ID fields like “ManagementAgentModelId” instead of “ManagementAgentModelID” and EF4 wasn’t happy with that – said it couldn’t decipher a primary key for the object. Apparently it wasn’t case insensitive, which is a bummer, because you don’t spell it “IDentifier” – that’s like an interface for your teeth or something.
In the end, I am quite pleased with the code-first capability of EF4. I think I’ll call it “code-fist” from now on – sounds more dynamic.