Integration with Azure Elastic Scale and connection pool issues

May 19, 2015 at 5:29 AM
Has anyone trying to use this framework with the Azure Elastic Scale library? I've been trying to integrate this and while it is technically "working" (it gives me back the correct SQL connection when I provide the sharding key), I have a serious bug.

Basically, my database connections are not closing after each request to my Web API layer when using Elastic Scale to provide the SQL connection in the DataContect constructor.

Below is the code showing how I have Elastic Scale integrated as well as the commented out sections showing how I have it setup when not using Elastic Scale and instead using a connection string from my web.config.

Any help is appreciated.

Domain Model:
public partial class MyDomainModel : DataContext<Guid> {
    
    // Use Elastic Scale
    public MyDomainModel(IShardManager sharding, ITenantStore tenantStore, string connStr)
        : base(
        sharding.GetShardMap(),
        tenantStore.CurrentTenantId,
        connStr
    ) {        
    }
    
    // Original without Elastic Scale
    //public MyDomainModel()
    //    : base("name=MyDomainModel") {
    //}
        
    public virtual DbSet<AuditLog> AuditLogs { get; set; }
    ...        
}
DataContext:
public class DataContext<T> : DbContext, IDataContextAsync {    
    private bool _disposed;
    private readonly Guid _instanceId;        

    // Regular constructor calls should not happen.
    // 1.) Use the protected c'tor with the connection string parameter to intialize a new shard. 
    // 2.) Use the public c'tor with the shard map parameter in the regular application calls with a tenant id.
    private DataContext() { }

    // C'tor to deploy schema and migrations to a new shard
    // Also original C'tor when used without Elastic Scale
    public DataContext(string connectionString)
        : base(SetInitializerForConnection(connectionString)) {

        _instanceId=Guid.NewGuid();
        Configuration.LazyLoadingEnabled=false;
        Configuration.ProxyCreationEnabled=false;
    }

    // Only static methods are allowed in calls into base class c'tors
    private static string SetInitializerForConnection(string connnectionString) {
        // We want existence checks so that the schema can get deployed
        Database.SetInitializer<DataContext<T>>(new CreateDatabaseIfNotExists<DataContext<T>>());
        return connnectionString;
    }

    // C'tor for data dependent routing. This call will open a validated connection routed to the proper
    // shard by the shard map manager. Note that the base class c'tor call will fail for an open connection
    // if migrations need to be done and SQL credentials are used. This is the reason for the 
    // separation of c'tors into the DDR case (this c'tor) and the internal c'tor for new shards.
    public DataContext(ShardMap shardMap, T shardingKey, string connectionStr)
        : base(CreateDDRConnection(shardMap, shardingKey, connectionStr), true /* contextOwnsConnection */) {
            
        Configuration.ProxyCreationEnabled=false;
    }

    // Only static methods are allowed in calls into base class c'tors
    private static DbConnection CreateDDRConnection(ShardMap shardMap, T shardingKey, string connectionStr) {
        // No initialization
        Database.SetInitializer<DataContext<T>>(null);

        // Ask shard map to broker a validated connection for the given key
        SqlConnection conn = new SqlConnection();
        try {
            conn=shardMap.OpenConnectionForKey<T>(shardingKey, connectionStr, ConnectionOptions.Validate);
        } catch (Exception ex) {
            throw new ShardNotFoundException(ex.Message);
        }
        return conn;
    }
}
Unity config:
public class UnityConfig {
    public static void RegisterComponents(HttpConfiguration config) {
        UnityContainer container = new UnityContainer();
        container        
            // Elastic Scale sharding
            .RegisterInstance<IShardManager>(new ShardManager(), new ContainerControlledLifetimeManager())           
            
            // Our tenant resolver
            .RegisterType<ITenantStore, TenantStore>(new PerRequestLifetimeManager())
            .RegisterType<ITenantConnectionStringFactory, TenantConnectionStringFactory>(new PerRequestLifetimeManager())
            
            // Use Elastic Scale
            .RegisterType<IDataContextAsync, MyDomainModel>(
                new PerRequestLifetimeManager(),
                new InjectionConstructor(
                    typeof(IShardManager),
                    typeof(ITenantStore),
                    new TenantConnectionStringFactory().ConnectionString
                )
            )
            // Without Elastic Scale
            //.RegisterType<IDataContextAsync, MyDomainModel>(new PerRequestLifetimeManager())
            
            .RegisterType<IRepositoryProvider, RepositoryProvider>(
                new PerRequestLifetimeManager(),
                new InjectionConstructor(new object[] { new RepositoryFactories() })
            )
            .RegisterType<IUnitOfWorkAsync, Repository.Pattern.Ef6.UnitOfWork>(new PerRequestLifetimeManager())
            .RegisterType<IRepositoryAsync<AuditLog>, Repository<AuditLog>>(new PerRequestLifetimeManager())
            .RegisterType<IAuditService, AuditService>(new PerRequestLifetimeManager());
            
        DependencyResolver.SetResolver(new Unity.Mvc5.UnityDependencyResolver(container));
        config.DependencyResolver = new UnityDependencyResolver(container);            
    }
}
Coordinator
May 27, 2015 at 12:16 AM
I have not used URF with Azure Elastic Scale, however my default response would be there is something not configured correctly with the lifecycle/lifetime manger for Unity, hope this helps.
Marked as answer by lelong37 on 5/26/2015 at 4:16 PM