13

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?

2 Answers 2

3

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) 
    {
    }
}
Sign up to request clarification or add additional context in comments.

Comments

2

From the 3 options you provide I'd rather choose option 2. Hasn't the partial keyword been introduced to solve the problem with autogenerated files? Using T4 templates brings additional complexity in my opinion, the maintaining developer has than to understand one more thing (Not everyone is familiar with T4), but extending a partial class is more standard c# development.

However I don't know the answer to 'is there a really good solution". Introducing a factory brings again new code to maintain, test and to understand, I am not sure this abstraction helps here because the factory itself would need to instantiate the dbcontext with the appropriate constructor (which you have to provide anyway).

Edit:

Just thought about it once more: the factory might be useful to resolve the connection string from another location (you said that you don't want the connectionstring in web.config), but that still doesn't solve the constructor problem

1 Comment

I am in fact using a connection string factory already, which successfully removed any data access related logic from my UI projects and moved it where it belongs namely the data access project. As you mentioned, it didn't solve the constructor issue.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.