/// <summary> /// This method ensures the tabular model is online and populates the CompatibilityLevel property. /// </summary> /// <param name="closedBimFile">A Boolean specifying if the user cancelled the comparison. For the case where running in Visual Studio, the user has the option of cancelling if the project BIM file is open.</param> public void InitializeCompatibilityLevel(bool closedBimFile = false) { if (UseBimFile) { TOM.Database tomDatabase = null; bool exceptionLoadingFile = false; try { tomDatabase = OpenDatabaseFromFile(); } catch { exceptionLoadingFile = true; } if (exceptionLoadingFile || tomDatabase == null) { throw new ConnectionException($"Can't load file \"{_bimFile}\"."); } _compatibilityLevel = tomDatabase.CompatibilityLevel; _dataSourceVersion = tomDatabase.Model.DefaultPowerBIDataSourceVersion.ToString(); _directQuery = (tomDatabase.Model != null && tomDatabase.Model.DefaultMode == Microsoft.AnalysisServices.Tabular.ModeType.DirectQuery); return; } if (UseProject) { //Initialize _projectDirectoryInfo FileInfo projectFileInfo; if (_project == null) { //Probably running in command-line mode projectFileInfo = new FileInfo(_projectFile); } else { projectFileInfo = new FileInfo(_project.FullName); } _projectDirectoryInfo = new DirectoryInfo(projectFileInfo.Directory.FullName); //Read settings file to get workspace server/db ReadSettingsFile(); //Read project file to get deployment server/cube names, and bim file ReadProjectFile(); //Overwrite the server if a workspace server provided if (_workspaceServerProvided) { this.ServerName = _workspaceServer; } } Microsoft.AnalysisServices.Server amoServer = new Microsoft.AnalysisServices.Server(); try { amoServer.Connect(BuildConnectionString()); } catch (ConnectionException) when(UseProject) { //See if can find integrated workspace server bool foundServer = false; string tempDataDir = Path.GetTempPath() + @"Microsoft\Microsoft SQL Server\OLAP\LocalServer\Data"; if (Directory.Exists(tempDataDir)) { var subDirs = Directory.GetDirectories(tempDataDir).OrderByDescending(d => new DirectoryInfo(d).CreationTime); //Need to order by descending in case old folders hanging around when VS was killed and SSDT didn't get a chance to clean up after itself foreach (string subDir in subDirs) { string[] iniFilePath = Directory.GetFiles(subDir, "msmdsrv.ini"); if (iniFilePath.Length == 1 && File.ReadAllText(iniFilePath[0]).Contains("<DataDir>" + _projectDirectoryInfo.FullName + @"\bin\Data</DataDir>")) //Todo: proper xml lookup { //Assuming this must be the folder, so now get the port number string[] portFilePath = Directory.GetFiles(subDir, "msmdsrv.port.txt"); if (portFilePath.Length == 1) { string port = File.ReadAllText(portFilePath[0]).Replace("\0", ""); this.ServerName = $"localhost:{Convert.ToString(port)}"; amoServer.Connect(BuildConnectionString()); foundServer = true; break; } } } } if (!foundServer) { throw; } } ////non-admins can't see any ServerProperties: social.msdn.microsoft.com/Forums/sqlserver/en-US/3d0bf49c-9034-4416-9c51-77dc32bf8b73/determine-current-user-permissionscapabilities-via-amo-or-xmla //if (!(amoServer.ServerProperties.Count > 0)) //non-admins can't see any ServerProperties //{ // throw new Microsoft.AnalysisServices.ConnectionException($"Current user {WindowsIdentity.GetCurrent().Name} is not an administrator on the Analysis Server " + this.ServerName); //} if (amoServer.ServerMode != ServerMode.Tabular && amoServer.ServerMode != ServerMode.SharePoint) //SharePoint is what Power BI Desktop runs as { throw new ConnectionException($"Analysis Server {this.ServerName} is not running in Tabular mode"); } Microsoft.AnalysisServices.Database amoDatabase = null; if (this.DatabaseName == "" && this.ServerName.ToUpper().StartsWith("localhost:".ToUpper())) { //PBI Desktop doesn't have db name yet if (amoServer.Databases.Count > 0) { amoDatabase = amoServer.Databases[0]; this.DatabaseName = amoDatabase.Name; } } else { amoDatabase = amoServer.Databases.FindByName(this.DatabaseName); } if (amoDatabase == null) { if (!this.UseProject) { throw new ConnectionException("Could not connect to database " + this.DatabaseName); } else { /* Check if folder exists using SystemGetSubdirs. If so attach. If not, do nothing - when execute BIM file below will create automatically. * Using XMLA to run SystemGetSubdirs rather than ADOMD.net here don't want a reference to ADOMD.net Dll. * Also, can't use Server.Execute method because it only takes XMLA execute commands (as opposed to XMLA discover commands), so need to submit the full soap envelope */ string dataDir = amoServer.ServerProperties["DataDir"].Value; if (dataDir.EndsWith("\\")) { dataDir = dataDir.Substring(0, dataDir.Length - 1); } string commandStatement = String.Format("SystemGetSubdirs '{0}'", dataDir); bool foundFault = false; XmlNodeList rows = Core.Comparison.ExecuteXmlaCommand(amoServer, "", commandStatement, ref foundFault); string dbDir = ""; foreach (XmlNode row in rows) { XmlNode dirNode = null; XmlNode allowedNode = null; foreach (XmlNode childNode in row.ChildNodes) { if (childNode.Name == "Dir") { dirNode = childNode; } else if (childNode.Name == "Allowed") { allowedNode = childNode; } } if (dirNode != null && allowedNode != null && dirNode.InnerText.Length >= this.DatabaseName.Length && dirNode.InnerText.Substring(0, this.DatabaseName.Length) == this.DatabaseName && allowedNode.InnerText.Length > 0 && allowedNode.InnerText == "1") { dbDir = dataDir + "\\" + dirNode.InnerText; break; } } if (dbDir != "") { //attach amoServer.Attach(dbDir); amoServer.Refresh(); amoDatabase = amoServer.Databases.FindByName(this.DatabaseName); } } } if (this.UseProject) { //_bimFileFullName = GetBimFileFullName(); if (String.IsNullOrEmpty(_ssdtBimFile)) { throw new ConnectionException("Could not load BIM file for Project " + this.ProjectName); } if (!closedBimFile) //If just closed BIM file, no need to execute it { //Execute BIM file contents as script on workspace database //We don't know the compatibility level yet, so try parsing json, if fail, try xmla ... try { //Replace "SemanticModel" with db name. JObject jDocument = JObject.Parse(File.ReadAllText(_ssdtBimFile)); if (jDocument["name"] == null || jDocument["id"] == null) { throw new ConnectionException("Could not read JSON in BIM file " + _ssdtBimFile); } jDocument["name"] = DatabaseName; jDocument["id"] = DatabaseName; //Todo: see if Tabular helper classes for this once documentation available after CTP string command = $@"{{ ""createOrReplace"": {{ ""object"": {{ ""database"": ""{DatabaseName}"" }}, ""database"": {jDocument.ToString()} }} }} "; amoServer.Execute(command); } catch (JsonReaderException) { //Replace "SemanticModel" with db name. Could do a global replace, but just in case it's not called SemanticModel, use dom instead //string xmlaScript = File.ReadAllText(xmlaFileFullName); XmlDocument document = new XmlDocument(); document.Load(_ssdtBimFile); XmlNamespaceManager nsmgr = new XmlNamespaceManager(document.NameTable); nsmgr.AddNamespace("myns1", "http://schemas.microsoft.com/analysisservices/2003/engine"); XmlNode objectDatabaseIdNode = document.SelectSingleNode("//myns1:Object/myns1:DatabaseID", nsmgr); XmlNode objectDefinitionDatabaseIdNode = document.SelectSingleNode("//myns1:ObjectDefinition/myns1:Database/myns1:ID", nsmgr); XmlNode objectDefinitionDatabaseNameNode = document.SelectSingleNode("//myns1:ObjectDefinition/myns1:Database/myns1:Name", nsmgr); if (objectDatabaseIdNode == null || objectDefinitionDatabaseIdNode == null || objectDefinitionDatabaseNameNode == null) { throw new ConnectionException("Could not access XMLA in BIM file " + _ssdtBimFile); } objectDatabaseIdNode.InnerText = DatabaseName; objectDefinitionDatabaseIdNode.InnerText = DatabaseName; objectDefinitionDatabaseNameNode.InnerText = DatabaseName; //1103, 1100 projects store the xmla as Alter (equivalent to createOrReplace), so just need to execute amoServer.Execute(document.OuterXml); } } //need next lines in case just created the db using the Execute method //amoServer.Refresh(); //todo workaround for bug 9719887 on 3/10/17 need to disconnect and reconnect amoServer.Disconnect(); amoServer.Connect(BuildConnectionString()); amoDatabase = amoServer.Databases.FindByName(this.DatabaseName); } if (amoDatabase == null) { throw new ConnectionException($"Can not load/find database {this.DatabaseName}."); } _compatibilityLevel = amoDatabase.CompatibilityLevel; if (_compatibilityLevel >= 1400) { _dataSourceVersion = amoDatabase.Model.DefaultPowerBIDataSourceVersion.ToString(); } _serverMode = amoServer.ServerMode; _directQuery = ((amoDatabase.Model != null && amoDatabase.Model.DefaultMode == Microsoft.AnalysisServices.Tabular.ModeType.DirectQuery) || amoDatabase.DirectQueryMode == DirectQueryMode.DirectQuery || amoDatabase.DirectQueryMode == DirectQueryMode.InMemoryWithDirectQuery || amoDatabase.DirectQueryMode == DirectQueryMode.DirectQueryWithInMemory); }