EF Code First Common Practices

Posted by ben 13. April 2011 08:56

This post covers some common practices for working with Entity Framework Code First (or EF 4.1 as was just released today).

Session Per Request

With Session-Per-Request our entity framework DbContext implementation is shared across a http request. This is a fairly standard approach when working with ORMs (we do something similar with NHibernate’s ISession).

The first thing we did was to make our DbContext a dependency within our repositories. We could then use our IoC container to inject the context and scope it accordingly (per http request). However, this gave us some unwanted coupling between our repository classes and Entity Framework so we decided to abstract DbContext into a simple context, as seen below:

    public interface IDbContext {
        IDbSet<TEntity> Set<TEntity>() where TEntity : class;
        int SaveChanges();
    }

Strictly speaking there is still some coupling since IDbSet<T> is part of System.Data.Entity but overall this interface makes things more testable.

Our application context, then implements this interface, in addition to inheriting from the Entity Framework DbContext:

    public class PortfolioContext : DbContext, IDbContext
    {
        public PortfolioContext(string connectionStringName) : base(connectionStringName) {}
        
        public IDbSet<Project> Projects { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder) {
            modelBuilder.Configurations.Add(new ProjectMap());
            base.OnModelCreating(modelBuilder);
        }

        public string CreateDatabaseScript() {
            return ((IObjectContextAdapter)this).ObjectContext.CreateDatabaseScript();
        }

        public new IDbSet<TEntity> Set<TEntity>() where TEntity : class {
            return base.Set<TEntity>();
        }
    }

I can then inject IDbContext into my repositories instead of my concrete DbContext. The code below shows the StructureMap configuration to handle session per request:

For<IDbContext>().HttpContextScoped()
	.Use(ctx => new PortfolioContext("Portfolio"));

Specifying the connection provider using your connection string

If you’ve installed the SQL Compact Edition Code First NuGet package it will add some pre app start code that sets the default connection factory to the SQL CE 4.0 provider.

In my experience this is not necessary and you can change the provider just by specifying it in your connection string; for example:

    <add name="Portfolio" connectionString="Data Source=|DataDirectory|Portfolio.sdf" providerName="System.Data.SqlServerCe.4.0" />
    <add name="Portfolio" connectionString="Data Source=(local)\sqlexpress;user id=sa;password=xxxxx;Database=portfolio" providerName="System.Data.SqlClient" />

Generic Repositories

People have mixed views about generic repositories. I find that they can save a lot of code duplication. They are only really of value due to the IQueryable collections (and delayed execution) that EF provides (just like we get with NH Linq).

We typically have a service layer that will consume the generic repositories and construct more specific queries e.g GetRecentPosts.

These services will have a dependency on IRepository<T> that is located in a common assembly (we actually use the same interface when working with NHibernate):

    public interface IRepository<T> where T : class
    {
        void DeleteOnSubmit(T entity);
        IQueryable<T> GetAll();
        T GetById(object id);
        void SaveOrUpdate(T entity);
    }

Our EF repository implementation can be seen below:

    public class EFRepository<T> : IRepository<T> where T : class, IEntity
    {
        protected readonly IDbContext context;
        protected readonly IDbSet<T> entities;

        public EFRepository(IDbContext context) {
            this.context = context;
            entities = context.Set<T>();
        }

        public virtual T GetById(object id) {
            return entities.Find(id);
        }

        public virtual IQueryable<T> GetAll() {
            return entities;
        }

        public virtual void SaveOrUpdate(T entity) {
            if (entities.Find(entity.Id) == null) {
                entities.Add(entity);
            }

            context.SaveChanges();
        }

        public virtual void DeleteOnSubmit(T entity) {
            entities.Remove(entity);

            context.SaveChanges();
        }
    }

Notice the generic constraint that our class must implement IEntity. This is just a simple interface that defines an Id property:

    public interface IEntity
    {
        Guid Id { get; set; }
    }

Since StructureMap supports generic types, we can now register our generic interface like so:

