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>