2016-12-02 11:25:57 +08:00
using System ;
using System.Collections.Generic ;
using Microsoft.AnalysisServices.Tabular ;
2017-01-05 12:50:54 +08:00
using System.Diagnostics ;
2016-12-02 11:25:57 +08:00
namespace AsPartitionProcessing.SampleClient
{
2017-01-09 12:02:38 +08:00
#region enum ExecutionMode
/// <summary>
/// Execution mode of the SampleClient application.
/// </summary>
enum ExecutionMode
2016-12-06 13:37:16 +08:00
{
2017-01-09 12:02:38 +08:00
/// <summary>
/// Initialize configuration inline using sample values.
/// </summary>
2016-12-06 13:37:16 +08:00
InitializeInline ,
2017-01-09 12:02:38 +08:00
/// <summary>
/// Initialize from configuration and logging database.
/// </summary>
2016-12-06 13:37:16 +08:00
InitializeFromDatabase ,
2017-01-09 12:02:38 +08:00
/// <summary>
/// Merge partitions in a table based on other parameters.
/// </summary>
2016-12-19 16:43:00 +08:00
MergePartitions ,
2017-01-09 12:02:38 +08:00
/// <summary>
/// Defragment partitioned tables in the model. List of partitioned tables defined in the configuration and logging database.
/// </summary>
2016-12-19 16:43:00 +08:00
DefragPartitionedTables
2016-12-06 13:37:16 +08:00
}
2017-01-09 12:02:38 +08:00
#endregion
2016-12-02 11:25:57 +08:00
class Program
{
2016-12-06 13:37:16 +08:00
//Set sample execution mode here:
2017-01-09 12:02:38 +08:00
private static ExecutionMode _executionMode = ExecutionMode . InitializeInline ;
2016-12-02 11:25:57 +08:00
2017-01-07 12:18:32 +08:00
static int Main ( string [ ] args )
2016-12-02 11:25:57 +08:00
{
try
{
2017-01-07 12:18:32 +08:00
#region Set defaults for merging & read command - line arguments if provided
2017-01-05 12:50:54 +08:00
2017-01-07 12:18:32 +08:00
string mergeTable = "Internet Sales" ;
Granularity mergeTargetGranuarity = Granularity . Yearly ;
string mergePartitionKey = "2012" ;
2017-01-09 12:02:38 +08:00
bool help ;
2017-01-07 12:18:32 +08:00
2017-01-09 12:02:38 +08:00
ParseArgs ( args , ref mergeTable , ref mergeTargetGranuarity , ref mergePartitionKey , out help ) ;
if ( help )
return 0 ; //ERROR_SUCCESS
2017-01-05 12:50:54 +08:00
#endregion
2017-01-07 12:18:32 +08:00
2017-01-09 12:02:38 +08:00
if ( _executionMode = = ExecutionMode . InitializeInline )
2016-12-02 11:25:57 +08:00
{
2017-01-05 12:50:54 +08:00
//Perform Processing
PartitionProcessor . PerformProcessing ( InitializeInline ( ) , LogMessage ) ;
2016-12-02 11:25:57 +08:00
}
2017-01-05 12:50:54 +08:00
else
2016-12-02 11:25:57 +08:00
{
2017-01-05 12:50:54 +08:00
List < ModelConfiguration > modelsConfig = InitializeFromDatabase ( ) ;
2016-12-02 11:25:57 +08:00
2017-01-05 12:50:54 +08:00
foreach ( ModelConfiguration modelConfig in modelsConfig )
2016-12-19 16:43:00 +08:00
{
2017-01-05 12:50:54 +08:00
SetCredentials ( modelConfig ) ; //For Azure AS
switch ( _executionMode )
{
2017-01-09 12:02:38 +08:00
case ExecutionMode . InitializeFromDatabase :
2017-01-05 12:50:54 +08:00
//Perform Processing
PartitionProcessor . PerformProcessing ( modelConfig , LogMessage ) ;
break ;
2017-01-09 12:02:38 +08:00
case ExecutionMode . MergePartitions :
2017-01-05 12:50:54 +08:00
//Perform Merging
2017-01-07 12:18:32 +08:00
PartitionProcessor . MergePartitions ( modelConfig , LogMessage , mergeTable , mergeTargetGranuarity , mergePartitionKey ) ;
2017-01-05 12:50:54 +08:00
break ;
2017-01-09 12:02:38 +08:00
case ExecutionMode . DefragPartitionedTables :
2017-01-05 12:50:54 +08:00
//Perform Defrag
PartitionProcessor . DefragPartitionedTables ( modelConfig , LogMessage ) ;
break ;
default :
break ;
}
2016-12-06 13:37:16 +08:00
}
2016-12-02 11:25:57 +08:00
}
}
catch ( Exception exc )
{
Console . ForegroundColor = ConsoleColor . Red ;
Console . WriteLine ( ) ;
2017-01-09 12:02:38 +08:00
Console . WriteLine ( exc . Message ) ;
2016-12-19 16:43:00 +08:00
Console . WriteLine ( ) ;
2017-01-07 12:18:32 +08:00
Console . ForegroundColor = ConsoleColor . White ;
if ( exc is ArgumentException )
{
return 160 ; //ERROR_BAD_ARGUMENTS
}
else
{
return 1360 ; //ERROR_GENERIC_NOT_MAPPED
}
2016-12-02 11:25:57 +08:00
}
2017-01-09 12:02:38 +08:00
finally
2017-01-05 12:50:54 +08:00
{
2017-01-09 12:02:38 +08:00
if ( Debugger . IsAttached )
{
Console . ForegroundColor = ConsoleColor . White ;
Console . WriteLine ( "Press any key to exit." ) ;
Console . ReadKey ( ) ;
}
2017-01-05 12:50:54 +08:00
}
2017-01-07 12:18:32 +08:00
return 0 ; //ERROR_SUCCESS
2016-12-02 11:25:57 +08:00
}
2017-01-09 12:02:38 +08:00
private static void ParseArgs ( string [ ] args , ref string mergeTable , ref Granularity mergeTargetGranuarity , ref string mergePartitionKey , out bool help )
{
help = false ;
if ( args . Length > 0 )
{
ArgumentOptions options = new ArgumentOptions ( ) ;
if ( CommandLine . Parser . Default . ParseArguments ( args , options ) )
{
Console . WriteLine ( $"Argument ExecutionMode: {options.ExecutionMode}" ) ;
switch ( options . ExecutionMode )
{
case "InitializeInline" :
_executionMode = ExecutionMode . InitializeInline ;
break ;
case "InitializeFromDatabase" :
_executionMode = ExecutionMode . InitializeFromDatabase ;
break ;
case "MergePartitions" :
_executionMode = ExecutionMode . MergePartitions ;
if ( options . MergeTable = = null | | options . TargetGranularity = = null | | options . MergePartitionKey = = null )
{
throw new ArgumentException ( $"ExecutionMode MergePartitions additional arguments not provided or not recognized. Requires --MergeTable, --TargetGranularity, --MergePartitionKey." ) ;
}
Console . WriteLine ( $"Argument MergeTable: {options.MergeTable}" ) ;
Console . WriteLine ( $"Argument TargetGranularity: {options.TargetGranularity}" ) ;
Console . WriteLine ( $"Argument MergePartitionKey: {options.MergePartitionKey}" ) ;
mergeTable = options . MergeTable ;
mergeTargetGranuarity = options . TargetGranularity = = "Yearly" ? Granularity . Yearly : Granularity . Monthly ;
mergePartitionKey = options . MergePartitionKey ;
break ;
case "DefragPartitionedTables" :
_executionMode = ExecutionMode . DefragPartitionedTables ;
break ;
default :
throw new ArgumentException ( $"Argument --ExecutionMode {options.ExecutionMode} not recognized." ) ;
//break;
}
}
else
{
if ( args [ 0 ] . ToLower ( ) ! = "--help" )
{
throw new ArgumentException ( $"Arguments provided not recognized." ) ;
}
help = true ;
}
}
}
2017-01-05 12:50:54 +08:00
private static ModelConfiguration InitializeInline ( )
2016-12-02 11:25:57 +08:00
{
ModelConfiguration partitionedModel = new ModelConfiguration (
modelConfigurationID : 1 ,
analysisServicesServer : "localhost" ,
analysisServicesDatabase : "AdventureWorks" ,
initialSetUp : true ,
incrementalOnline : true ,
integratedAuth : true ,
userName : "" ,
password : "" ,
2016-12-29 08:58:53 +08:00
maxParallelism : - 1 ,
2016-12-20 16:34:25 +08:00
commitTimeout : - 1 ,
2016-12-02 11:25:57 +08:00
tableConfigurations :
new List < TableConfiguration >
{
new TableConfiguration (
tableConfigurationID : 1 ,
analysisServicesTable : "Internet Sales" ,
partitioningConfigurations :
new List < PartitioningConfiguration >
{
new PartitioningConfiguration (
partitioningConfigurationID : 1 ,
granularity : Granularity . Monthly ,
numberOfPartitionsFull : 12 ,
numberOfPartitionsForIncrementalProcess : 3 ,
maxDate : Convert . ToDateTime ( "2012-12-01" ) ,
sourceTableName : "[dbo].[FactInternetSales]" ,
sourcePartitionColumn : "OrderDateKey"
)
}
) ,
new TableConfiguration (
tableConfigurationID : 2 ,
analysisServicesTable : "Reseller Sales" ,
partitioningConfigurations :
new List < PartitioningConfiguration >
{
new PartitioningConfiguration (
partitioningConfigurationID : 2 ,
granularity : Granularity . Yearly ,
numberOfPartitionsFull : 3 ,
numberOfPartitionsForIncrementalProcess : 1 ,
maxDate : Convert . ToDateTime ( "2012-12-01" ) ,
sourceTableName : "[dbo].[FactResellerSales]" ,
sourcePartitionColumn : "OrderDateKey"
)
}
)
}
) ;
#region Not needed as sample includes pre - prepared version of AdventureWorks
////This section not to be used normally - just to get started with AdventureWorks. It removes existing partitions that come in AdventureWorks and creates a template one to align with assumptions listed at top of PartitionProcessor.cs file.
//if (partitionedModel.InitialSetUp)
//{
// Console.WriteLine("Initialize AdventureWorks template partitions? [y/n]");
// if (Console.ReadLine().ToLower() == "y")
// InitializeAdventureWorksDatabase(partitionedModel);
//}
#endregion
2017-01-05 12:50:54 +08:00
return partitionedModel ;
2016-12-02 11:25:57 +08:00
}
private static List < ModelConfiguration > InitializeFromDatabase ( )
{
ConfigDatabaseConnectionInfo connectionInfo = new ConfigDatabaseConnectionInfo ( ) ;
connectionInfo . Server = Settings . Default . ConfigServer ;
connectionInfo . Database = Settings . Default . ConfigDatabase ;
connectionInfo . IntegratedAuth = Settings . Default . ConfigDatabaseIntegratedAuth ;
if ( ! Settings . Default . ConfigDatabaseIntegratedAuth )
{
Console . Write ( "User name for config database: " ) ;
connectionInfo . UserName = Console . ReadLine ( ) ;
Console . Write ( "Password for config database: " ) ;
connectionInfo . Password = ReadPassword ( ) ;
}
return ConfigDatabaseHelper . ReadConfig ( connectionInfo ) ;
}
private static void LogMessage ( string message , ModelConfiguration partitionedModel )
{
//Can provide custom logger here
2016-12-06 13:37:16 +08:00
2016-12-02 11:25:57 +08:00
try
{
2017-01-09 12:02:38 +08:00
if ( ! ( _executionMode = = ExecutionMode . InitializeInline ) )
2016-12-06 13:37:16 +08:00
{
2016-12-02 11:25:57 +08:00
ConfigDatabaseHelper . LogMessage ( message , partitionedModel ) ;
2016-12-06 13:37:16 +08:00
}
2016-12-02 11:25:57 +08:00
Console . WriteLine ( message ) ;
}
catch ( Exception exc )
{
2016-12-19 16:43:00 +08:00
Console . ForegroundColor = ConsoleColor . Red ;
2016-12-02 11:25:57 +08:00
Console . WriteLine ( exc . Message ) ;
2016-12-19 16:43:00 +08:00
Console . WriteLine ( ) ;
Console . ForegroundColor = ConsoleColor . White ;
2016-12-02 11:25:57 +08:00
Console . WriteLine ( "Press any key to exit." ) ;
Console . ReadKey ( ) ;
2017-01-07 12:18:32 +08:00
Console . ForegroundColor = ConsoleColor . White ;
2016-12-02 11:25:57 +08:00
Environment . Exit ( 0 ) ; //Avoid recursion if errored connecting to db
}
}
2017-01-05 12:50:54 +08:00
private static void SetCredentials ( ModelConfiguration modelConfig )
{
if ( ! modelConfig . IntegratedAuth ) //For Azure AS
{
Console . WriteLine ( ) ;
Console . Write ( "User name for AS server: " ) ;
modelConfig . UserName = Console . ReadLine ( ) ;
Console . Write ( "Password for AS server: " ) ;
modelConfig . Password = ReadPassword ( ) ;
}
}
private static string ReadPassword ( )
2016-12-02 11:25:57 +08:00
{
string password = "" ;
ConsoleKeyInfo info = Console . ReadKey ( true ) ;
while ( info . Key ! = ConsoleKey . Enter )
{
if ( info . Key ! = ConsoleKey . Backspace )
{
Console . Write ( "*" ) ;
password + = info . KeyChar ;
}
else if ( info . Key = = ConsoleKey . Backspace )
{
if ( ! string . IsNullOrEmpty ( password ) )
{
// remove one character from the list of password characters
password = password . Substring ( 0 , password . Length - 1 ) ;
// get the location of the cursor
int pos = Console . CursorLeft ;
// move the cursor to the left by one character
Console . SetCursorPosition ( pos - 1 , Console . CursorTop ) ;
// replace it with space
Console . Write ( " " ) ;
// move the cursor to the left by one character again
Console . SetCursorPosition ( pos - 1 , Console . CursorTop ) ;
}
}
info = Console . ReadKey ( true ) ;
}
// add a new line because user pressed enter at the end of password
Console . WriteLine ( ) ;
return password ;
}
#region Not needed as sample includes pre - prepared version of AdventureWorks
private static void InitializeAdventureWorksDatabase ( ModelConfiguration parameters )
{
//In order to align with assumptions listed in PartitionProcessor.cs, need to:
//1. Delete existing partitions in InternetSales and ResellerSales
//2. Create template partition (again, see comments at top of PartitionProcessor.cs)
Console . WriteLine ( "Initializing AdventureWorks ..." ) ;
using ( Server server = new Server ( ) )
{
//Connect and get main objects
string serverConnectionString ;
if ( parameters . IntegratedAuth )
serverConnectionString = $"Provider=MSOLAP;Data Source={parameters.AnalysisServicesServer};" ;
else
{
serverConnectionString = $"Provider=MSOLAP;Data Source={parameters.AnalysisServicesServer};User ID={parameters.UserName};Password={parameters.Password};Persist Security Info=True;Impersonation Level=Impersonate;" ;
}
server . Connect ( serverConnectionString ) ;
Database database = server . Databases . FindByName ( parameters . AnalysisServicesDatabase ) ;
if ( database = = null )
{
throw new Microsoft . AnalysisServices . ConnectionException ( $"Could not connect to database {parameters.AnalysisServicesDatabase}." ) ;
}
InitializeAdventureWorksTable ( database , "Internet Sales" , "[dbo].[FactInternetSales]" ) ;
InitializeAdventureWorksTable ( database , "Reseller Sales" , "[dbo].[FactResellerSales]" ) ;
database . Update ( Microsoft . AnalysisServices . UpdateOptions . ExpandFull ) ;
server . Disconnect ( ) ;
}
}
private static void InitializeAdventureWorksTable ( Database database , string analysisServicesTableName , string sourceFactTableName )
{
Table table = database . Model . Tables . Find ( analysisServicesTableName ) ;
if ( table = = null )
{
throw new Microsoft . AnalysisServices . ConnectionException ( $"Could not connect to table {analysisServicesTableName}." ) ;
}
table . Partitions . Clear ( ) ;
Partition templatePartition = new Partition ( ) ;
templatePartition . Name = analysisServicesTableName ;
table . Partitions . Add ( templatePartition ) ;
templatePartition . Source = new QueryPartitionSource ( )
{
DataSource = database . Model . DataSources [ 0 ] , //Assuming this is only data source (SqlServer localhost)
Query = $"SELECT * FROM {sourceFactTableName}"
} ;
}
#endregion
}
}