2

I have an application that use multiple Database.

i found out i can change that by using the connection builder. like so :

var configNameEf = "ProjectConnection";
var cs = System.Configuration.ConfigurationManager.ConnectionStrings[configNameEf].ConnectionString;
var sqlcnxstringbuilder = new SqlConnectionStringBuilder(cs);
sqlcnxstringbuilder.InitialCatalog = _Database;

but then i need to change the autofac Lifescope of UnitOfWork so that it will now redirect the request to the good Database instance.

what i found out after quite a while is that i can do it like this from a DelegatedHandler :

HttpConfiguration config = GlobalConfiguration.Configuration;
DependencyConfig.Register(config, sqlcnxstringbuilder.ToString());
request.Properties["MS_DependencyScope"] = config.DependencyResolver.GetRequestLifetimeScope();

The question is, is there any other way to do that, that change the MS_DependencyScope parametter of the request. This solution work but i think it is kind of shady.

here is the registry in DependencyConfig:

public static void Register(HttpConfiguration config, String bdContext = null)
        {
        var builder = new ContainerBuilder();
        builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
        builder.Register(_ => new ProjectContext(bdContext)).As<ProjectContext>().InstancePerApiRequest();

        builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerApiRequest();

        // Register IMappingEngine
        builder.Register(_ => Mapper.Engine).As<IMappingEngine>().SingleInstance();
        config.DependencyResolver = new AutofacWebApiDependencyResolver(builder.Build());
        config.DependencyResolver.BeginScope();

    }
Rugdr
  • 199
  • 2
  • 15
  • The question isn't entirely clear - are you trying to change the database on a per-request basis? Or are you changing it application-wide based on some sort of deployment configuration? – Travis Illig Jun 13 '14 at 14:10
  • it change on a per request basis if it was different from the last call. – Rugdr Jun 13 '14 at 15:09

1 Answers1

2

The way the question is described and the way the answer to my comment sounds, you have the following situation:

  • The application uses per-request lifetime units of work. I see this from your registrations.
  • Only one database is used in the application at a given point in time. That is, each request doesn't have to determine a different database; they all use the same one until the connection string changes. This is seen in the way the database is retrieved from using a fixed application setting.
  • The connection string in configuration may change, at which point the database used needs to change.

Assuming I have understood the question correctly...

If the app setting is in web.config (as it appears), then changing the string in web.config will actually restart the application. This question talks about that in more detail: How to prevent an ASP.NET application restarting when the web.config is modified?

If that's the case, you don't have any work to do - just register the database as a singleton and when the web.config changes, the app restarts, re-runs the app startup logic, gets the new database, and magic happens.

If the app setting is not in web.config then you should probably create a project context factory class.

The factory would serve as the encapsulation for the logic of reading configuration and building the connection to the database. It'll also serve as the place to cache the connection for the times when the setting hasn't changed.

The interface would look something like this:

public interface IProjectContextFactory
{
  ProjectContext GetContext();
}

A simple implementation (without locking, error handling, logging, and all the good stuff you should put in) might be:

public class ProjectContextFactory : IProjectContextFactory
{
  private ProjectContext _currentContext = null;
  private string _currentConnectionString = null;
  private const string ConnectionKey = "ProjectConnection";

  public ProjectContext GetContext()
  {
    // Seriously, don't forget the locking, etc. in here
    // to make this thread-safe! I'm omitting it for simplicity.
    var cs = ConfigurationManager.ConnectionStrings[ConnectionKey].ConnectionString;
    if(this._currentConnectionString != cs)
    {
      this._currentConnectionString = cs;
      var builder = new SqlConnectionStringBuilder(cs);
      builder.InitialCatalog = _Database;
      this._currentContext = new ProjectContext(builder.ToString());
    }
    return this._currentContext;
  }
}

OK, now you have a factory that caches the built project context and only changes it if the configuration changes. (If you're not caching the ProjectContext and are, instead, caching the database connection string or something else, the principle still holds - you need a class that manages the caching and checking of the configuration so the change can happen as needed.)

Now that you have a cache/factory, you can use that in your Autofac registrations rather than a raw connection string.

builder.RegisterType<ProjectContextFactory>()
       .As<IProjectContextFactory>()
       .SingleInstance();
builder.Register(c => c.Resolve<IProjectContextFactory>().GetContext())
       .As<ProjectContext>()
       .InstancePerRequest();

The ProjectContext will now change on a per request basis when the configured connection string changes.

Aside: I see odd stuff going on with the request lifetime scope. I see in your registration that you're creating your own request lifetime scope. With this method you shouldn't have to do that. If, however, you find that you still need to (or want to), you need to make sure both the originally-created lifetime scope and the one you created are disposed. Lifetime scopes do not get automatically disposed and do hang onto object references so they can handle disposal. There is a high probability that if you're not handling this properly then you have a subtle memory leak. The Autofac Web API integration will take care of creation and disposal of the request lifetime for you, but if you change out the request lifetime, odd things are going to happen.

Community
  • 1
  • 1
Travis Illig
  • 23,195
  • 2
  • 62
  • 85
  • thank's for the answer, it is exactly what i was looking for, the factory approach seem's to fit more the bill of the application. – Rugdr Jun 18 '14 at 13:24