0

We have a multi-database solution and are passing the connection string to a factory function like so:

container.Register<IDbContextFactory>(
    f => new DynamicDbContextFactory(ClientConfig.GetConnectionString()),
    new PerScopeLifetime());

ClientConfig contains a static dictionary that gets populated on app start that maps a sub domain to a connection string. It seems that this approach is causing a memory leak (not 100% sure about this causing the leak but there is a leak).

public class ClientConfig
{
    private static ConcurrentDictionary<string, string> ConnectionStringManager 
    { 
        get;
        set; 
    }

    // etc.
}

My question is in MVC what is the best way to hold a list of connection strings that can be easily looked up on each request in order to pass that down the chain.

lucuma
  • 18,247
  • 4
  • 66
  • 91
  • I don't see any Autofac usage in here and the first example with the `DynamicDbContextFactory` is not Autofac syntax. Can you post some additional information about where Autofac is used (or remove the Autofac tag if it's not being used)? – Travis Illig Apr 01 '15 at 03:22
  • I forgot to remove the autofac tag.. It has been removed. – lucuma Apr 01 '15 at 13:54

1 Answers1

0

Edit : The question was initially tagged with Autofac


With Autofac you don't have to use a dictionary and something like that to do what you want. You can use a custom parameter :

public class ConnectionStringParameter : Parameter
{
    public override Boolean CanSupplyValue(ParameterInfo pi, 
                                           IComponentContext context, 
                                           out Func<Object> valueProvider)
    {
        valueProvider = null;

        if (pi.ParameterType == typeof(String)
            && String.Equals(pi.Name, "connectionString", 
                             StringComparison.OrdinalIgnoreCase))
        {
            valueProvider = () =>
            {
                // get connectionstring based on HttpContext.Current.Request.Url.Host 
                return String.Empty;
            };
        }

        return valueProvider != null;
    }
}

Then register your Parameter using a Module

public class ConnectionStringModule : Autofac.Module
{
    protected override void AttachToComponentRegistration(
        IComponentRegistry componentRegistry, IComponentRegistration registration)
    {
        registration.Preparing += registration_Preparing;
    }

    private void registration_Preparing(Object sender, PreparingEventArgs e)
    {
        Parameter[] parameters = new Parameter[] { new ConnectionStringParameter() }; 
        e.Parameters = e.Parameters.Concat(parameters);
    }
}

Module you have to register inside your container using

ContainerBuilder builder = new ContainerBuilder();
builder.RegisterModule(new ConnectionStringModule());

Each time Autofac have to resolve a parameter of type String named connectionString it will used the custom parameter and get your connectionstring based on what you want.


By the way this code sample use HttpContext.Current. In case of a multithreaded process it may return null. I don't recommend using HttpContext.Current for such things. You can use an intermediate class instead of accessing it, for example a IConnectionstringProvider interface.

public interface IConnectionstringProvider
{
    String ConnectionString { get; }
}
public class ConnectionStringProvider : IConnectionstringProvider
{
    public ConnectionStringProvider(Strong host)
    {
        // get connectionstring based on host
        this._connectionString = String.Empty;
    }

    private readonly String _connectionString;

    public String ConnectionString
    {
        get { return this._connectionString; }
    }
}

Inside your Parameter you will have to change the valueProvider by

valueProvider = () =>
{
    return context.Resolve<IConnectionstringProvider>().ConnectionString; 
};

And finally you will have to register your IConnectionstringProvider at the beginning of the request lifetimescope :

class Program
{
    static void Main(string[] args)
    {
        ContainerBuilder builder = new ContainerBuilder();
        builder.RegisterModule(new ConnectionStringModule());

        IContainer container = builder.Build();
        container.ChildLifetimeScopeBeginning += container_ChildLifetimeScopeBeginning;
    }

    private static void container_ChildLifetimeScopeBeginning(
        Object sender, LifetimeScopeBeginningEventArgs e)
    {
        String host = HttpContext.Current.Request.Url.Host; 
        ContainerBuilder childLifetimeScopeBuilder = new ContainerBuilder();
        childLifetimeScopeBuilder.RegisterInstance(new ConnectionStringProvider(host))
                                 .As<IConnectionstringProvider>()
                                 .SingleInstance();
        childLifetimeScopeBuilder.Update(e.LifetimeScope.ComponentRegistry);
    }
}

Of course there is many way to do it but you have the idea

Cyril Durand
  • 15,834
  • 5
  • 54
  • 62