Search code examples
asp.net-mvcmulti-tenantcastle-activerecord

How to initialize and persist Castle ActiveRecordStarter per session for multi tenancy apps?


I am using Castle ActiveRecord in my Asp.net / MVC 2 / Multi-tenancy application with SQL Server as my backend.

For every user logging in, the app loads the corresponding DB, dynamically at run time like below:

IDictionary<string, string> properties = new Dictionary<string, string>();

    properties.Add("connection.driver_class", "NHibernate.Driver.SqlClientDriver");
    properties.Add("dialect", "NHibernate.Dialect.MsSql2005Dialect");
    properties.Add("connection.provider", "NHibernate.Connection.DriverConnectionProvider");
    properties.Add("proxyfactory.factory_class", "NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle");

    properties.Add("connection.connection_string", strDBConnection);

    InPlaceConfigurationSource source = new InPlaceConfigurationSource();
    source.Add(typeof(ActiveRecordBase), properties);

    ActiveRecordStarter.Initialize(new System.Reflection.Assembly[] { asm1 }, source);

The strDBConnection string comes from another small database that holds the user info, corresponding DB, etc.

Scenario:

  1. When a user logs in, his DB gets loaded, he can do his CRUD jobs -- No Probs !
  2. Another user logs in (from another remote machine) his DB gets loaded -- No Probs !
  3. Now, when the first user reads from DB, he sees new data from the second user's DB

My little understanding for this behavious is : ActiveRecordStarter is a Static object.

Could someone help me with a solution for this situation ?

The expected behaviour: each user should access his own DB only, securely, in parallel / at the same time.

Thanks a lot !


Solution

  • ActiveRecordStarter.Initialize should only be called once in your app (in Application_Start in Global.asax).

    To achieve what you want, create a class that inherits from NHibernate.Connection.DriverConnectionProvider:

    public class MyCustomConnectionProvider : DriverConnectionProvider
    {
        protected override string GetNamedConnectionString(IDictionary<string, string> settings)
        {
            return string.Empty;
        }
    
        public override IDbConnection GetConnection()
        {
            // Get your connection here, based on the request
            // You can use HttpContext.Current to get information about the current request
            var conn = Driver.CreateConnection();
            conn.ConnectionString = ... // Retrieve the connection string here;
            conn.Open();
            return conn;
        }
    }
    

    Then set the connection.provider property to the name of your class:

     properties.Add("connection.provider", "MyCompany.Domain.MyCustomConnectionProvider, MyCompany.AssemblyName");