/// <summary>
        /// Reconfigure the server-side objects previously configured
        /// </summary>
        public async Task ReconfigureAsync(string xconfig)
        {
            var command = "call {0}.Reconfigure(\"{1}\");".FormatWith(ClrAssemblyID, xconfig.EscapeMdxString());

            Action action = () =>
            {
                AnalysisServicesHelper.ExecuteNonQuery(_connectionString, command);
            };

            using (var task = Task.Factory.StartNew(action))
                await task.ConfigureAwait(continueOnCapturedContext : false);
        }
        /// <summary>
        /// Uninstall the server-side objects previously configured
        /// </summary>
        public async Task UninstallAsync()
        {
            var command = "call {0}.Uninstall();".FormatWith(ClrAssemblyID);

            Action action = () =>
            {
                AnalysisServicesHelper.ExecuteNonQuery(_connectionString, command);
                AnalysisServicesHelper.UnregisterClrAssembly(_connectionString, ClrAssemblyID);
            };

            using (var task = Task.Factory.StartNew(action))
                await task.ConfigureAwait(continueOnCapturedContext : false);
        }
        /// <summary>
        /// Execute server-side AnalyzeBatch procedure
        /// </summary>
        public async Task <AnalyzerStatistics> AnalyzeBatchAsync(string connectionStringBatch, string statement, ClearCacheMode clearCacheMode = ClearCacheMode.Default, string xconfig = null, string batchName = null, bool throwOnError = true)
        {
            #region Argument exception

            if (connectionStringBatch == null)
            {
                throw new ArgumentNullException("connectionStringBatch");
            }

            if (statement == null)
            {
                throw new ArgumentNullException("statement");
            }

            #endregion

            var currentProcess = Process.GetCurrentProcess();
            var processName    = currentProcess.ProcessName;
            var clientVersion  = "{0} ({1})".FormatWith(currentProcess.MainModule.FileVersionInfo.FileVersion, ClientVersion);

#if DEBUG
            Func <Guid, ClearCacheMode, string> commandAnalyze = (batchID, cacheMode) => "call {0}.AnalyzeBatchWithDebug(\"{1}\", {2}, \"{3}\", \"{4}\", \"{5}\", \"{6}\", \"{7}\", \"{8}\");".FormatWith(ClrAssemblyID, statement.EscapeMdxString(), (int)cacheMode, batchID, connectionStringBatch.EscapeMdxString(), clientVersion, processName, batchName, throwOnError);
#else
            Func <Guid, ClearCacheMode, string> commandAnalyze = (batchID, cacheMode) => "call {0}.AnalyzeBatch(\"{1}\", {2}, \"{3}\", \"{4}\", \"{5}\", \"{6}\", \"{7}\", \"{8}\");".FormatWith(ClrAssemblyID, statement.EscapeMdxString(), (int)cacheMode, batchID, connectionStringBatch.EscapeMdxString(), clientVersion, processName, batchName, throwOnError);
#endif
            Func <AnalyzerStatistics> analyzeProcedure = () =>
            {
                ExecutionProgressControl?.StartMonitor_Batch();
                try
                {
                    var batchID = Guid.NewGuid();

                    var coldCacheExecutionResult = AnalysisServicesHelper.ExecuteForDataSet(_connectionString, commandAnalyze(batchID, clearCacheMode), _cancellationTokenSource.Token).ToAnalyzerExecutionResult(dispose: true);
                    var warmCacheExecutionResult = AnalysisServicesHelper.ExecuteForDataSet(_connectionString, commandAnalyze(batchID, ClearCacheMode.Nothing), _cancellationTokenSource.Token).ToAnalyzerExecutionResult(dispose: true);

                    return(AnalyzerStatistics.CreateFromAnalyzerExecutionResults(coldCacheExecutionResult, warmCacheExecutionResult));
                }
                finally
                {
                    ExecutionProgressControl?.StopMonitor_Batch();
                }
            };

            using (_cancellationTokenSource = new CancellationTokenSource())
            {
                using (var task = Task.Factory.StartNew(analyzeProcedure))
                    return(await task.ConfigureAwait(continueOnCapturedContext : false));
            }
        }
        /// <summary>
        /// Retrieve server-side assembly configuration
        /// </summary>
        public async Task <DataTable> GetConfigurationForEngineAsync()
        {
            var command = "call {0}.GetConfiguration({1});".FormatWith(ClrAssemblyID, (int)ConfigurationType.Engine);

            Func <DataTable> function = () =>
            {
                return(AnalysisServicesHelper.ExecuteForDataTable(_connectionString, command));
            };

            using (_cancellationTokenSource = new CancellationTokenSource())
            {
                using (var task = Task.Factory.StartNew(function))
                    return(await task.ConfigureAwait(continueOnCapturedContext : false));
            }
        }
        /// <summary>
        /// Retrieve server-side assembly version
        /// </summary>
        public async Task <Version> GetVersionAsync()
        {
            var command = "call {0}.GetVersion();".FormatWith(ClrAssemblyID);

            Func <Version> function = () =>
            {
                if (AnalysisServicesHelper.ClrAssemblyInstalled(_connectionString, ClrAssemblyID))
                {
                    return(AnalysisServicesHelper.ExecuteForDataTable(_connectionString, command).ToVersion());
                }

                return(null);
            };

            using (var task = Task.Factory.StartNew(function))
                return(await task.ConfigureAwait(continueOnCapturedContext : false));
        }
        /// <summary>
        /// Install and configure the server-side objects using custom assembly file path
        /// </summary>
        public async Task InstallAsync(string assemblyFilePath = null)
        {
            if (assemblyFilePath == null)
            {
                string clrAssemblyFileName;

                switch (AnalysisServicesHelper.InstanceVersion(_connectionString).Major)
                {
                case 11:
                    clrAssemblyFileName = ClrAssemblyFileNameFormat.FormatWith(2012);
                    break;

                case 12:
                    clrAssemblyFileName = ClrAssemblyFileNameFormat.FormatWith(2014);
                    break;

                case 13:
                    clrAssemblyFileName = ClrAssemblyFileNameFormat.FormatWith(2016);
                    break;

                case 14:
                    clrAssemblyFileName = ClrAssemblyFileNameFormat.FormatWith(2017);
                    break;

                default:
                    throw new ApplicationException("Unsupported SSAS version");
                }

                assemblyFilePath = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), clrAssemblyFileName);
            }

            var command = "call {0}.Install();".FormatWith(ClrAssemblyID);

            Action action = () =>
            {
                AnalysisServicesHelper.RegisterClrAssembly(_connectionString, ClrAssemblyID, ClrAssemblyName, ClrAssemblyDescription, assemblyFilePath);
                AnalysisServicesHelper.ExecuteNonQuery(_connectionString, command);
            };

            using (var task = Task.Factory.StartNew(action))
                await task.ConfigureAwait(continueOnCapturedContext : false);
        }
        /// <summary>
        /// Execute server-side Analyze procedure
        /// </summary>
        public async Task <AnalyzerStatistics> AnalyzeAsync(string statement, ClearCacheMode clearCacheMode = ClearCacheMode.Default, int queryResultRowLimit = 0)
        {
            #region Argument exception

            if (statement == null)
            {
                throw new ArgumentNullException("statement");
            }

            #endregion

            var currentProcess = Process.GetCurrentProcess();
            var processName    = currentProcess.ProcessName;
            var clientVersion  = "{0} ({1})".FormatWith(currentProcess.MainModule.FileVersionInfo.FileVersion, ClientVersion);

#if DEBUG
            Func <ClearCacheMode, string> commandAnalyze = (cacheMode) => "call {0}.AnalyzeWithDebug(\"{1}\", {2}, {3}, \"{4}\", \"{5}\");".FormatWith(ClrAssemblyID, statement.EscapeMdxString(), (int)cacheMode, queryResultRowLimit, clientVersion, processName);
#else
            Func <ClearCacheMode, string> commandAnalyze = (cacheMode) => "call {0}.Analyze(\"{1}\", {2}, {3}, \"{4}\", \"{5}\");".FormatWith(ClrAssemblyID, statement.EscapeMdxString(), (int)cacheMode, queryResultRowLimit, clientVersion, processName);
#endif
            Action <DataSet, string> saveDebug = (data, dbgType) =>
            {
                if (!DebugToXml)
                {
                    return;
                }

                var debugPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"SSASQueryAnalyzer\debug");
                Directory.CreateDirectory(debugPath);
                data.WriteXml(Path.Combine(debugPath, "AnalyzerExecutionResult-{0:yyyyMMdd-hhmmss-fff}.{1}").FormatWith(DateTime.Now, dbgType), XmlWriteMode.WriteSchema);
            };

            Func <AnalyzerStatistics> analyzeProcedure = () =>
            {
                ExecutionProgressControl?.StartMonitor();
                try
                {
                    AnalyzerExecutionResult coldCacheExecutionResult;
                    AnalyzerExecutionResult warmCacheExecutionResult;

                    using (var data = AnalysisServicesHelper.ExecuteForDataSet(_connectionString, commandAnalyze(clearCacheMode), _cancellationTokenSource.Token))
                    {
                        saveDebug(data, "xqac");
                        coldCacheExecutionResult = data.ToAnalyzerExecutionResult(dispose: true);
                    }

                    ExecutionProgressControl?.ColdCacheExecutionCompleted();

                    using (var data = AnalysisServicesHelper.ExecuteForDataSet(_connectionString, commandAnalyze(ClearCacheMode.Nothing), _cancellationTokenSource.Token))
                    {
                        saveDebug(data, "xqaw");
                        warmCacheExecutionResult = data.ToAnalyzerExecutionResult(dispose: true);
                    }

                    ExecutionProgressControl?.WarmCacheExecutionCompleted();

                    return(AnalyzerStatistics.CreateFromAnalyzerExecutionResults(coldCacheExecutionResult, warmCacheExecutionResult));
                }
                finally
                {
                    ExecutionProgressControl?.StopMonitor();
                }
            };

            using (_cancellationTokenSource = new CancellationTokenSource())
            {
                using (var task = Task.Factory.StartNew(analyzeProcedure))
                    return(await task.ConfigureAwait(continueOnCapturedContext : false));
            }
        }
        /// <summary>
        ///
        /// </summary>
        public ProcedureEvents GetLastEvent()
        {
            var command = "call {0}.GetLastEvent();".FormatWith(ClrAssemblyID);

            return((ProcedureEvents)AnalysisServicesHelper.ExecuteScalar(_connectionString, command));
        }