For(typeof(IRepository<>)).Use(typeof(EFRepository<>));

Occasionally the generic repository didn’t meet all our requirements. We had a number of cases where we needed to eagerly load associations. Fortunately, since the EFRepository methods are virtual we can just create a new implementation and override the necessary methods:

    public class ProjectRepository : EFRepository<Project> {

        public ProjectRepository(IDbContext context) 
            : base(context) { }
        
        public override Project GetById(object id) {
            var project = entities
                .Include(p => p.Template)
                .Include(p => p.Category)
                .SingleOrDefault(p => p.Id == (Guid)id);
            return project;
        }
    }

Now to use this implementation instead of the default, we just update our StructureMap configuration:

For<IRepository<Project>>().Use<ProjectRepository>();

Seeding the Database

Below is the database initializer that we use to create our database (if it doesn’t already exist).

The initializer accepts a boolean parameter that determines whether or not to create sample data (a setting from web.config):

    public class PortfolioDatabaseInitializer : IDatabaseInitializer<PortfolioContext>
    {
        private readonly bool createSampleData;
        
        public PortfolioDatabaseInitializer(bool createSampleData = false) {
            this.createSampleData = createSampleData;
        }
        
        public void InitializeDatabase(PortfolioContext context)
        {
            if (context.Database.Exists()) return;
            
            context.Database.Delete();
            context.Database.Create();

            // Add seed data
            Seed(context);
        }

        protected void Seed(PortfolioContext context)
        {
            context.ApplyTo(
                SetupData.InsertDefaultUser
            );

            if (createSampleData) {
                context.ApplyTo(
                    TestData.InsertTestImages,
                    TestData.InsertCategories,
                    TestData.InsertProjects,
                    TestData.InsertBlogPosts
                );
            }

            context.SaveChanges();
        }
    }

The context.ApplyTo method is a handy functional extension method, courtesy of Mike Hadlow:

public static void ApplyTo(this T arg, params Action[] actions) {
            Array.ForEach(actions, action => action(arg));
        }

This will apply the context to the specified methods, for example:

	public static void InsertCategories(IDbContext context)
	{
		category1 = new Category { Name = "Test Category 1", Slug = "test-category-1" };
		category2 = new Category { Name = "Test Category 2", Slug = "test-category-2" };

		context.AddRange(category1, category2);
	}

Testing entity persistence

Below is the base class we use for testing entity persistence. For this we use SQL Compact Edition as it works well in groups. Note that you don’t even need to set a connection string, it will just create the database in your test project output directory, in our case the database will be named “Portfolio.sdf”.

    [TestFixture]
    public abstract class PersistenceTest
    {
        protected IDbContext context;

        [SetUp]
        public void SetUp()
        {
            DbDatabase.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");
            SetupContext();
            context.Database.Delete();
            context.Database.Create();
        }
      
        /// <summary>
        /// Persistance test helper
        /// </summary>
        /// <typeparam name="T">Entity type</typeparam>
        /// <param name="entity">Entity</param>
        protected T SaveAndLoadEntity<T>(T entity) where T : BaseEntity
        {
            context.Set<T>().Add(entity);
            context.SaveChanges();

            object id = entity.Id;

            context.Dispose();
            SetupContext();

            var fromDb = context.Set<T>().Find(id);
            fromDb.ShouldNotBeNull();
            return fromDb;
        }
		
	protected void SetupContext() {
		context = new PortfolioContext("Portfolio");
	}
    }

Finally an example persistence test:

	[Test]
	public void Can_save_and_load_project()
	{
		var project = new Project
		{
			Title = "Some project",
			Slug = "some-project",
			ShortDescription = "A short description",
			Description = "<strong>Html for the win</strong>",
		};

		SaveAndLoadEntity(project);
	}

Overall we are finding that code first along with the above practices makes Entity Framework much more enjoyable to use, and due to it’s partial trust support and great integration with SQL CE, we are using it over NH on smaller projects.

Tags:
Categories Entity Framework | ASP.NET | C# | Development

blog comments powered by Disqus