diff --git a/src/Dax.Model.Extractor/Data/ConnectionStringUtils.cs b/src/Dax.Model.Extractor/Data/ConnectionStringUtils.cs new file mode 100644 index 0000000..c6c28d4 --- /dev/null +++ b/src/Dax.Model.Extractor/Data/ConnectionStringUtils.cs @@ -0,0 +1,46 @@ +using System; +using System.Data.Common; +using System.Globalization; + +namespace Dax.Model.Extractor.Data +{ + public static class ConnectionStringUtils + { + public static string GetDataSource(string connectionString) => GetValue(connectionString, ConnectionStringKeywords.DataSource); + + public static string GetInitialCatalog(string connectionString) => GetValue(connectionString, ConnectionStringKeywords.InitialCatalog); + + public static string GetConnectionString(string serverNameOrConnectionString, string databaseName) + { + var builder = new DbConnectionStringBuilder(useOdbcRules: false); + try { + builder.ConnectionString = serverNameOrConnectionString; + } + catch { + // Assume servername + builder[ConnectionStringKeywords.Provider] = "MSOLAP"; + builder[ConnectionStringKeywords.DataSource] = serverNameOrConnectionString; + } + builder[ConnectionStringKeywords.InitialCatalog] = databaseName; + return builder.ConnectionString; + } + + private static string GetValue(string connectionString, string keyword) + { + var builder = new DbConnectionStringBuilder(useOdbcRules: false); + builder.ConnectionString = connectionString; + + if (builder.TryGetValue(keyword, out object value)) + return ((IConvertible)value).ToString(CultureInfo.InvariantCulture); + + return string.Empty; // default to empty string to keep the result consistent with the OleDbConnectionStringBuilder + } + } + + internal static class ConnectionStringKeywords + { + public const string Provider = "Provider"; + public const string DataSource = "Data Source"; + public const string InitialCatalog = "Initial Catalog"; + } +} diff --git a/src/Dax.Model.Extractor/Data/IDbConnectionExtensions.cs b/src/Dax.Model.Extractor/Data/IDbConnectionExtensions.cs deleted file mode 100644 index b0b3aa6..0000000 --- a/src/Dax.Model.Extractor/Data/IDbConnectionExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Microsoft.AnalysisServices.AdomdClient; -using System.Data; -using System.Data.OleDb; - -namespace Dax.Model.Extractor.Data -{ - internal static class IDbConnectionExtensions - { - public static IDbCommand CreateCommand(this IDbConnection connection, string commandText) - { - return connection switch - { - AdomdConnection adomdConnection => new AdomdCommand(commandText, adomdConnection), - OleDbConnection oledbConnection => new OleDbCommand(commandText, oledbConnection), - TomConnection tomConnection => new TomCommand(commandText, tomConnection), - _ => connection.CreateCommand(commandText) - }; - } - } -} diff --git a/src/Dax.Model.Extractor/Dax.Model.Extractor.csproj b/src/Dax.Model.Extractor/Dax.Model.Extractor.csproj index 70cad7c..a09df74 100644 --- a/src/Dax.Model.Extractor/Dax.Model.Extractor.csproj +++ b/src/Dax.Model.Extractor/Dax.Model.Extractor.csproj @@ -1,7 +1,7 @@  - net462;netcoreapp3.1;net6.0-windows + net462;netcoreapp3.1;net6.0 true @@ -22,13 +22,12 @@ - - + diff --git a/src/Dax.Model.Extractor/DmvExtractor.cs b/src/Dax.Model.Extractor/DmvExtractor.cs index 9a86a98..e4f5ac7 100644 --- a/src/Dax.Model.Extractor/DmvExtractor.cs +++ b/src/Dax.Model.Extractor/DmvExtractor.cs @@ -34,7 +34,9 @@ public class DmvExtractor protected IDbCommand CreateCommand(string commandText) { - return Connection.CreateCommand(commandText); + var command = Connection.CreateCommand(); + command.CommandText = commandText; + return command; } public DmvExtractor(Dax.Metadata.Model daxModel, IDbConnection connection, string serverName, string databaseName, string extractorApp, string extractorVersion) diff --git a/src/Dax.Model.Extractor/StatExtractor.cs b/src/Dax.Model.Extractor/StatExtractor.cs index 96d89b5..925980a 100644 --- a/src/Dax.Model.Extractor/StatExtractor.cs +++ b/src/Dax.Model.Extractor/StatExtractor.cs @@ -19,7 +19,9 @@ private StatExtractor(Dax.Metadata.Model daxModel, IDbConnection connection) protected IDbCommand CreateCommand(string commandText) { - return Connection.CreateCommand(commandText); + var command = Connection.CreateCommand(); + command.CommandText = commandText; + return command; } // UpdateStatisticsModel has been marked as obsolete because its usage may require rerunning the DMVs for models with DirectLake partitions. Since this logic should be handled by the library, we may consider removing it from the public APIs in a future release. diff --git a/src/Dax.Model.Extractor/TomExtractor.cs b/src/Dax.Model.Extractor/TomExtractor.cs index cf55f0c..41882d5 100644 --- a/src/Dax.Model.Extractor/TomExtractor.cs +++ b/src/Dax.Model.Extractor/TomExtractor.cs @@ -1,7 +1,10 @@ using Dax.Metadata; +using Dax.Model.Extractor.Data; using Microsoft.AnalysisServices.AdomdClient; +using Newtonsoft.Json.Linq; using System; -using System.Data.OleDb; +using System.Data.Common; +using System.Globalization; using System.Linq; using Tom = Microsoft.AnalysisServices.Tabular; @@ -288,7 +291,7 @@ public static Dax.Metadata.Model GetDaxModel(string connectionString, string app var database = GetDatabase(connectionString); Tom.Model tomModel = database.Model; string databaseName = database.Name; - string serverName = GetDataSource(connectionString); + string serverName = ConnectionStringUtils.GetDataSource(connectionString); var daxModel = TomExtractor.GetDaxModel(tomModel, applicationName, applicationVersion); @@ -312,18 +315,6 @@ public static Dax.Metadata.Model GetDaxModel(string connectionString, string app return daxModel; } - private static string GetDataSource(string connectionString) - { - var builder = new OleDbConnectionStringBuilder(connectionString); - return builder.DataSource; - } - - private static string GetInitialCatalog(string connectionString) - { - var builder = new OleDbConnectionStringBuilder(connectionString); - builder.TryGetValue("Initial Catalog", out object initialCatalog); - return initialCatalog.ToString(); - } public static Tom.Database GetDatabase(string serverName, string databaseName) { Tom.Server server = new(); @@ -337,7 +328,7 @@ public static Tom.Database GetDatabase(string connectionString) { Tom.Server server = new(); server.Connect(connectionString); - var databaseName = GetInitialCatalog(connectionString); + var databaseName = ConnectionStringUtils.GetInitialCatalog(connectionString); Tom.Database db = server.Databases.FindByName(databaseName); // if db is null either it does not exist or we do not have admin rights to it return db ?? throw new ArgumentException($"The database '{databaseName}' could not be found. Either it does not exist or you do not have admin rights to it."); @@ -350,7 +341,7 @@ public static Dax.Metadata.Model GetDaxModel(string serverName, string databaseN var daxModel = TomExtractor.GetDaxModel(tomModel, applicationName, applicationVersion); - string connectionString = GetConnectionString(serverName, databaseName); + string connectionString = ConnectionStringUtils.GetConnectionString(serverName, databaseName); using (AdomdConnection connection = new(connectionString)) { @@ -371,22 +362,5 @@ public static Dax.Metadata.Model GetDaxModel(string serverName, string databaseN } return daxModel; } - - private static string GetConnectionString(string dataSourceOrConnectionString, string databaseName) - { - OleDbConnectionStringBuilder csb = new(); - try - { - csb.ConnectionString = dataSourceOrConnectionString; - } - catch - { - // Assume servername - csb.Provider = "MSOLAP"; - csb.DataSource = dataSourceOrConnectionString; - } - csb["Initial Catalog"] = databaseName; - return csb.ConnectionString; - } } } diff --git a/src/Dax.Vpax.CLI/Commands/ExportCommandOptions.cs b/src/Dax.Vpax.CLI/Commands/ExportCommandOptions.cs index f6ffb15..8d45320 100644 --- a/src/Dax.Vpax.CLI/Commands/ExportCommandOptions.cs +++ b/src/Dax.Vpax.CLI/Commands/ExportCommandOptions.cs @@ -1,5 +1,5 @@ using System.CommandLine; -using System.Data.OleDb; +using System.Data.Common; namespace Dax.Vpax.CLI.Commands; @@ -11,12 +11,14 @@ internal static class ExportCommandOptions parse: (result) => { var connectionString = result.Tokens.Single().Value; - - var builder = new OleDbConnectionStringBuilder(connectionString); - if (!builder.ContainsKey("Initial Catalog")) - result.ErrorMessage = "The connection string does not contain the 'Initial Catalog' property."; - - return connectionString; + { + var builder = new DbConnectionStringBuilder(useOdbcRules: false); + builder.ConnectionString = connectionString; + + if (!builder.ContainsKey("Initial Catalog")) + result.ErrorMessage = "The connection string does not contain the 'Initial Catalog' property."; + } + return connectionString; // always return the original value }); public static readonly Argument PathArgument = new( diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 47e29ae..ca53b77 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -12,7 +12,6 @@ - diff --git a/utils/TestDaxWpf/MainWindow.xaml.cs b/utils/TestDaxWpf/MainWindow.xaml.cs index 2a83842..3cdcecb 100644 --- a/utils/TestDaxWpf/MainWindow.xaml.cs +++ b/utils/TestDaxWpf/MainWindow.xaml.cs @@ -60,17 +60,17 @@ public static Dax.Metadata.Model GetDaxModel(string serverName, string databaseN server.Connect(serverName); Microsoft.AnalysisServices.Database db = server.Databases[databaseName]; Microsoft.AnalysisServices.Tabular.Model tomModel = db.Model; - var daxModel = Dax.Metadata.Extractor.TomExtractor.GetDaxModel(tomModel, "TestDaxModel", "0.1"); + var daxModel = Dax.Model.Extractor.TomExtractor.GetDaxModel(tomModel, "TestDaxModel", "0.1"); var connectionString = GetConnectionString(serverName, databaseName); using (var connection = new AdomdConnection(connectionString)) { // Populate statistics from DMV - Dax.Metadata.Extractor.DmvExtractor.PopulateFromDmv(daxModel, connection, serverName, databaseName, "TestDaxModel", "0.1"); + Dax.Model.Extractor.DmvExtractor.PopulateFromDmv(daxModel, connection, serverName, databaseName, "TestDaxModel", "0.1"); // Populate statistics by querying the data model if (readStatisticsFromData) { - Dax.Metadata.Extractor.StatExtractor.UpdateStatisticsModel(daxModel, connection, 10); + Dax.Model.Extractor.StatExtractor.UpdateStatisticsModel(daxModel, connection, 10); } } return daxModel; diff --git a/utils/TestPowerBI/Form1.cs b/utils/TestPowerBI/Form1.cs index 1d0eb46..fabe390 100644 --- a/utils/TestPowerBI/Form1.cs +++ b/utils/TestPowerBI/Form1.cs @@ -221,7 +221,7 @@ private void TestPbiShared(string name, string id) //Console.WriteLine("Connection open"); Dax.Metadata.Model m = new Dax.Metadata.Model(); - Dax.Metadata.Extractor.DmvExtractor.PopulateFromDmv(m, conn, serverName, databaseName, "Test", "0.1"); + Dax.Model.Extractor.DmvExtractor.PopulateFromDmv(m, conn, serverName, databaseName, "Test", "0.1"); Dax.Vpax.Tools.VpaxTools.ExportVpax(@"c:\temp\" + name + ".vpax",m); } diff --git a/utils/TestWpfPowerBI/MainWindow.xaml.cs b/utils/TestWpfPowerBI/MainWindow.xaml.cs index f01e6c3..605b8fc 100644 --- a/utils/TestWpfPowerBI/MainWindow.xaml.cs +++ b/utils/TestWpfPowerBI/MainWindow.xaml.cs @@ -322,8 +322,8 @@ private Dax.ViewModel.VpaModel GetVpaModel(string groupName, string datasetName, Dax.Metadata.Model m = new Dax.Metadata.Model(); // NOTE: groupName is the serverName in the arguments // datasetName is the databaseName in the arguments - Dax.Metadata.Extractor.DmvExtractor.PopulateFromDmv(m, conn, groupName, datasetName, "Test", "0.1"); - Dax.Metadata.Extractor.StatExtractor.UpdateStatisticsModel(m, conn, 4); + Dax.Model.Extractor.DmvExtractor.PopulateFromDmv(m, conn, groupName, datasetName, "Test", "0.1"); + Dax.Model.Extractor.StatExtractor.UpdateStatisticsModel(m, conn, 4); return new Dax.ViewModel.VpaModel(m); }