Adding support for a single connection string with multiple Entity Framework models using Dependency Injection

Recently I was working on a project that used more than one Entity Framework model to access different sections of the same database. The Entity Framework models were created using Model first to create edmx model files. The problem with this approach is that the connection string are unique to each model, as they contain model meta-data. This means that we have the same database connection string in several places.

Using our IoC container, we can create a dependency that will inject a single database connection string into each database context, which means that we can have a single database connection stored in our config file (web.config or app.config).

Creating a Entity Framework connection string

An Entity Framework connection string may be created in code using the EntityConnectionStringBuilder class (located in the System.Data.Entity.Core.EntityClient namespace). We will be using Castle Windsor to inject this connection string into our DbContext objects, so we should create an interface in a common project (i.e. a project that all the Entity Framework models will be able to access). We will call this interface IEntityFrameworkConnectionBuilder

    /// <summary>
    /// This interface allows the connection for an entity framework context to be determined from the model name. This will allow different EF models to use a single connection string
    /// </summary>
    public interface IEntityFrameworkConnectionBuilder
    {
        /// <summary>
        /// Create a EF connection string from the mode name
        /// </summary>
        /// <param name="modelName"></param>
        /// <returns></returns>
        string createEntityFrameworkConnection(string modelName);
    }

We can create the implementation of this interface in a project that has references to the EntityFramework libraries (installed by NuGet). The EntityFrameworkConnectionBuilder class is shown below:

using System;
using System.Collections.Generic;
using System.Linq;

using System.Data.Entity.Core.EntityClient;

namespace EF.Demo.Common
{
    /// <summary>
    /// This allows the connection for an entity framework context to be determined from the model name. This will allow different EF models to use a single connection string
    /// </summary>
    public class EntityFrameworkConnectionBuilder : IEntityFrameworkConnectionBuilder
    {

        public string _providerName;
        public string _dbConnectionstring;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="providerName"></param>
        /// <param name="dbConnectionstring"></param>
        public EntityFrameworkConnectionBuilder(string providerName, string dbConnectionstring)
        {
            _providerName = providerName;
            _dbConnectionstring = dbConnectionstring;
        }


        public string createEntityFrameworkConnection(string modelName)
        {
            var entityConnectionStringBuilder = new EntityConnectionStringBuilder();
            entityConnectionStringBuilder.Provider = _providerName;
            entityConnectionStringBuilder.ProviderConnectionString = _dbConnectionstring;
            entityConnectionStringBuilder.Metadata = string.Format(@"res://*/{0}.csdl|res://*/{0}.ssdl|res://*/{0}.msl", modelName);

            return entityConnectionStringBuilder.ToString();
        }
    }
}

As the provider and database connection string will be the same for all Entity Framework models, we can pass these into the EntityFrameworkConnectionBuilder as constructor parameters, which will be set by the IoC container.

The implementation of the createEntityFrameworkConnection method uses the EntityConnectionStringBuilder to build the correct entity framework connection string based on the supplied model name and the injected database provider and connection string.

Injecting the IEntityFrameworkConnectionBuilder into the Entity Framework context

We will use our IoC container to inject the IEntityFrameworkConnectionBuilder object into our Entity framework DB context classes. As the context classes are automatically generated by the Entity Framework model, we do not really want to manually alter these, as any changes would be lost if the model was regenerated. We can use partial classes to get around this problem.

When you created the model, you would have specified the EntityContainerName property. A class that inherits from DbContext will have been created in the model as the custom context for your model. In the same namespace, we can create a new class with this same name, and mark it as partial so that it will be merged with the generated class when compiled.

For example, if we created the Entity framework model with the EntityContainerName “MyModel”, we can create a MyModel partial class:

using EF.Demo.Common;

namespace EF.Demo.Common.Domain.Models
{
    /// <summary>
    /// A DB Context for the data model
    /// </summary>
    /// <remarks>
    /// This partial class allows a specific constructor to be specified for use with the IoC container
    /// As it is a partial file, the auto generated context may be re-generated without breaking this IoC specific bit
    /// </remarks>
    public partial class MyModel
    {
        /// <summary>
        /// Construct db context with the injected IEntityFrameworkConnectionBuilder and model name
        /// </summary>
        /// <param name="entityFrameworkConnectionBuilder"></param>
        /// <param name="modelName"></param>
        /// <remarks>
        /// This constructor is intended for use with an IoC container (e.g. Castle Windsor) to inject the properties
        /// and generate the correct connection string, using IEntityFrameworkConnectionBuilder
        /// </remarks>
        public MyModel(IEntityFrameworkConnectionBuilder entityFrameworkConnectionBuilder, string modelName)
            : base(entityFrameworkConnectionBuilder.createEntityFrameworkConnection(modelName))
        {
            
        }
    }
}

This class contains a constructor for the context that receives an instance of the IEntityFrameworkConnectionBuilder interface and the relevant model name. This constructor will be called by the IoC container, and the parameters will be resolved automatically (this will be described later when we configure the IoC container).

The connection string for the context is passed to the base DbContext by calling the base constructor and passing in the connection string generated from the IEntityFrameworkConnectionBuilder.createEntityFrameworkConnection(modelName) method.

