Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inject custom connection string into Entity Framework's DbContext

I want to inject a custom connection string into my EF context instead of using the connection string in my web.config. The idea is to move all database related logic off of my MVC project into a separate layer. I also want this layer to be responsible for the proper connection strings instead of my web applications.

The services currently using the context are calling the default constructor:

using (var context = new MyDbContext()) {
    //...
}

The default constructor is internally calling DbContext with the name of the connection string from the web.config:

public partial class MyDbContext : DbContext
{
    public MyDbContext()
        : base("name=MyDbContext")
    {
    }

    //...
}

In order to inject my custom connection string I would need an overloaded constructor which takes the connection string as an argument. Unfortunately there is not such a constructor provided.

It is obvious that adding the constructor overload manually right inside the MyDbContext class would be a very bad idea, since this class is auto-generated and will be overwritten any time soon. Let's not talk about this any more, it's forbidden. Period.

Since MyDbContext is a partial class, one could add the additional constructor in a separate class file partial class MyDbContext, but this seems smelly either. I don't know why, but my brain says bad idea.

After some investigation I found out, that one can tell EF to include this additional constructor by editing the T4 template Model.Context.tt which is a mix of C# and some template markup. Here is the original constructor:

public <#=Code.Escape(container)#>()
    : base("name=<#=container.Name#>")
{
<#
    WriteLazyLoadingEnabled(container);
#>
}

It is obviously easy to add similar logic to generate an overloaded constructor containing the connection string:

public <#=Code.Escape(container)#>(string nameOrConnectionString)
    : base(nameOrConnectionString)
{
<#
    WriteLazyLoadingEnabled(container);
#>
}

I tried this and noticed, that both re-generating the model classes and updating the model from DB will not affect the T4 template, thus the additional constructor will always be there. Good! At first glance this looks like a suitable solution, but...

And here is my question: Is that really a good solution?

Let's compare the three options again:

  1. Edit the auto-generated class (ok, we agreed to forget about this)
  2. Add the constructor in a partial class file
  3. Edit the T4 template to tell EF to generate the additional constructor

From these three options the third seems to me to be the most convenient and clean solution. What is your opinion? Has someone a good reason, why this would be a bad idea? Are there more options to inject connection strings, maybe using a custom connectionFactory? If so, how would I do that?

like image 836
Jan Avatar asked Apr 02 '13 14:04

Jan


1 Answers

Like @shaft proposed, I replaced my T4 template approach to using a partial class. As it turned out, this is indeed a lot simpler and intuitive, than any other solution I currently know of.

The auto-generated class looks like:

public partial class MyDbContext : DbContext
{
    public MyDbContext() : base("name=MyDbContext")
    {
    }

    //...
}

I just added another partial class of the same name. Issue solved.

public partial class MyDbContext 
{
    public MyDbContext(string nameOrConnectionString)
             : base(nameOrConnectionString) 
    {
    }
}
like image 155
Jan Avatar answered Oct 11 '22 17:10

Jan