1

I am attempting to configure a web service with Autofac so that I can map a different connection context for each controller:

// database connections
container.Register(c => new DocumentControllerActivator()).As<IHttpControllerActivator>().InstancePerApiControllerType(typeof(DocumentController));
container.Register(c => new WorkflowControllerActivator()).As<IHttpControllerActivator>().InstancePerApiControllerType(typeof(WorkflowController));

and:

public class WorkflowControllerActivator : IHttpControllerActivator
{
    // snip...
    var connectionString = "workflow connection string";

    var container = new ContainerBuilder();

    container.Register(c =>
    {
        var newConnectionContext = new SqlServerConnectionContext(connectionString) {ProductID = productId};
        newConnectionContext.Open();

        return newConnectionContext;
    }).As<ISqlServerConnectionContext>().As<IConnectionContext>().InstancePerApiRequest();

    var dr = (AutofacWebApiDependencyResolver)GlobalConfiguration.Configuration.DependencyResolver;
    container.Update(dr.Container.ComponentRegistry);

    return (IHttpController)request.GetDependencyScope().GetService(typeof(WorkflowController));
}

The DocumentControllerActivator differs only in the connection string the the return object type.

[AutofacControllerConfiguration]
public class WorkflowController : ApiController

When I attempt to access the service, the DocumentController throws an error saying that "Unable to cast object of type 'SearchService.Controllers.WorkflowController' to type 'SearchService.Controllers.DocumentController'." It's as if the second InstancePerApiControllerType registration is overwriting the first (i.e. it is doing it for all controllers).

Any suggestions where I've gone wrong? Or, an alternate solution? (Other than a service locator pattern in each controller.)

Colin Young
  • 3,018
  • 1
  • 22
  • 46

1 Answers1

0

Not sure why the ControllerActivator approach isn't working, but a simpler and less web api stack specific alternative could be:

public interface ISqlServerConnectionContextFactory
{
  ISqlServerConnectionContext Create();
}

// Register this with your ContainerBuilder
public class SqlServerConnectionContextFactory : ISqlServerConnectionContextFactory
{
  private string _connectionString;

  public SqlServerConnectionContextFactory(string connectionString)
  {
    _connectionString = connectionString;  
  }

  public ISqlServerConnectionContext Create()
  {
    var connectionContext = new SqlServerConnectionContext(_connectionString);
    connectionContext.Open();
    return connectionContext;
  }
}

public class MyController : ApiController
{
  private ISqlServerConnectionContext _sqlServerConnectionContext;

  public MyController(Func<string, ISqlServerConnectionContextFactory> connectionFactory)
  {
    _sqlServerConnectionContext = connectionFactory("MyConnectionString");
  }
}

See http://nblumhardt.com/2010/01/the-relationship-zoo/ for more information about AutoFac relationships and auto generated factories

In this case, when the AutoFac Container sees the Func parameter, it passes in a delegate that acts as a factory method that returns a ISqlServerConnectionContextFactory and passes the string through to its constructor. AutoFac really is rather clever!

Matt Caton
  • 3,473
  • 23
  • 25
  • That's perfect, and I'm hanging my head in shame for not having thought of it myself since we already had an ```ISqlServerConnectionContextFactory``` interface and implementation in the project, but it was "legacy" code from our pre-DI days. A bit of minor surgery and it solved the problem nicely (which was a bit more complex since our controllers have a whole tree of dependencies and the connection dependency was somewhere down in the weeds not on the controller itself). – Colin Young Jul 17 '14 at 17:58
  • It turns out that things are a little more complicated -- each controller uses a different connection, but the connection is also used by a generic objects farther down the dependency tree that are shared by both, and there I don't seem to be able to tell which connection string I need since they appear to be resolved much earlier than the controllers. – Colin Young Jul 17 '14 at 20:44
  • If I understand right this sounds like it might be worth putting this as a new question so as to not take this one too far off track? I'll keep an eye out for it if so. – Matt Caton Jul 18 '14 at 10:24