2

can anyone tell me what I am doing wrong? I am wanting to use Mehdime.Entity from https://www.nuget.org/packages/Mehdime.Entity in order to manage my DBContext-derived classes in a Console Application. I am also using NInject.

The connection strings for my DBContext-derived classes are partially generated from standard app.config ConnectionStrings and also by an AppDomain value that (in my Console App case) comes in via a command line argument.

My DBContext-derived classes have their connection strings prepared using a program-implemented class which takes into account of the command line argument as follows:

public class TaskManagementDbContext : DbContext
{
    public TaskManagementDbContext(IConnectionStringResolver csr) :
        base(csr.GetConnectionString("Default"))
    {
    }
}

(IConnectionStringResolver basically implements GetConnectionString() which returns the connection string by using given named standard app.config ConnectionString and the command line argument.

This is fine when I use NInject to instantiate the DbContext directly but when trying to use with Mehdime.Entity, it is AmbientDbContextLocator that is doing the instantiation and it throws a MissingMethodException because it requires my DBContext-derived class to have a parameterless constructor:

public class TaskRepository : ITaskRepository
{
    private readonly IAmbientDbContextLocator _ambientDbContextLocator;

    private TaskManagementDbContext DbContext
    {
        get
        {
            // MissingMethodException thrown "No parameterless constructor defined for this object"
            var dbContext = _ambientDbContextLocator.Get<TaskManagementDbContext>();
            ...
        }
    }

How should I provide a connection string to my DBContext-derived classes at run-time in this situation? I suspect I am going about this the wrong way. Thanks.

Chris Walsh
  • 3,423
  • 2
  • 42
  • 62
  • Further searching has uncovered the following which will probably lead to an answer for me: (1) [How to implement IDbContextFactory for use with Entity Framework data migrations](http://www.codeproject.com/Tips/540033/IDbContextFactory-which-gets-Connection-String-at), (2) [github DbContextScope Issue #8](https://github.com/mehdime/DbContextScope/issues/8), (3) [How to implement IDbContextFactory for use with Entity Framework data migrations](http://stackoverflow.com/questions/11395283/how-to-implement-idbcontextfactory-for-use-with-entity-framework-data-migrations) – Chris Walsh Nov 10 '15 at 04:17

1 Answers1

2

OK. I've worked out the solution and I'm putting it here for anyone else with this issue:

  1. Create your own implementation of IDbContextFactory (see below). I put this in the same class library as my Data Access Layer (i.e. my DbContexts). You will see in my example how I "look for" a specific constructor prototype (in my case, 1 parameter of type IDbContextFactory - your's will no doubt be different). If found, get the actual parameters and invoke a new instance of your DBContext-derived class. If not found, you can throw an exception or in my case, try to call the default constructor (if exists).

Code:

using System;
using System.Data.Entity;
using Mehdime.Entity;
using Ninject;
using TaskProcessor.Common;

namespace TaskProcessor.Data.Connection
{
    public class DbContextWithCSRFactory : IDbContextFactory
    {
        public TDbContext CreateDbContext<TDbContext>() where TDbContext : DbContext
        {
            // Try to locate a constuctor with a single IConnectionStringResolver parameter...
            var ci = typeof(TDbContext).GetConstructor(new[] { typeof(IConnectionStringResolver) });
            if(ci != null)
            {
                // Call it with the actual parameter
                var param1 = GlobalKernel.Instance.Get<IConnectionStringResolver>();
                return (TDbContext)ci.Invoke(new object[] { param1 });
            }

            // Call parameterless constuctor instead (this is the default of what DbContextScope does)
            return (TDbContext)Activator.CreateInstance<TDbContext>();
        }
    }
}
  1. Create a binding in NInject so that your IDbContextFactory implementation is called:

Code:

private void AddBindings(IKernel kernel)
{   ...
    kernel.Bind<IDbContextFactory>().To<Data.Connection.DbContextWithCSRFactory>().InSingletonScope();
}

Everything now falls into place.

Chris Walsh
  • 3,423
  • 2
  • 42
  • 62