Creating DbContext instances

Typically the Db Context objects are created with a using statement, to ensure that they are dispose of correctly when finished with.

using (var dbContext = new Models.OnlineFinances_Common())
{

}

However, we need a mechanism to create our context objects by resolving them through our IoC container so that the IEntityFrameworkConnectionBuilder object and model name may be passed to it, via the new constructor that we created. As we are going to use castle Windsor, we can make use of the Typed Factory Facility.

In order to create a typed factory, we need to create an interface with a Create<> and a Release<> method:

using System;
using System.Data.Entity;

namespace EF.Demo.Common
{
    /// <summary>
    /// A factory for creating and removing data base contexts
    /// </summary>
    /// <remarks>
    /// The purpose of this factory is to allow us to create dbContext instances 
    /// within code (probably in a using statement so it is disposed of correctly)
    /// but allow any dependencies in the context class to be resolved. The main dependency is
    /// the IEntityFrameworkConnectionBuilder, which will be added to a partial class for each specific
    /// DbContext based class. This interface will allow use to specify a single DB connection
    /// string for multiple Entity Framework models
    /// 
    /// This factory interface is not actually implemented anywhere. Instead
    /// we use Castle Windsor's Typed Factory Facility to auto create the abstract factory
    /// see http://docs.castleproject.org/Windsor.Typed-Factory-Facility.ashx   
    /// and http://docs.castleproject.org/Windsor.Typed-Factory-Facility-interface-based-factories.ashx
    /// </remarks>
    public interface IEntityFrameworkContextFactory
    {
        T Create<T>()
            where T : DbContext;

        void Release<T>(T context)
            where T : DbContext;

    }
}

Wherever we are making use of the DbContext (e.g. a repository, a unit of work, or some other pattern), we can inject the IEntityFrameworkContextFactory and then use this to create our DbContext objects.

    public class MyRepository : IMyRepository
    {
        #region IoC injected dependencies

        private IEntityFrameworkContextFactory _entityFrameworkContextFactory;

        public MyRepository(IEntityFrameworkContextFactory entityFrameworkContextFactory)
        {
            if (entityFrameworkContextFactory == null)
                throw new ArgumentNullException("entityFrameworkContextFactory");

            _entityFrameworkContextFactory = entityFrameworkContextFactory;
        }

        #endregion

        public List<Models.SomeModel> getAllSomeModels()
        {
            // Use the IEntityFrameworkContextFactory to create the context. This will allow the IoC container to inject dependencies, such as the single connection string
            using (var dbContext = _entityFrameworkContextFactory.Create<Models.MyModel>())
            {
                return dbContext.SomeModel.ToList();
            }
        }
    }
}

Note that we are not calling the Release method from the factory, as in this case the context objects will be created with a transient lifecycle, which I believe means that they will be released when the using statement completes.

Castle Windsor Configuration

The final step is to register our various interfaces and classes with Castle Windsor.

In our root project, we can create an installer. The mechanism to run this installer will depend on the type of project, but for example in an ASP.NET MVC project, it would be installed from the Application_Start event in Global.asax.

using Castle.Windsor;
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Facilities.TypedFactory;

using EF.Demo.Common;
using EF.Demo.Common.Domain;
using EF.Demo.Common.Domain.Models;


namespace EF.Demo.Web
{
    public class EntityFrameworkInstaller : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            // Register the IEntityFrameworkConnectionBuilder implementation
            // with the OnlineFinances db connection string
            container.Register(Component.For<IEntityFrameworkConnectionBuilder>()
                .ImplementedBy<EntityFrameworkConnectionBuilder>()
                .DependsOn(new
                    {
                        providerName = "System.Data.SqlClient",
                        dbConnectionString = System.Configuration.ConfigurationManager
                            .ConnectionStrings["MyConnectionString"]
                            .ConnectionString
                    })
                .LifestyleSingleton()
                );

            // Register the IEntityFrameworkContextFactory as a Typed Factory
            container.AddFacility<TypedFactoryFacility>()
                .Register(
                    Component.For<IEntityFrameworkContextFactory>()
                    .AsFactory());

            // Register the MyModel Entity framework context, with the correct model name
            container.Register(Component.For<MyModel>()
                .DependsOn(new
                    {
                        modelName = "Models.MyModel"
                    })
                .LifestyleTransient()
                );

        }
    }
}

In this installer, we are first registering the IEntityFrameworkConnectionBuilder interface with its implementation, and retrieving the database connection string from the configuration with the name MyConnectionString. We are also passing the provider type, which in this case is System.Data.SqlClient.

Next we register the IEntityFrameworkContextFactory as a Typed Factory.

Finally, we register all of the Entity Framework context objects. It is important that the correct model name is configured here, as this will be passed to the database context. To check what the model name should be, when you created the Entity Framework model, a connection string will have been created in the configuration file, this will have the model name embedded in it.

  <connectionStrings>
	  <add name="MyModel" connectionString="metadata=res://*/Models.MyModel.csdl|res://*/Models.MyModel.ssdl|res://*/Models.MyModel.msl;provider= ..
          />
  </connectionStrings>

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s