The previous version was strictly tied to the ASP.NET session mechanism, so it was difficult to use it in web farms with automatic load balancing between servers. The client was able to work only with the server on which its session was started since it contained an instance of the QueryBuilder object for this user. Now, this restriction is in the past.

The default behaviour of saving the component state within the ASP.NET session remains the same as it's fast and convenient for simple apps, but now you can change it easily. Have a look how an instance of the QueryBuilder and QueryTransformer object is obtained in the demo projects. The QueryBuilderStore factory uses the SessionStore object under the hood:

public ActionResult Index()
{
    // Get an instance of the QueryBuilder object
    var qb = QueryBuilderStore.Get();

    if (qb == null) {
        qb = new QueryBuilder();
        
        // initialize new instance here
        InitQueryBuilder(qb);

        QueryBuilderStore.Put(qb)
    }
}

What you can do now in the Corporate version of Active Query Builder ASP.NET Edition is to replace the storage provider with your implementation of IQueryBuilderStorageProvider interface:

public interface IQueryBuilderStorageProvider
{
    QueryBuilder Get(string id);
    void Put(QueryBuilder qb);
    void Delete(string id);
}

Then you can replace the default SessionQueryBuilderProvider with your own during the application startup:

public class MvcRazor : HttpApplication
{
    protected void Application_Start()
    {
            /*...*/
            QueryBuilderStore.Provider = new QueryBuilderRedisStoreProvider();
    }
}

and use the component the same way as you did that before.

Note: The state of the QueryBuilder object can be serialised and de-serialised using just one LayoutSQL property since the version 3.1. All you have to do is to redefine the Get, Put and Delete methods and make one additional step: after de-serializing an instance of the QueryBuilder object, you may need to assign a database connection or pass the pre-loaded content of MetadataContainer to it.

The following implementation lets store the component's state in Redis:

    using StackExchange.Redis;

    public class QueryBuilderRedisStoreProvider : IQueryBuilderProvider
    {
        private readonly IDatabase _db;

        public RedisQueryBuilderProvider()
        {
            var redis = ConnectionMultiplexer.Connect();
            _db = redis.GetDatabase();
        }

        public QueryBuilder Get(string id)
        {
            var layout = _db.StringGet(id);

            var qb = new QueryBuilder(id);

            if (layout.HasValue)
                qb.LayoutSQL = layout;

            return qb;
        }

        public void Put(QueryBuilder qb)
        {
            _db.StringSetAsync(qb.Tag, qb.LayoutSQL);
        }

        public void Delete(string id)
        {
            _db.StringSetAsync(id, "");
        }
    }

The new CustomStorage demo project illustrates this functionality by saving the component's state in a SQLite database.