This commit is contained in:
Christian Wade 2019-09-13 21:30:22 -07:00
parent 392e6a7fee
commit fd682bcd78
54 changed files with 3141 additions and 732 deletions

View File

@ -168,7 +168,7 @@ namespace BismNormalizer.CommandLine
throw new FileNotFoundException($"File not found {bsmnFile}");
}
Console.WriteLine($"About to deserialize {bsmnFile}");
ComparisonInfo comparisonInfo = ComparisonInfo.DeserializeBsmnFile(bsmnFile);
ComparisonInfo comparisonInfo = ComparisonInfo.DeserializeBsmnFile(bsmnFile, "BISM Normalizer Command Line");
Console.WriteLine();
if (comparisonInfo.ConnectionInfoSource.UseProject)
@ -198,19 +198,20 @@ namespace BismNormalizer.CommandLine
Console.WriteLine("--Comparing ...");
if (credsProvided)
{
comparisonInfo.CredsProvided = true;
comparisonInfo.SourceUsername = sourceUsername;
comparisonInfo.SourcePassword = sourcePassword;
comparisonInfo.TargetUsername = targetUsername;
comparisonInfo.TargetPassword = targetPassword;
if (!String.IsNullOrEmpty(workspaceServer))
{
_comparison = ComparisonFactory.CreateComparison(comparisonInfo, sourceUsername, sourcePassword, targetUsername, targetPassword, workspaceServer);
}
else
{
_comparison = ComparisonFactory.CreateComparison(comparisonInfo, sourceUsername, sourcePassword, targetUsername, targetPassword);
comparisonInfo.WorkspaceServerProvided = true;
comparisonInfo.WorkspaceServer = workspaceServer;
}
}
else
{
_comparison = ComparisonFactory.CreateComparison(comparisonInfo);
}
_comparison.ValidationMessage += HandleValidationMessage;
_comparison.Connect();
_comparison.CompareTabularModels();

View File

@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("4.0.1.10")]
[assembly: AssemblyFileVersion("4.0.1.10")]
[assembly: AssemblyVersion("4.0.1.12")]
[assembly: AssemblyFileVersion("4.0.1.12")]

View File

@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("4.0.1.10")]
[assembly: AssemblyFileVersion("4.0.1.10")]
[assembly: AssemblyVersion("4.0.1.12")]
[assembly: AssemblyFileVersion("4.0.1.12")]

View File

@ -40,20 +40,20 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.AnalysisServices, Version=16.3.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AnalysisServices.retail.amd64.16.3.0\lib\net45\Microsoft.AnalysisServices.dll</HintPath>
<Reference Include="Microsoft.AnalysisServices, Version=18.0.5.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AnalysisServices.retail.amd64.18.0.5\lib\net45\Microsoft.AnalysisServices.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AnalysisServices.Core, Version=16.3.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AnalysisServices.retail.amd64.16.3.0\lib\net45\Microsoft.AnalysisServices.Core.dll</HintPath>
<Reference Include="Microsoft.AnalysisServices.Core, Version=18.0.5.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AnalysisServices.retail.amd64.18.0.5\lib\net45\Microsoft.AnalysisServices.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AnalysisServices.SPClient.Interfaces, Version=16.3.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AnalysisServices.retail.amd64.16.3.0\lib\net45\Microsoft.AnalysisServices.SPClient.Interfaces.dll</HintPath>
<Reference Include="Microsoft.AnalysisServices.SPClient.Interfaces, Version=18.0.5.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AnalysisServices.retail.amd64.18.0.5\lib\net45\Microsoft.AnalysisServices.SPClient.Interfaces.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AnalysisServices.Tabular, Version=16.3.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AnalysisServices.retail.amd64.16.3.0\lib\net45\Microsoft.AnalysisServices.Tabular.dll</HintPath>
<Reference Include="Microsoft.AnalysisServices.Tabular, Version=18.0.5.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AnalysisServices.retail.amd64.18.0.5\lib\net45\Microsoft.AnalysisServices.Tabular.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AnalysisServices.Tabular.Json, Version=16.3.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AnalysisServices.retail.amd64.16.3.0\lib\net45\Microsoft.AnalysisServices.Tabular.Json.dll</HintPath>
<Reference Include="Microsoft.AnalysisServices.Tabular.Json, Version=18.0.5.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AnalysisServices.retail.amd64.18.0.5\lib\net45\Microsoft.AnalysisServices.Tabular.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
</ItemGroup>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.AnalysisServices.retail.amd64" version="16.3.0" targetFramework="net461" />
<package id="Microsoft.AnalysisServices.retail.amd64" version="18.0.5" targetFramework="net472" />
</packages>

View File

@ -0,0 +1,107 @@
<?xml version="1.0" encoding="utf-8"?>
<ApplicationInsights xmlns="http://schemas.microsoft.com/ApplicationInsights/2013/Settings">
<TelemetryInitializers>
<Add Type="Microsoft.ApplicationInsights.DependencyCollector.HttpDependenciesParsingTelemetryInitializer, Microsoft.AI.DependencyCollector"/>
<Add Type="Microsoft.ApplicationInsights.WindowsServer.AzureRoleEnvironmentTelemetryInitializer, Microsoft.AI.WindowsServer"/>
<Add Type="Microsoft.ApplicationInsights.WindowsServer.AzureWebAppRoleEnvironmentTelemetryInitializer, Microsoft.AI.WindowsServer"/>
<Add Type="Microsoft.ApplicationInsights.WindowsServer.BuildInfoConfigComponentVersionTelemetryInitializer, Microsoft.AI.WindowsServer"/>
</TelemetryInitializers>
<TelemetryModules>
<Add Type="Microsoft.ApplicationInsights.DependencyCollector.DependencyTrackingTelemetryModule, Microsoft.AI.DependencyCollector">
<ExcludeComponentCorrelationHttpHeadersOnDomains>
<!--
Requests to the following hostnames will not be modified by adding correlation headers.
Add entries here to exclude additional hostnames.
NOTE: this configuration will be lost upon NuGet upgrade.
-->
<Add>core.windows.net</Add>
<Add>core.chinacloudapi.cn</Add>
<Add>core.cloudapi.de</Add>
<Add>core.usgovcloudapi.net</Add>
</ExcludeComponentCorrelationHttpHeadersOnDomains>
<IncludeDiagnosticSourceActivities>
<Add>Microsoft.Azure.EventHubs</Add>
<Add>Microsoft.Azure.ServiceBus</Add>
</IncludeDiagnosticSourceActivities>
</Add>
<Add Type="Microsoft.ApplicationInsights.Extensibility.PerfCounterCollector.PerformanceCollectorModule, Microsoft.AI.PerfCounterCollector">
<!--
Use the following syntax here to collect additional performance counters:
<Counters>
<Add PerformanceCounter="\Process(??APP_WIN32_PROC??)\Handle Count" ReportAs="Process handle count" />
...
</Counters>
PerformanceCounter must be either \CategoryName(InstanceName)\CounterName or \CategoryName\CounterName
NOTE: performance counters configuration will be lost upon NuGet upgrade.
The following placeholders are supported as InstanceName:
??APP_WIN32_PROC?? - instance name of the application process for Win32 counters.
??APP_W3SVC_PROC?? - instance name of the application IIS worker process for IIS/ASP.NET counters.
??APP_CLR_PROC?? - instance name of the application CLR process for .NET counters.
-->
</Add>
<Add Type="Microsoft.ApplicationInsights.Extensibility.PerfCounterCollector.QuickPulse.QuickPulseTelemetryModule, Microsoft.AI.PerfCounterCollector"/>
<Add Type="Microsoft.ApplicationInsights.WindowsServer.AppServicesHeartbeatTelemetryModule, Microsoft.AI.WindowsServer"/>
<Add Type="Microsoft.ApplicationInsights.WindowsServer.AzureInstanceMetadataTelemetryModule, Microsoft.AI.WindowsServer">
<!--
Remove individual fields collected here by adding them to the ApplicationInsighs.HeartbeatProvider
with the following syntax:
<Add Type="Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing.DiagnosticsTelemetryModule, Microsoft.ApplicationInsights">
<ExcludedHeartbeatProperties>
<Add>osType</Add>
<Add>location</Add>
<Add>name</Add>
<Add>offer</Add>
<Add>platformFaultDomain</Add>
<Add>platformUpdateDomain</Add>
<Add>publisher</Add>
<Add>sku</Add>
<Add>version</Add>
<Add>vmId</Add>
<Add>vmSize</Add>
<Add>subscriptionId</Add>
<Add>resourceGroupName</Add>
<Add>placementGroupId</Add>
<Add>tags</Add>
<Add>vmScaleSetName</Add>
</ExcludedHeartbeatProperties>
</Add>
NOTE: exclusions will be lost upon upgrade.
-->
</Add>
<Add Type="Microsoft.ApplicationInsights.WindowsServer.DeveloperModeWithDebuggerAttachedTelemetryModule, Microsoft.AI.WindowsServer"/>
<Add Type="Microsoft.ApplicationInsights.WindowsServer.UnhandledExceptionTelemetryModule, Microsoft.AI.WindowsServer"/>
<Add Type="Microsoft.ApplicationInsights.WindowsServer.UnobservedExceptionTelemetryModule, Microsoft.AI.WindowsServer">
<!--</Add>
<Add Type="Microsoft.ApplicationInsights.WindowsServer.FirstChanceExceptionStatisticsTelemetryModule, Microsoft.AI.WindowsServer">-->
</Add>
</TelemetryModules>
<ApplicationIdProvider Type="Microsoft.ApplicationInsights.Extensibility.Implementation.ApplicationId.ApplicationInsightsApplicationIdProvider, Microsoft.ApplicationInsights"/>
<TelemetrySinks>
<Add Name="default">
<TelemetryProcessors>
<Add Type="Microsoft.ApplicationInsights.Extensibility.PerfCounterCollector.QuickPulse.QuickPulseTelemetryProcessor, Microsoft.AI.PerfCounterCollector"/>
<Add Type="Microsoft.ApplicationInsights.Extensibility.AutocollectedMetricsExtractor, Microsoft.ApplicationInsights"/>
<Add Type="Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel.AdaptiveSamplingTelemetryProcessor, Microsoft.AI.ServerTelemetryChannel">
<MaxTelemetryItemsPerSecond>5</MaxTelemetryItemsPerSecond>
<ExcludedTypes>Event</ExcludedTypes>
</Add>
<Add Type="Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel.AdaptiveSamplingTelemetryProcessor, Microsoft.AI.ServerTelemetryChannel">
<MaxTelemetryItemsPerSecond>5</MaxTelemetryItemsPerSecond>
<IncludedTypes>Event</IncludedTypes>
</Add>
</TelemetryProcessors>
<TelemetryChannel Type="Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel.ServerTelemetryChannel, Microsoft.AI.ServerTelemetryChannel"/>
</Add>
</TelemetrySinks>
<!--
Learn more about Application Insights configuration with ApplicationInsights.config here:
http://go.microsoft.com/fwlink/?LinkID=513840
Note: If not present, please add <InstrumentationKey>Your Key</InstrumentationKey> to the top of this file.
--></ApplicationInsights>

View File

@ -99,20 +99,20 @@
<Reference Include="Microsoft.AI.WindowsServer, Version=2.8.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ApplicationInsights.WindowsServer.2.8.1\lib\net45\Microsoft.AI.WindowsServer.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AnalysisServices, Version=16.3.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AnalysisServices.retail.amd64.16.3.0\lib\net45\Microsoft.AnalysisServices.dll</HintPath>
<Reference Include="Microsoft.AnalysisServices, Version=18.0.5.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AnalysisServices.retail.amd64.18.0.5\lib\net45\Microsoft.AnalysisServices.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AnalysisServices.Core, Version=16.3.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AnalysisServices.retail.amd64.16.3.0\lib\net45\Microsoft.AnalysisServices.Core.dll</HintPath>
<Reference Include="Microsoft.AnalysisServices.Core, Version=18.0.5.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AnalysisServices.retail.amd64.18.0.5\lib\net45\Microsoft.AnalysisServices.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AnalysisServices.SPClient.Interfaces, Version=16.3.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AnalysisServices.retail.amd64.16.3.0\lib\net45\Microsoft.AnalysisServices.SPClient.Interfaces.dll</HintPath>
<Reference Include="Microsoft.AnalysisServices.SPClient.Interfaces, Version=18.0.5.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AnalysisServices.retail.amd64.18.0.5\lib\net45\Microsoft.AnalysisServices.SPClient.Interfaces.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AnalysisServices.Tabular, Version=16.3.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AnalysisServices.retail.amd64.16.3.0\lib\net45\Microsoft.AnalysisServices.Tabular.dll</HintPath>
<Reference Include="Microsoft.AnalysisServices.Tabular, Version=18.0.5.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AnalysisServices.retail.amd64.18.0.5\lib\net45\Microsoft.AnalysisServices.Tabular.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AnalysisServices.Tabular.Json, Version=16.3.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AnalysisServices.retail.amd64.16.3.0\lib\net45\Microsoft.AnalysisServices.Tabular.Json.dll</HintPath>
<Reference Include="Microsoft.AnalysisServices.Tabular.Json, Version=18.0.5.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AnalysisServices.retail.amd64.18.0.5\lib\net45\Microsoft.AnalysisServices.Tabular.Json.dll</HintPath>
</Reference>
<Reference Include="Microsoft.ApplicationInsights, Version=2.8.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.ApplicationInsights.2.8.1\lib\net46\Microsoft.ApplicationInsights.dll</HintPath>
@ -208,7 +208,9 @@
</Reference>
<Reference Include="System.DirectoryServices.AccountManagement" />
<Reference Include="System.Drawing" />
<Reference Include="System.Management" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Runtime.Caching" />
<Reference Include="System.Web" />
<Reference Include="System.Web.Extensions" />
<Reference Include="System.Windows.Forms" />
@ -226,11 +228,28 @@
<Compile Include="TabularCompare\Enums.cs" />
<Compile Include="TabularCompare\BlobKeyEventArgs.cs" />
<Compile Include="TabularCompare\PasswordPromptEventArgs.cs" />
<Compile Include="TabularCompare\TabularMetadata\CalculationItem.cs" />
<Compile Include="TabularCompare\TabularMetadata\CalculationItemCollection.cs" />
<Compile Include="TabularCompare\TabularMetadata\RefreshPolicy.cs" />
<Compile Include="TabularCompare\TabularMetadata\RefreshPolicyCollection.cs" />
<Compile Include="TabularCompare\TabularMetadata\Model.cs" />
<Compile Include="TabularCompare\TabularMetadata\Expression.cs" />
<Compile Include="TabularCompare\TabularMetadata\ExpressionCollection.cs" />
<Compile Include="TabularCompare\TabularMetadata\CalcDependency.cs" />
<Compile Include="TabularCompare\TabularMetadata\CalcDependencyCollection.cs" />
<Compile Include="TabularCompare\Telemetry.cs" />
<Compile Include="TabularCompare\UI\ComparisonControl.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="TabularCompare\UI\ComparisonControl.Designer.cs">
<DependentUpon>ComparisonControl.cs</DependentUpon>
</Compile>
<Compile Include="TabularCompare\UI\Connections.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="TabularCompare\UI\Connections.Designer.cs">
<DependentUpon>Connections.cs</DependentUpon>
</Compile>
<Compile Include="TabularCompare\UI\HighDpiUtils.cs" />
<Compile Include="TabularCompare\UI\BlobCredentials.cs">
<SubType>Form</SubType>
@ -260,12 +279,6 @@
<Compile Include="TabularCompare\MultidimensionalMetadata\Table.cs" />
<Compile Include="TabularCompare\MultidimensionalMetadata\TableCollection.cs" />
<Compile Include="TabularCompare\MultidimensionalMetadata\ITabularObject.cs" />
<Compile Include="TabularCompare\UI\ComparisonControl.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="TabularCompare\UI\ComparisonControl.Designer.cs">
<DependentUpon>ComparisonControl.cs</DependentUpon>
</Compile>
<Compile Include="TabularCompare\Core\Comparison.cs" />
<Compile Include="TabularCompare\Core\ComparisonObject.cs" />
<Compile Include="TabularCompare\PartitionRowCounter.cs" />
@ -330,11 +343,11 @@
<Compile Include="TabularCompare\UI\TreeGridView.cs" />
<Compile Include="TabularCompare\UI\TreeGridViewComparison.cs" />
<Compile Include="TabularCompare\UI\TreeGridViewValidationOutput.cs" />
<Compile Include="TabularCompare\UI\Connections.cs">
<Compile Include="TabularCompare\UI\ConnectionsAlmt.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="TabularCompare\UI\Connections.Designer.cs">
<DependentUpon>Connections.cs</DependentUpon>
<Compile Include="TabularCompare\UI\ConnectionsAlmt.Designer.cs">
<DependentUpon>ConnectionsAlmt.cs</DependentUpon>
</Compile>
<Compile Include="WarningList.cs" />
<Compile Include="EditorFactory.cs" />
@ -418,6 +431,8 @@
<Resource Include="Resources\Error.png" />
<Resource Include="Resources\Culture.png" />
<Resource Include="Resources\Expression.png" />
<Resource Include="Resources\CalculationGroup.png" />
<Resource Include="Resources\CalculationItem.png" />
<Content Include="Resources\LicenseTerms.txt">
<IncludeInVSIX>true</IncludeInVSIX>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
@ -434,9 +449,7 @@
<Resource Include="Resources\Warning.png" />
<Resource Include="Resources\WarningToolWindow.png" />
<Resource Include="Resources\SkipActionGrey.png" />
<None Include="ApplicationInsights.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="ApplicationInsights.config" />
<None Include="TabularCompare\TabularCompare.dgml">
<SubType>Designer</SubType>
</None>
@ -448,6 +461,8 @@
<Resource Include="Resources\ProgressError.png" />
<Resource Include="Resources\ProgressWarning.png" />
<Resource Include="Resources\Processing.png" />
<Resource Include="Resources\Model.png" />
<Resource Include="Resources\RefreshPolicy.png" />
<Content Include="Templates\TabularCompare.bsmn">
<IncludeInVSIX>true</IncludeInVSIX>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
@ -470,11 +485,13 @@
<ItemGroup>
<EmbeddedResource Include="TabularCompare\UI\ComparisonControl.resx">
<DependentUpon>ComparisonControl.cs</DependentUpon>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="TabularCompare\UI\Connections.resx">
<DependentUpon>Connections.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="TabularCompare\UI\ConnectionsAlmt.resx">
<DependentUpon>ConnectionsAlmt.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="TabularCompare\UI\Deployment.resx">
<DependentUpon>Deployment.cs</DependentUpon>
</EmbeddedResource>

Binary file not shown.

View File

@ -114,7 +114,7 @@
<Target Name="AfterBuild" Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<!--Run tests (BismNormalizer.Tests.dll)-->
<ItemGroup>
<!--<ItemGroup>
<TrxFiles Include="TestResults\*.trx" />
</ItemGroup>
<Message Text=" " Importance="high" />
@ -123,7 +123,7 @@
<Delete Files="@(TrxFiles)" />
<Message Text="- About to run vstest on: @(OutputDlls)" Importance="high" />
<Message Text=" " Importance="high" />
<Exec Command="vstest.console.exe @(OutputDlls) /Logger:trx" />
<Exec Command="vstest.console.exe @(OutputDlls) /Logger:trx" />-->
<PropertyGroup>
<ObfuscDir>$(MSBuildProjectDirectory)\bin\ReleaseObfusc</ObfuscDir>

View File

@ -12,7 +12,7 @@ namespace BismNormalizer
{
public static void Main()
{
using (Comparison c = ComparisonFactory.CreateComparison("C:\\TabularCompare1.bsmn"))
using (Comparison c = ComparisonFactory.CreateComparison("C:\\TabularCompare1.bsmn", ""))
{
c.Connect();
c.CompareTabularModels();

View File

@ -29,5 +29,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("4.0.1.10")]
[assembly: AssemblyFileVersion("4.0.1.10")]
[assembly: AssemblyVersion("4.0.1.12")]
[assembly: AssemblyFileVersion("4.0.1.12")]

View File

@ -140,7 +140,7 @@ namespace BismNormalizer {
}
/// <summary>
/// Looks up a localized string similar to BISM Normalizer Warning List.
/// Looks up a localized string similar to Warning List.
/// </summary>
internal static string ToolWindowTitle {
get {

View File

@ -125,7 +125,7 @@
<value>Resources\Progress.gif;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="ToolWindowTitle" xml:space="preserve">
<value>BISM Normalizer Warning List</value>
<value>Warning List</value>
</data>
<data name="LogoSmall" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\LogoSmall.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 B

View File

@ -12,10 +12,10 @@ namespace BismNormalizer {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.1.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.2.0.0")]
public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
@ -286,5 +286,41 @@ namespace BismNormalizer {
this["OptionHighDpiLocal"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool OptionCompositeModelsOverride {
get {
return ((bool)(this["OptionCompositeModelsOverride"]));
}
set {
this["OptionCompositeModelsOverride"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool OptionRetainPolicyPartitions {
get {
return ((bool)(this["OptionRetainPolicyPartitions"]));
}
set {
this["OptionRetainPolicyPartitions"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool OptionRetainStorageMode {
get {
return ((bool)(this["OptionRetainStorageMode"]));
}
set {
this["OptionRetainStorageMode"] = value;
}
}
}
}

View File

@ -5,7 +5,7 @@
// The PropertyChanged event is raised after a setting's value is changed.
// The SettingsLoaded event is raised after the setting values are loaded.
// The SettingsSaving event is raised before the setting values are saved.
internal sealed partial class Settings {
public sealed partial class Settings {
public Settings() {
// // To add event handlers for saving and changing settings, uncomment the lines below:

View File

@ -68,5 +68,14 @@
<Setting Name="OptionHighDpiLocal" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="OptionCompositeModelsOverride" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="OptionRetainPolicyPartitions" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="OptionRetainStorageMode" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
</Settings>
</SettingsFile>

View File

@ -14,7 +14,9 @@ namespace BismNormalizer.TabularCompare
{
// Factory pattern: https://msdn.microsoft.com/en-us/library/orm-9780596527730-01-05.aspx
private static List<int> _supportedCompatibilityLevels = new List<int>() { 1100, 1103, 1200, 1400 };
private static int _minCompatibilityLevel = 1100;
private static int _maxCompatibilityLevel = 1500;
private static List<string> _supportedDataSourceVersions = new List<string> { "PowerBI_V3" };
/// <summary>
/// Uses factory design pattern to return an object of type Core.Comparison, which is instantiated using MultidimensionalMetadata.Comparison or TabularMeatadata.Comparison depending on SSAS compatibility level. Use this overload when running in Visual Studio.
@ -42,9 +44,9 @@ namespace BismNormalizer.TabularCompare
/// </summary>
/// <param name="bsmnFile">Full path to the BSMN file.</param>
/// <returns>Core.Comparison object</returns>
public static Comparison CreateComparison(string bsmnFile)
public static Comparison CreateComparison(string bsmnFile, string appName)
{
ComparisonInfo comparisonInfo = ComparisonInfo.DeserializeBsmnFile(bsmnFile);
ComparisonInfo comparisonInfo = ComparisonInfo.DeserializeBsmnFile(bsmnFile, appName);
return CreateComparison(comparisonInfo);
}
@ -59,57 +61,136 @@ namespace BismNormalizer.TabularCompare
return CreateComparisonInitialized(comparisonInfo);
}
/// <summary>
/// Uses factory design pattern to return an object of type Core.Comparison, which is instantiated using MultidimensionalMetadata.Comparison or TabularMeatadata.Comparison depending on SSAS compatibility level.
/// </summary>
/// <param name="comparisonInfo">ComparisonInfo object for the comparison.</param>
/// <returns>Core.Comparison object</returns>
public static Comparison CreateComparison(ComparisonInfo comparisonInfo, string sourceUsername, string sourcePassword, string targetUsername, string targetPassword)
{
comparisonInfo.InitializeCompatibilityLevels(sourceUsername, sourcePassword, targetUsername, targetPassword);
return CreateComparisonInitialized(comparisonInfo);
}
/// <summary>
/// Uses factory design pattern to return an object of type Core.Comparison, which is instantiated using MultidimensionalMetadata.Comparison or TabularMeatadata.Comparison depending on SSAS compatibility level.
/// </summary>
/// <param name="comparisonInfo">ComparisonInfo object for the comparison.</param>
/// <returns>Core.Comparison object</returns>
public static Comparison CreateComparison(ComparisonInfo comparisonInfo, string sourceUsername, string sourcePassword, string targetUsername, string targetPassword, string workspaceServer)
{
comparisonInfo.InitializeCompatibilityLevels(sourceUsername, sourcePassword, targetUsername, targetPassword, workspaceServer);
return CreateComparisonInitialized(comparisonInfo);
}
private static Comparison CreateComparisonInitialized(ComparisonInfo comparisonInfo)
{
Telemetry.TrackEvent("CreateComparisonInitialized", new Dictionary<string, string> { { "App", "BismNormalizer" } });
Telemetry.TrackEvent("CreateComparisonInitialized", new Dictionary<string, string> { { "App", comparisonInfo.AppName.Replace(" ", "") } });
if (comparisonInfo.SourceCompatibilityLevel != comparisonInfo.TargetCompatibilityLevel && !(comparisonInfo.SourceCompatibilityLevel == 1200 && comparisonInfo.TargetCompatibilityLevel == 1400))
////Currently can't source from PBIP to AS because AS doesn't work with inline data sources:
//if (comparisonInfo.ConnectionInfoSource.ServerName.StartsWith("powerbi://") && !comparisonInfo.ConnectionInfoTarget.ServerName.StartsWith("powerbi://"))
//{
// throw new ConnectionException($"Source model is a Power BI dataset and the target is Analysis Services, which is not supported in the current version.");
//}
//If composite models not allowed on AS, check DQ/Import at model level matches:
if (!comparisonInfo.ConnectionInfoSource.ServerName.StartsWith("powerbi://") && !Settings.Default.OptionCompositeModelsOverride && comparisonInfo.SourceDirectQuery != comparisonInfo.TargetDirectQuery)
{
throw new ConnectionException($"Mixed DirectQuery settings are not supported for AS skus.\nSource is {(comparisonInfo.SourceDirectQuery ? "On" : "Off")} and target is {(comparisonInfo.TargetDirectQuery ? "On" : "Off")}.");
}
#region Data-source versions check
//If Power BI, check the default datasource version
//Source
bool sourceDataSourceVersionRequiresUpgrade = false;
if (comparisonInfo.ConnectionInfoSource.ServerName.StartsWith("powerbi://") && !_supportedDataSourceVersions.Contains(comparisonInfo.SourceDataSourceVersion))
{
sourceDataSourceVersionRequiresUpgrade = true;
}
//Target
bool targetDataSourceVersionRequiresUpgrade = false;
if (comparisonInfo.ConnectionInfoTarget.ServerName.StartsWith("powerbi://") && !_supportedDataSourceVersions.Contains(comparisonInfo.TargetDataSourceVersion))
{
targetDataSourceVersionRequiresUpgrade = true;
}
//Check if user willing to upgrade the data-source version(s)
if (sourceDataSourceVersionRequiresUpgrade && targetDataSourceVersionRequiresUpgrade)
{
string message = $"The source and target are Power BI datasets have default data-source versions of {comparisonInfo.SourceDataSourceVersion} and {comparisonInfo.TargetDataSourceVersion} respectively, which are not supported for comparison.";
if (comparisonInfo.Interactive && System.Windows.Forms.MessageBox.Show(
message += $"\nDo you want to upgrade them both to {_supportedDataSourceVersions[0]} and allow the comparison?\n\nNOTE: this is a irreversible operation and you may not be able to download the PBIX file(s) to Power BI Desktop. You should only do this if you have the original PBIX as a backup.", comparisonInfo.AppName, System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question) != System.Windows.Forms.DialogResult.Yes)
{
throw new ConnectionException(message);
}
}
else if (sourceDataSourceVersionRequiresUpgrade)
{
string message = $"The source is a Power BI dataset with default data-source version of {comparisonInfo.SourceDataSourceVersion}, which is not supported for comparison.";
if (comparisonInfo.Interactive && System.Windows.Forms.MessageBox.Show(
message += $"\nDo you want to upgrade it to {_supportedDataSourceVersions[0]} and allow the comparison?\n\nNOTE: this is a irreversible operation and you may not be able to download the PBIX file(s) to Power BI Desktop. You should only do this if you have the original PBIX as a backup.", comparisonInfo.AppName, System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question) != System.Windows.Forms.DialogResult.Yes)
{
throw new ConnectionException(message);
}
}
else if (targetDataSourceVersionRequiresUpgrade)
{
string message = $"The target is a Power BI datasets with default data-source version of {comparisonInfo.TargetDataSourceVersion}, which is not supported for comparison.";
if (comparisonInfo.Interactive && System.Windows.Forms.MessageBox.Show(
message += $"\nDo you want to upgrade it to {_supportedDataSourceVersions[0]} and allow the comparison?\n\nNOTE: this is a irreversible operation and you may not be able to download the PBIX file(s) to Power BI Desktop. You should only do this if you have the original PBIX as a backup.", comparisonInfo.AppName, System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question) != System.Windows.Forms.DialogResult.Yes)
{
throw new ConnectionException(message);
}
}
#endregion
//Check if one of the supported compat levels:
if (
!(comparisonInfo.SourceCompatibilityLevel >= _minCompatibilityLevel && comparisonInfo.SourceCompatibilityLevel <= _maxCompatibilityLevel &&
comparisonInfo.TargetCompatibilityLevel >= _minCompatibilityLevel && comparisonInfo.TargetCompatibilityLevel <= _maxCompatibilityLevel
)
)
{
throw new ConnectionException($"This combination of mixed compatibility levels is not supported.\nSource is {Convert.ToString(comparisonInfo.SourceCompatibilityLevel)} and target is {Convert.ToString(comparisonInfo.TargetCompatibilityLevel)}.");
}
if (comparisonInfo.SourceDirectQuery != comparisonInfo.TargetDirectQuery)
//Return the comparison object & offer upgrade of target if appropriate
Comparison returnComparison = null;
if (comparisonInfo.SourceCompatibilityLevel >= 1200 && comparisonInfo.TargetCompatibilityLevel >= 1200)
{
throw new ConnectionException($"Mixed DirectQuery settings are not supported.\nSource is {(comparisonInfo.SourceDirectQuery ? "On" : "Off")} and target is {(comparisonInfo.TargetDirectQuery ? "On" : "Off")}.");
returnComparison = new TabularMetadata.Comparison(comparisonInfo);
TabularMetadata.Comparison returnTabularComparison = (TabularMetadata.Comparison)returnComparison;
//Upgrade default DATA-SOURCE versions if required
if (sourceDataSourceVersionRequiresUpgrade)
{
returnTabularComparison.SourceTabularModel.Connect();
returnTabularComparison.SourceTabularModel.TomDatabase.Model.DefaultPowerBIDataSourceVersion = Microsoft.AnalysisServices.Tabular.PowerBIDataSourceVersion.PowerBI_V3;
returnTabularComparison.SourceTabularModel.TomDatabase.Update();
returnTabularComparison.Disconnect();
}
if (targetDataSourceVersionRequiresUpgrade)
{
returnTabularComparison.TargetTabularModel.Connect();
returnTabularComparison.TargetTabularModel.TomDatabase.Model.DefaultPowerBIDataSourceVersion = Microsoft.AnalysisServices.Tabular.PowerBIDataSourceVersion.PowerBI_V3;
returnTabularComparison.TargetTabularModel.TomDatabase.Update();
returnTabularComparison.Disconnect();
}
//We know both models have same compatibility level, but is it supported?
if (!_supportedCompatibilityLevels.Contains(comparisonInfo.SourceCompatibilityLevel))
//Check if source has a higher compat level than the target and offer upgrade if appropriate.
if (comparisonInfo.SourceCompatibilityLevel > comparisonInfo.TargetCompatibilityLevel)
{
throw new ConnectionException($"Models have compatibility level of {Convert.ToString(comparisonInfo.SourceCompatibilityLevel)}, which is not supported by this version of BISM Normalizer.\nPlease check http://bism-normalizer.com/purchase for other versions.");
}
string message = $"Source compatibility level is { Convert.ToString(comparisonInfo.SourceCompatibilityLevel) } and target is { Convert.ToString(comparisonInfo.TargetCompatibilityLevel) }, which is not supported for comparison.\n";
if (comparisonInfo.SourceCompatibilityLevel >= 1200)
if (comparisonInfo.Interactive &&
!comparisonInfo.ConnectionInfoTarget.UseProject && //Upgrade in SSDT not supported
System.Windows.Forms.MessageBox.Show(
message += $"\nDo you want to upgrade the target to {Convert.ToString(comparisonInfo.SourceCompatibilityLevel)} and allow the comparison?", comparisonInfo.AppName, System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes)
{
return new TabularMetadata.Comparison(comparisonInfo);
returnTabularComparison.TargetTabularModel.Connect();
returnTabularComparison.TargetTabularModel.TomDatabase.CompatibilityLevel = comparisonInfo.SourceCompatibilityLevel;
returnTabularComparison.TargetTabularModel.TomDatabase.Update();
returnTabularComparison.Disconnect();
}
else
{
return new MultidimensionalMetadata.Comparison(comparisonInfo);
throw new ConnectionException(message + "\nUpgrade the target compatibility level and retry.");
}
}
}
else
{
if (comparisonInfo.SourceCompatibilityLevel == comparisonInfo.TargetCompatibilityLevel)
{
returnComparison = new MultidimensionalMetadata.Comparison(comparisonInfo);
}
else
{
throw new ConnectionException($"This combination of mixed compatibility levels is not supported.\nSource is {Convert.ToString(comparisonInfo.SourceCompatibilityLevel)} and target is {Convert.ToString(comparisonInfo.TargetCompatibilityLevel)}.");
}
}
return returnComparison;
}
}
}

View File

@ -18,9 +18,20 @@ namespace BismNormalizer.TabularCompare
private SkipSelectionCollection _skipSelectionCollection;
private int _sourceCompatibilityLevel;
private int _targetCompatibilityLevel;
private string _sourceDataSourceVersion;
private string _targetDataSourceVersion;
private bool _sourceDirectQuery;
private bool _targetDirectQuery;
private bool _promptForDatabaseProcessing;
private bool _interactive = true;
private string _appName = "<AppName>";
private bool _credsProvided = false;
private string _sourceUsername;
private string _sourcePassword;
private string _targetUsername;
private string _targetPassword;
private bool _workspaceServerProvided = false;
private string _workspaceServer;
/// <summary>
/// Initializes a new instance of the ComparisonInfo class.
@ -73,17 +84,29 @@ namespace BismNormalizer.TabularCompare
}
/// <summary>
/// SSAS compatibility level for the source tabular model.
/// Compatibility level for the source tabular model.
/// </summary>
[XmlIgnore()]
public int SourceCompatibilityLevel => _sourceCompatibilityLevel;
/// <summary>
/// SSAS compatibility level for the target tabular model.
/// Compatibility level for the target tabular model.
/// </summary>
[XmlIgnore()]
public int TargetCompatibilityLevel => _targetCompatibilityLevel;
/// <summary>
/// Default data source version for the source tabular model.
/// </summary>
[XmlIgnore()]
public string SourceDataSourceVersion => _sourceDataSourceVersion;
/// <summary>
/// Default data source version for the target tabular model.
/// </summary>
[XmlIgnore()]
public string TargetDataSourceVersion => _targetDataSourceVersion;
/// <summary>
/// Flag depending on whehter source tabular model is in DirectQuery mode.
/// </summary>
@ -106,6 +129,96 @@ namespace BismNormalizer.TabularCompare
set { _promptForDatabaseProcessing = value; }
}
/// <summary>
/// Flag depending on whether running in interactive mode. Command line execution is not.
/// </summary>
[XmlIgnore()]
public bool Interactive
{
get { return _interactive; }
set { _interactive = value; }
}
/// <summary>
/// Name of the app. For example, BISM Normalizer or ALM Toolkit.
/// </summary>
[XmlIgnore()]
public string AppName
{
get { return _appName; }
set { _appName = value; }
}
/// <summary>
/// Flag depending on whether credentials provided to connect to AS/PBI. Used for command line mode/automated build.
/// </summary>
[XmlIgnore()]
public bool CredsProvided
{
get { return _credsProvided; }
set { _credsProvided = value; }
}
/// <summary>
/// Username for source model for when CredsProvided = true.
/// </summary>
[XmlIgnore()]
public string SourceUsername
{
get { return _sourceUsername; }
set { _sourceUsername = value; }
}
/// <summary>
/// Password for source model for when CredsProvided = true.
/// </summary>
[XmlIgnore()]
public string SourcePassword
{
get { return _sourcePassword; }
set { _sourcePassword = value; }
}
/// <summary>
/// Username for target model for when CredsProvided = true.
/// </summary>
[XmlIgnore()]
public string TargetUsername
{
get { return _targetUsername; }
set { _targetUsername = value; }
}
/// <summary>
/// Password for target model for when CredsProvided = true.
/// </summary>
[XmlIgnore()]
public string TargetPassword
{
get { return _targetPassword; }
set { _targetPassword = value; }
}
/// <summary>
/// Flag depending on whether workspace server was provided. Used for command line mode/automated build.
/// </summary>
[XmlIgnore()]
public bool WorkspaceServerProvided
{
get { return _workspaceServerProvided; }
set { _workspaceServerProvided = value; }
}
/// <summary>
/// Workspace server name for when WorkspaceServerProvided = true. Used for command line mode/automated build.
/// </summary>
[XmlIgnore()]
public string WorkspaceServer
{
get { return _workspaceServer; }
set { _workspaceServer = value; }
}
#endregion
/// <summary>
@ -113,7 +226,7 @@ namespace BismNormalizer.TabularCompare
/// </summary>
/// <param name="bsmnFile">BSMN file to be deserialized.</param>
/// <returns>Deserialized instance of ComparisonInfo.</returns>
public static ComparisonInfo DeserializeBsmnFile(string bsmnFile)
public static ComparisonInfo DeserializeBsmnFile(string bsmnFile, string appName)
{
if (!File.Exists(bsmnFile))
{
@ -121,54 +234,40 @@ namespace BismNormalizer.TabularCompare
}
XmlSerializer reader = new XmlSerializer(typeof(ComparisonInfo));
StreamReader file = new StreamReader(bsmnFile);
return (ComparisonInfo)reader.Deserialize(file);
ComparisonInfo returnComparisonInfo = (ComparisonInfo)reader.Deserialize(file);
returnComparisonInfo.AppName = appName;
return returnComparisonInfo;
}
/// <summary>
/// Finds models' compatibility levels (and preps databases on workspace servers for comparison). This overload to be used when client is not Visual Studio - e.g. command line.
/// </summary>
/// <param name="compatibilityLevelSource"></param>
/// <param name="compatibilityLevelTarget"></param>
public void InitializeCompatibilityLevels()
{
ConnectionInfoSource.InitializeCompatibilityLevel();
ConnectionInfoTarget.InitializeCompatibilityLevel();
PopulateDatabaseProperties();
}
/// <summary>
/// Finds models' compatibility levels (and preps databases on workspace servers for comparison). This overload to be used when client is not Visual Studio - e.g. command line.
/// </summary>
public void InitializeCompatibilityLevels(string sourceUsername, string sourcePassword, string targetUsername, string targetPassword)
if (_credsProvided)
{
ConnectionInfoSource.CredsProvided = true;
ConnectionInfoSource.Username = sourceUsername;
ConnectionInfoSource.Password = sourcePassword;
ConnectionInfoSource.InitializeCompatibilityLevel();
ConnectionInfoSource.Username = _sourceUsername;
ConnectionInfoSource.Password = _sourcePassword;
ConnectionInfoTarget.CredsProvided = true;
ConnectionInfoTarget.Username = targetUsername;
ConnectionInfoTarget.Password = targetPassword;
ConnectionInfoTarget.InitializeCompatibilityLevel();
ConnectionInfoTarget.Username = _targetUsername;
ConnectionInfoTarget.Password = _targetPassword;
PopulateDatabaseProperties();
if (_workspaceServerProvided)
{
ConnectionInfoSource.WorkspaceServerProvided = true;
ConnectionInfoSource.WorkspaceServer = _workspaceServer;
ConnectionInfoTarget.WorkspaceServerProvided = true;
ConnectionInfoTarget.WorkspaceServer = _workspaceServer;
}
}
/// <summary>
/// Finds models' compatibility levels (and preps databases on workspace servers for comparison). This overload to be used when client is not Visual Studio - e.g. command line.
/// </summary>
public void InitializeCompatibilityLevels(string sourceUsername, string sourcePassword, string targetUsername, string targetPassword, string workspaceServer)
{
ConnectionInfoSource.CredsProvided = true;
ConnectionInfoSource.Username = sourceUsername;
ConnectionInfoSource.Password = sourcePassword;
ConnectionInfoSource.InitializeCompatibilityLevel(workspaceServer: workspaceServer);
ConnectionInfoTarget.CredsProvided = true;
ConnectionInfoTarget.Username = targetUsername;
ConnectionInfoTarget.Password = targetPassword;
ConnectionInfoTarget.InitializeCompatibilityLevel(workspaceServer: workspaceServer);
ConnectionInfoSource.InitializeCompatibilityLevel();
ConnectionInfoTarget.InitializeCompatibilityLevel();
PopulateDatabaseProperties();
}
@ -202,6 +301,9 @@ namespace BismNormalizer.TabularCompare
_sourceCompatibilityLevel = ConnectionInfoSource.CompatibilityLevel;
_targetCompatibilityLevel = ConnectionInfoTarget.CompatibilityLevel;
_sourceDataSourceVersion = ConnectionInfoSource.DataSourceVersion;
_targetDataSourceVersion = ConnectionInfoTarget.DataSourceVersion;
_sourceDirectQuery = ConnectionInfoSource.DirectQuery;
_targetDirectQuery = ConnectionInfoTarget.DirectQuery;
}
@ -250,7 +352,7 @@ namespace BismNormalizer.TabularCompare
filesToClose += $"\n- {projectItemToClose.ContainingProject.Name.Replace(".smproj", "")}\\{projectItemToClose.Name}";
}
if (MessageBox.Show($"BISM Normalizer needs to close the following file(s) that are\nopen in Visual Studio. Do you want to continue?{filesToClose}", "BISM Normalizer", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
if (MessageBox.Show($"{_appName} needs to close the following file(s) that are\nopen in Visual Studio. Do you want to continue?{filesToClose}", _appName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
{
userCancelled = true;
}

View File

@ -24,6 +24,7 @@ namespace BismNormalizer.TabularCompare
private string _projectName;
private string _projectFile;
private int _compatibilityLevel;
private string _dataSourceVersion;
private bool _directQuery;
private string _bimFileFullName;
private EnvDTE.Project _project;
@ -34,11 +35,11 @@ namespace BismNormalizer.TabularCompare
private bool _credsProvided = false;
private string _username;
private string _password;
private bool _workspaceServerProvided = false;
private string _workspaceServer;
#endregion
#region Properties
/// <summary>
/// Initializes a new instance of the ConnectionInfo class.
/// </summary>
@ -92,11 +93,17 @@ namespace BismNormalizer.TabularCompare
}
/// <summary>
/// The SSAS compatibility level for the connection.
/// Compatibility level for the connection.
/// </summary>
[XmlIgnore()]
public int CompatibilityLevel => _compatibilityLevel;
/// <summary>
/// Default data source version for the connection.
/// </summary>
[XmlIgnore()]
public string DataSourceVersion => _dataSourceVersion;
/// <summary>
/// A Boolean specifying whether the tabular model for the connection is running in DirectQuery mode.
/// </summary>
@ -167,7 +174,25 @@ namespace BismNormalizer.TabularCompare
set { _password = value; }
}
#endregion
/// <summary>
/// Flag depending on whether workspace server was provided. Used for command line mode/automated build.
/// </summary>
[XmlIgnore()]
public bool WorkspaceServerProvided
{
get { return _workspaceServerProvided; }
set { _workspaceServerProvided = value; }
}
/// <summary>
/// Workspace server name for when WorkspaceServerProvided = true. Used for command line mode/automated build.
/// </summary>
[XmlIgnore()]
public string WorkspaceServer
{
get { return _workspaceServer; }
set { _workspaceServer = value; }
}
private void ReadSettingsFile()
{
@ -336,7 +361,7 @@ namespace BismNormalizer.TabularCompare
/// 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, string workspaceServer = null)
public void InitializeCompatibilityLevel(bool closedBimFile = false)
{
if (UseProject)
{
@ -360,9 +385,9 @@ namespace BismNormalizer.TabularCompare
ReadProjectFile();
//Overwrite the server if a workspace server provided
if (!String.IsNullOrEmpty(workspaceServer))
if (_workspaceServerProvided)
{
this.ServerName = workspaceServer;
this.ServerName = _workspaceServer;
}
}
@ -549,6 +574,7 @@ $@"{{
throw new ConnectionException($"Can not load/find database {this.DatabaseName}.");
}
_compatibilityLevel = tabularDatabase.CompatibilityLevel;
_dataSourceVersion = tabularDatabase.Model.DefaultPowerBIDataSourceVersion.ToString();
_directQuery = ((tabularDatabase.Model != null && tabularDatabase.Model.DefaultMode == Microsoft.AnalysisServices.Tabular.ModeType.DirectQuery) ||
tabularDatabase.DirectQueryMode == DirectQueryMode.DirectQuery || tabularDatabase.DirectQueryMode == DirectQueryMode.InMemoryWithDirectQuery || tabularDatabase.DirectQueryMode == DirectQueryMode.DirectQueryWithInMemory);
}

View File

@ -237,7 +237,7 @@ namespace BismNormalizer.TabularCompare.Core
/// Generate Excel report of differences.
/// </summary>
/// <param name="progBar"></param>
public void ReportDifferences(ProgressBar progBar)
public void ReportDifferences(ToolStripProgressBar progBar)
{
try
{
@ -250,7 +250,7 @@ namespace BismNormalizer.TabularCompare.Core
//Wb.Sheets[1].Delete();
Excel.Worksheet Ws = default(Excel.Worksheet);
Ws = Wb.ActiveSheet;
Ws.Name = "Bism Normalizer Report";
Ws.Name = "Comparison Report";
int row = 1, lastDataSourceRow = -1, lastTableRow = -1;
// set up headers
@ -360,7 +360,7 @@ namespace BismNormalizer.TabularCompare.Core
}
}
private void PopulateExcelRow(Excel.Worksheet Ws, ref int row, ref int lastDataSourceRow, ref int lastTableRow, ComparisonObject comparisonObject, ProgressBar progBar)
private void PopulateExcelRow(Excel.Worksheet Ws, ref int row, ref int lastDataSourceRow, ref int lastTableRow, ComparisonObject comparisonObject, ToolStripProgressBar progBar)
{
progBar.PerformStep();
row += 1;
@ -380,6 +380,9 @@ namespace BismNormalizer.TabularCompare.Core
//Type column
switch (comparisonObject.ComparisonObjectType)
{
case ComparisonObjectType.Model:
Ws.Cells[row, 1].Value = "Model";
break;
case ComparisonObjectType.DataSource:
Ws.Cells[row, 1].Value = "Data Source";
break;
@ -404,6 +407,12 @@ namespace BismNormalizer.TabularCompare.Core
Ws.Cells[row, 2].InsertIndent(3);
Ws.Cells[row, 5].InsertIndent(3);
break;
case ComparisonObjectType.CalculationItem:
Ws.Cells[row, 1].Value = "Calculation Item";
Ws.Cells[row, 1].InsertIndent(3);
Ws.Cells[row, 2].InsertIndent(3);
Ws.Cells[row, 5].InsertIndent(3);
break;
case ComparisonObjectType.Perspective:
Ws.Cells[row, 1].Value = "Perspective";
break;

View File

@ -4,7 +4,7 @@ namespace BismNormalizer.TabularCompare
/// <summary>
/// Type of object that a validation message relates to. For example, Table, Measure, MeasureCalculationDependency, etc.
/// </summary>
public enum ValidationMessageType { DataSource, Table, Relationship, Measure, Kpi, Perspective, Culture, Role, Expression, Action, MeasureCalculationDependency, General }; //General used for command line only
public enum ValidationMessageType { Model, DataSource, Table, Relationship, Measure, Kpi, CalculationGroup, CalculationItem, Perspective, Culture, Role, Expression, Action, MeasureCalculationDependency, AggregationDependency, General }; //General used for command line only
/// <summary>
/// Status for a validation message, such as Informational and Warning.
@ -14,7 +14,7 @@ namespace BismNormalizer.TabularCompare
/// <summary>
/// Type of comparison object. For example, Table, Measure, Relationship, etc.
/// </summary>
public enum ComparisonObjectType { DataSource, Table, Relationship, Measure, Kpi, Perspective, Culture, Role, Expression, Action, Connection }; //Need connection for backwards compatibility when deserializing from xml. Set to data source.
public enum ComparisonObjectType { Model, DataSource, Table, Relationship, Measure, Kpi, CalculationItem, Perspective, Culture, Role, Expression, Action, Connection }; //Need connection for backwards compatibility when deserializing from xml. Set to data source.
/// <summary>
/// Status of comparison object, such as Same Definition, Different Definitions and Missing In Target.
@ -40,4 +40,5 @@ namespace BismNormalizer.TabularCompare
/// Type of dependency. For example, DataSource, Partition, Expression.
/// </summary>
public enum CalcDependencyObjectType { DataSource, Partition, Expression };
}

View File

@ -2712,7 +2712,7 @@ namespace BismNormalizer.TabularCompare.MultidimensionalMetadata
// Show row count for each table
foreach (ProcessingTable table in _tablesToProcess)
{
int rowCount = _connectionInfo.DirectQuery ? 0 : Core.Comparison.FindRowCount(_amoServer, table.Name, _amoDatabase.Name);
int rowCount = Core.Comparison.FindRowCount(_amoServer, table.Name, _amoDatabase.Name);
_parentComparison.OnDeploymentMessage(new DeploymentMessageEventArgs(table.Name, "Success. " + String.Format("{0:#,###0}", rowCount) + " rows transferred.", DeploymentStatus.Success));
}
_parentComparison.OnDeploymentComplete(new DeploymentCompleteEventArgs(DeploymentStatus.Success, null));
@ -2779,7 +2779,7 @@ namespace BismNormalizer.TabularCompare.MultidimensionalMetadata
PartitionRowCounter partition = table.FindPartition(partitionIdNodeList[0].InnerText);
partition.RowCount = e.IntegerData;
_parentComparison.OnDeploymentMessage(new DeploymentMessageEventArgs(table.Name, "Retreived " + String.Format("{0:#,###0}", table.GetRowCount()) + " rows ...", DeploymentStatus.Deploying));
_parentComparison.OnDeploymentMessage(new DeploymentMessageEventArgs(table.Name, "Retrieved " + String.Format("{0:#,###0}", table.GetRowCount()) + " rows ...", DeploymentStatus.Deploying));
}
}

View File

@ -20,6 +20,8 @@ namespace BismNormalizer.TabularCompare
private bool _optionActions;
private bool _optionPartitions;
private bool _optionRetainPartitions;
private bool _optionRetainPolicyPartitions;
private bool _optionRetainStorageMode;
private bool _optionMeasureDependencies;
private ProcessingOption _optionProcessingOption;
private bool _optionTransaction;
@ -38,6 +40,8 @@ namespace BismNormalizer.TabularCompare
_optionActions = Settings.Default.OptionActions;
_optionPartitions = Settings.Default.OptionPartitions;
_optionRetainPartitions = Settings.Default.OptionRetainPartitions;
_optionRetainPolicyPartitions = Settings.Default.OptionRetainPolicyPartitions;
_optionRetainStorageMode = Settings.Default.OptionRetainStorageMode;
_optionMeasureDependencies = Settings.Default.OptionMeasureDependencies;
_optionProcessingOption = (ProcessingOption)Enum.Parse(typeof(ProcessingOption), Settings.Default.OptionProcessingOption);
_optionTransaction = Settings.Default.OptionTransaction;
@ -117,6 +121,24 @@ namespace BismNormalizer.TabularCompare
set { _optionRetainPartitions = value; }
}
/// <summary>
/// A Boolean specifying whether to retain refresh-policy partitions for table updates.
/// </summary>
public bool OptionRetainPolicyPartitions
{
get { return _optionRetainPolicyPartitions; }
set { _optionRetainPolicyPartitions = value; }
}
/// <summary>
/// A Boolean specifying whether to retain storage for table updates on composite models.
/// </summary>
public bool OptionRetainStorageMode
{
get { return _optionRetainStorageMode; }
set { _optionRetainStorageMode = value; }
}
/// <summary>
/// A Boolean specifying whether to display warnings for missing measure dependencies.
/// </summary>
@ -168,6 +190,8 @@ namespace BismNormalizer.TabularCompare
Settings.Default.OptionActions = _optionActions;
Settings.Default.OptionPartitions = _optionPartitions;
Settings.Default.OptionRetainPartitions = _optionRetainPartitions;
Settings.Default.OptionRetainPolicyPartitions = _optionRetainPolicyPartitions;
Settings.Default.OptionRetainStorageMode = _optionRetainStorageMode;
Settings.Default.OptionMeasureDependencies = _optionMeasureDependencies;
Settings.Default.OptionProcessingOption = _optionProcessingOption.ToString();
Settings.Default.OptionTransaction = _optionTransaction;

View File

@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Globalization;
using Microsoft.AnalysisServices.Tabular;
using Tom=Microsoft.AnalysisServices.Tabular;
namespace BismNormalizer.TabularCompare.TabularMetadata
{
/// <summary>
/// Abstraction of a tabular model calculationItem with properties and methods for comparison purposes.
/// </summary>
public class CalculationItem : TabularObject
{
private Table _parentTable;
private Tom.CalculationItem _tomCalculationItem;
/// <summary>
/// Initializes a new instance of the CalculationItem class using multiple parameters.
/// </summary>
/// <param name="parentTable">Table object that the calculationItem belongs to.</param>
/// <param name="tomCalculationItem">Tabular Object Model CalculationItem object abtstracted by the CalculationItem class.</param>
/// <param name="isKpi">Indicates whether the calculationItem is a KPI.</param>
public CalculationItem(Table parentTable, Tom.CalculationItem tomCalculationItem) : base(tomCalculationItem)
{
_parentTable = parentTable;
_tomCalculationItem = tomCalculationItem;
}
/// <summary>
/// Table object that the Relationship oject belongs to.
/// </summary>
public Table ParentTable => _parentTable;
/// <summary>
/// Tabular Object Model CalculationItem object abtstracted by the CalculationItem class.
/// </summary>
public Tom.CalculationItem TomCalculationItem => _tomCalculationItem;
/// <summary>
/// Name of the table that the CalculationItem oject belongs to.
/// </summary>
public string TableName => _tomCalculationItem.CalculationGroup.Table.Name;
public override string ToString() => this.GetType().FullName;
/// <summary>
/// Find missing calculation dependencies by inspecting the DAX expression for the calculationItem and iterating columns and other calculationItems in the tabular model for validity of the expression.
/// </summary>
/// <returns>List of missing dependencies to be displayed or logged as warnings.</returns>
public List<string> FindMissingCalculationItemDependencies()
{
List<string> dependencies = new List<string>();
using (StringReader lines = new StringReader(_tomCalculationItem.Expression))
{
string line = string.Empty;
while ((line = lines.ReadLine()) != null)
{
if (line.TrimStart().Length > 1 && line.TrimStart().Substring(0, 2) != "--") //Ignore comments
{
//Todo2: still need to parse for /* blah */ type comments. Currently can show missing dependency that doesn't apply if within a comment
string whatsRemainingOfLine = line;
while (whatsRemainingOfLine.Contains('[') && whatsRemainingOfLine.Contains(']'))
{
int openSquareBracketPosition = whatsRemainingOfLine.IndexOf('[', 0);
//brilliant person at microsoft has ]] instead of ]
int closeSquareBracketPosition = whatsRemainingOfLine.Replace("]]", " ").IndexOf(']', openSquareBracketPosition + 1);
if (openSquareBracketPosition < closeSquareBracketPosition - 1)
{
string potentialDependency = whatsRemainingOfLine.Substring(openSquareBracketPosition + 1, closeSquareBracketPosition - openSquareBracketPosition - 1);
if (!potentialDependency.Contains('"') &&
!_tomCalculationItem.Expression.Contains($"\"{potentialDependency}\"") && //it's possible the calculationItem itself is deriving the column name from an ADDCOLUMNS for example
!dependencies.Contains(potentialDependency))
{
//unbelievable: some genius at m$ did a replace on ] with ]]
dependencies.Add(potentialDependency);
}
}
whatsRemainingOfLine = whatsRemainingOfLine.Substring(closeSquareBracketPosition + 1);
}
}
}
}
List<string> missingDependencies = new List<string>();
foreach (string dependency in dependencies)
{
bool foundDependency = false;
foreach (Table table in _parentTable.ParentTabularModel.Tables)
{
//Check if another calculationItem or column has same name
if (table.CalculationItems.ContainsNameCaseInsensitive(dependency) || table.ColumnsContainsNameCaseInsensitive(dependency))
{
foundDependency = true;
break;
}
}
if (!foundDependency)
{
missingDependencies.Add(dependency);
}
}
return missingDependencies;
}
}
}

View File

@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
namespace BismNormalizer.TabularCompare.TabularMetadata
{
/// <summary>
/// Represents a collection of CalculationItem objects.
/// </summary>
public class CalculationItemCollection : List<CalculationItem>
{
/// <summary>
/// Find an object in the collection by name.
/// </summary>
/// <param name="name"></param>
/// <returns>CalculationItem object if found. Null if not found.</returns>
public CalculationItem FindByName(string name)
{
foreach (CalculationItem calculationItem in this)
{
if (calculationItem.Name == name)
{
return calculationItem;
}
}
return null;
}
/// <summary>
/// A Boolean specifying whether the collection contains object by name.
/// </summary>
/// <param name="name"></param>
/// <returns>True if the object is found, or False if it's not found.</returns>
public bool ContainsName(string name)
{
foreach (CalculationItem calculationItem in this)
{
if (calculationItem.Name == name)
{
return true;
}
}
return false;
}
/// <summary>
/// A Boolean specifying whether the collection contains object by name searching without case sensitivity.
/// </summary>
/// <param name="name"></param>
/// <returns>True if the object is found, or False if it's not found.</returns>
public bool ContainsNameCaseInsensitive(string name)
{
foreach (CalculationItem calculationItem in this)
{
if (calculationItem.Name.ToUpper() == name.ToUpper())
{
return true;
}
}
return false;
}
/// <summary>
/// Returns a collection of CalculationItem objects filtered by the parent table's name.
/// </summary>
/// <param name="tableName"></param>
/// <returns>CalculationItemCollection</returns>
public CalculationItemCollection FilterByTableName(string tableName)
{
CalculationItemCollection returnCalculationItems = new CalculationItemCollection();
foreach (CalculationItem calculationItem in this)
{
if (calculationItem.TableName == tableName)
{
returnCalculationItems.Add(calculationItem);
}
}
return returnCalculationItems;
}
/// <summary>
/// Removes an object from the collection by its name.
/// </summary>
/// <param name="name"></param>
/// <returns>True if the object was removed, or False if was not found.</returns>
public bool RemoveByName(string name)
{
foreach (CalculationItem calculationItem in this)
{
if (calculationItem.Name == name)
{
this.Remove(calculationItem);
return true;
}
}
return false;
}
}
}

View File

@ -72,6 +72,29 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
{
_comparisonObjectCount = 0;
#region Model
if (_comparisonInfo.TargetCompatibilityLevel >= 1460) //Target compat level is always >= source one.
{
// check if Model object definition is different
ComparisonObject comparisonObjectModel;
if (_sourceTabularModel.Model.ObjectDefinition != _targetTabularModel.Model.ObjectDefinition)
{
comparisonObjectModel = new ComparisonObject(ComparisonObjectType.Model, ComparisonObjectStatus.DifferentDefinitions, _sourceTabularModel.Model, _targetTabularModel.Model, MergeAction.Update);
_comparisonObjects.Add(comparisonObjectModel);
_comparisonObjectCount += 1;
}
else
{
// they are equal, ...
comparisonObjectModel = new ComparisonObject(ComparisonObjectType.Model, ComparisonObjectStatus.SameDefinition, _sourceTabularModel.Model, _targetTabularModel.Model, MergeAction.Skip);
_comparisonObjects.Add(comparisonObjectModel);
_comparisonObjectCount += 1;
}
}
#endregion
#region DataSources
foreach (DataSource dataSourceSource in _sourceTabularModel.DataSources)
@ -155,6 +178,18 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
}
#endregion
#region CalculationItems for Table that is Missing in Target
foreach (CalculationItem calculationItemSource in tblSource.CalculationItems.FilterByTableName(tblSource.Name))
{
ComparisonObjectType comparisonObjectType = ComparisonObjectType.CalculationItem;
ComparisonObject comparisonObjectCalculationItem = new ComparisonObject(comparisonObjectType, ComparisonObjectStatus.MissingInTarget, calculationItemSource, null, MergeAction.Create);
comparisonObjectTable.ChildComparisonObjects.Add(comparisonObjectCalculationItem);
_comparisonObjectCount += 1;
}
#endregion
}
else
{
@ -269,6 +304,53 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
}
#endregion
#region CalculationItems (table in source and target)
// see if matching calculationItem in source and target
foreach (CalculationItem calculationItemSource in tblSource.CalculationItems.FilterByTableName(tblSource.Name))
{
ComparisonObjectType comparisonObjectType = ComparisonObjectType.CalculationItem;
if (tblTarget.CalculationItems.FilterByTableName(tblTarget.Name).ContainsName(calculationItemSource.Name))
{
//CalculationItem in source and target, so check definition
CalculationItem calculationItemTarget = tblTarget.CalculationItems.FilterByTableName(tblTarget.Name).FindByName(calculationItemSource.Name);
if (calculationItemSource.ObjectDefinition == calculationItemTarget.ObjectDefinition)
{
//CalculationItem has same definition
ComparisonObject comparisonObjectCalculationItem = new ComparisonObject(comparisonObjectType, ComparisonObjectStatus.SameDefinition, calculationItemSource, calculationItemTarget, MergeAction.Skip);
comparisonObjectTable.ChildComparisonObjects.Add(comparisonObjectCalculationItem);
_comparisonObjectCount += 1;
}
else
{
//CalculationItem has different definition
ComparisonObject comparisonObjectCalculationItem = new ComparisonObject(comparisonObjectType, ComparisonObjectStatus.DifferentDefinitions, calculationItemSource, calculationItemTarget, MergeAction.Update);
comparisonObjectTable.ChildComparisonObjects.Add(comparisonObjectCalculationItem);
_comparisonObjectCount += 1;
}
}
else
{
ComparisonObject comparisonObjectCalculationItem = new ComparisonObject(comparisonObjectType, ComparisonObjectStatus.MissingInTarget, calculationItemSource, null, MergeAction.Create);
comparisonObjectTable.ChildComparisonObjects.Add(comparisonObjectCalculationItem);
_comparisonObjectCount += 1;
}
}
//now check if target contains calculationItems Missing in Source
foreach (CalculationItem calculationItemTarget in tblTarget.CalculationItems.FilterByTableName(tblTarget.Name))
{
ComparisonObjectType comparisonObjectType = ComparisonObjectType.CalculationItem;
if (!tblSource.CalculationItems.FilterByTableName(tblSource.Name).ContainsName(calculationItemTarget.Name))
{
ComparisonObject comparisonObjectCalculationItem = new ComparisonObject(comparisonObjectType, ComparisonObjectStatus.MissingInSource, null, calculationItemTarget, MergeAction.Delete);
comparisonObjectTable.ChildComparisonObjects.Add(comparisonObjectCalculationItem);
_comparisonObjectCount += 1;
}
}
#endregion
}
}
@ -298,12 +380,24 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
foreach (Measure measureTarget in tblTarget.Measures.FilterByTableName(tblTarget.Name))
{
ComparisonObjectType comparisonObjectType = measureTarget.IsKpi ? ComparisonObjectType.Kpi : ComparisonObjectType.Measure;
ComparisonObject comparisonObjectMeasure = new ComparisonObject(ComparisonObjectType.Measure, ComparisonObjectStatus.MissingInSource, null, measureTarget, MergeAction.Delete);
ComparisonObject comparisonObjectMeasure = new ComparisonObject(comparisonObjectType, ComparisonObjectStatus.MissingInSource, null, measureTarget, MergeAction.Delete);
comparisonObjectTable.ChildComparisonObjects.Add(comparisonObjectMeasure);
_comparisonObjectCount += 1;
}
#endregion
#region CalculationItems for Table that is Missing in Source
foreach (CalculationItem calculationItemTarget in tblTarget.CalculationItems.FilterByTableName(tblTarget.Name))
{
ComparisonObjectType comparisonObjectType = ComparisonObjectType.CalculationItem;
ComparisonObject comparisonObjectCalculationItem = new ComparisonObject(comparisonObjectType, ComparisonObjectStatus.MissingInSource, null, calculationItemTarget, MergeAction.Delete);
comparisonObjectTable.ChildComparisonObjects.Add(comparisonObjectCalculationItem);
_comparisonObjectCount += 1;
}
#endregion
}
}
@ -538,7 +632,18 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
{
#region Refresh/reconnect source and target dbs to check if server definition has changed
if (_uncommitedChanges)
bool reconnect = false;
try
{
_sourceTabularModel.TomDatabase.Refresh();
_targetTabularModel.TomDatabase.Refresh();
}
catch (Exception)
{
reconnect = true;
}
if (reconnect || _uncommitedChanges)
{
// Reconnect to re-initialize
_sourceTabularModel = new TabularModel(this, _comparisonInfo.ConnectionInfoSource, _comparisonInfo);
@ -546,11 +651,6 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
_targetTabularModel = new TabularModel(this, _comparisonInfo.ConnectionInfoTarget, _comparisonInfo);
_targetTabularModel.Connect();
}
else
{
_sourceTabularModel.TomDatabase.Refresh();
_targetTabularModel.TomDatabase.Refresh();
}
if (!_sourceTabularModel.ConnectionInfo.UseProject && _sourceTabularModel.TomDatabase.LastSchemaUpdate > _lastSourceSchemaUpdate)
{
@ -614,6 +714,17 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
#endregion
#region Model1
//Doing before tables in case need to set DiscourageImplicitMeasures=true to create calc groups downstream
bool updatedModel = false;
foreach (ComparisonObject comparisonObject in _comparisonObjects)
{
updatedModel = UpdateModel(comparisonObject, true);
}
#endregion
#region Tables
// do deletions first to minimize chance of conflict
@ -664,6 +775,19 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
_targetTabularModel.CleanUpVariations();
#region Model2
//Doing model after tables in case there are calc group tables created so cannot set DisableImplictMeasures=false
if (!updatedModel)
{
foreach (ComparisonObject comparisonObject in _comparisonObjects)
{
UpdateModel(comparisonObject, false);
}
}
#endregion
#region Measures / KPIs
foreach (ComparisonObject comparisonObject in _comparisonObjects)
@ -692,6 +816,34 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
#endregion
#region CalculationItems
foreach (ComparisonObject comparisonObject in _comparisonObjects)
{
foreach (ComparisonObject childComparisonObject in comparisonObject.ChildComparisonObjects)
{
DeleteCalculationItem(childComparisonObject); //CalculationItem
}
}
foreach (ComparisonObject comparisonObject in _comparisonObjects)
{
foreach (ComparisonObject childComparisonObject in comparisonObject.ChildComparisonObjects)
{
CreateCalculationItem(childComparisonObject, comparisonObject.SourceObjectName); //CalculationItem, Table
}
}
foreach (ComparisonObject comparisonObject in _comparisonObjects)
{
foreach (ComparisonObject childComparisonObject in comparisonObject.ChildComparisonObjects)
{
UpdateCalculationItem(childComparisonObject, comparisonObject.SourceObjectName); //CalculationItem, Table
}
}
#endregion
#region Perspectives
//Restore perspectives that were backed up earlier. Having done this there won't be any dependency issues, so can start comparison changes.
@ -759,6 +911,8 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
#endregion
_targetTabularModel.CleanUpAggregations();
#region Cultures
//Restore cultures that were backed up earlier. Having done this there won't be any dependency issues, so can start comparison changes.
@ -1026,7 +1180,56 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
#endregion
//DataSources
#region Model
private bool UpdateModel(ComparisonObject comparisonObject, bool beforeTables)
{
if (comparisonObject.ComparisonObjectType == ComparisonObjectType.Model && comparisonObject.MergeAction == MergeAction.Update)
{
Model sourceModel = _sourceTabularModel.Model;
Model targetModel = _targetTabularModel.Model;
bool targetHasCalcGroups = false;
foreach (Table table in _targetTabularModel.Tables)
{
if (table.IsCalculationGroup)
{
targetHasCalcGroups = true;
break;
}
}
if (beforeTables)
{
//In this case, may need to create calc groups downstream, so may need to set DiscourageImplicitMeasures to true
if (!targetHasCalcGroups && sourceModel.TomModel.DiscourageImplicitMeasures)
{
_targetTabularModel.UpdateModel(sourceModel, targetModel);
OnValidationMessage(new ValidationMessageEventArgs($"Update model.", ValidationMessageType.Model, ValidationMessageStatus.Informational));
return true;
}
}
else
{
//In this case, have already had chance to create/delete calc groups, so OK to disable implicit measures if able
if (targetHasCalcGroups && sourceModel.TomModel.DiscourageImplicitMeasures == false)
{
OnValidationMessage(new ValidationMessageEventArgs($"Unable to update model because (considering changes) the target has calculation group(s) and the source has DiscourageImplicitMeasures set to false.", ValidationMessageType.Model, ValidationMessageStatus.Warning));
}
else
{
_targetTabularModel.UpdateModel(sourceModel, targetModel);
OnValidationMessage(new ValidationMessageEventArgs($"Update model.", ValidationMessageType.Model, ValidationMessageStatus.Informational));
return true;
}
}
}
return false;
}
#endregion
#region DataSources
private void DeleteDataSource(ComparisonObject comparisonObject)
{
@ -1118,7 +1321,9 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
}
}
//Expressions
#endregion
#region Expressions
private void DeleteExpression(ComparisonObject comparisonObject)
{
@ -1134,7 +1339,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
else
{
string message = $"Unable to delete expression {comparisonObject.TargetObjectName} because the following objects depend on it: {String.Join(", ", warningObjectList)}.";
if (_comparisonInfo.OptionsInfo.OptionRetainPartitions)
if (_comparisonInfo.OptionsInfo.OptionRetainPartitions && !_comparisonInfo.OptionsInfo.OptionRetainPolicyPartitions)
{
message += " Note: the option to retain partitions is on, which may be affecting this.";
}
@ -1203,14 +1408,19 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
}
}
//Tables
#endregion
#region Tables
private void DeleteTable(ComparisonObject comparisonObject)
{
if (comparisonObject.ComparisonObjectType == ComparisonObjectType.Table && comparisonObject.MergeAction == MergeAction.Delete)
{
Table targetTable = _targetTabularModel.Tables.FindByName(comparisonObject.TargetObjectName);
bool isCalculationGroup = false;
if (targetTable != null) isCalculationGroup = targetTable.IsCalculationGroup;
_targetTabularModel.DeleteTable(comparisonObject.TargetObjectName);
OnValidationMessage(new ValidationMessageEventArgs($"Delete table '{comparisonObject.TargetObjectName}'.", ValidationMessageType.Table, ValidationMessageStatus.Informational));
OnValidationMessage(new ValidationMessageEventArgs($"Delete {(isCalculationGroup ? "calculation group" : "table")} '{comparisonObject.TargetObjectName}'.", ValidationMessageType.Table, ValidationMessageStatus.Informational));
}
}
@ -1223,6 +1433,8 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
bool fromDependencies = false;
bool nonStructuredDataSourceLocal = false;
if (!sourceTable.IsCalculationGroup)
{
foreach (Partition partition in sourceTable.TomTable.Partitions)
{
//Check any objects in source that this partition depends on are also going to be created if not already in target
@ -1237,12 +1449,28 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
if (HasBlockingOldPartitionDependency(partition, ref warningObjectList))
fromDependencies = true; //Need if clause in case last of n partitions has no dependencies and sets back to true
}
}
if (!fromDependencies)
{
if (sourceTable.IsCalculationGroup)
{
if (_targetTabularModel.Model.TomModel.DiscourageImplicitMeasures != true)
{
OnValidationMessage(new ValidationMessageEventArgs($"Unable to create calculation group {comparisonObject.SourceObjectName} because the target model doesn't have DiscourageImplicitMeasures set to true.", ValidationMessageType.Table, ValidationMessageStatus.Warning));
}
else
{
_targetTabularModel.CreateTable(sourceTable);
OnValidationMessage(new ValidationMessageEventArgs($"Create calculation group '{comparisonObject.SourceObjectName}'.", ValidationMessageType.Table, ValidationMessageStatus.Informational));
}
}
else
{
_targetTabularModel.CreateTable(sourceTable);
OnValidationMessage(new ValidationMessageEventArgs($"Create table '{comparisonObject.SourceObjectName}'.", ValidationMessageType.Table, ValidationMessageStatus.Informational));
}
}
else
{
if (!nonStructuredDataSourceLocal)
@ -1287,9 +1515,16 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
}
if (!fromDependencies)
{
if (tableSource.IsCalculationGroup != tableTarget.IsCalculationGroup)
{
OnValidationMessage(new ValidationMessageEventArgs($"Unable to update table {comparisonObject.TargetObjectName} because either source or target is a calculation group (but not both).", (tableSource.IsCalculationGroup ? ValidationMessageType.CalculationGroup : ValidationMessageType.Table), ValidationMessageStatus.Warning));
}
else
{
_targetTabularModel.UpdateTable(tableSource, tableTarget, out string retainPartitionsMessage);
OnValidationMessage(new ValidationMessageEventArgs($"Update table '{comparisonObject.TargetObjectName}'. {retainPartitionsMessage}", ValidationMessageType.Table, ValidationMessageStatus.Informational));
OnValidationMessage(new ValidationMessageEventArgs($"Update {(tableSource.IsCalculationGroup ? "calculation group" : "table")} '{comparisonObject.TargetObjectName}'. {retainPartitionsMessage}", ValidationMessageType.Table, ValidationMessageStatus.Informational));
}
}
else
{
@ -1305,7 +1540,9 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
}
}
//Relationships
#endregion
#region Relationships
private void DeleteRelationship(ComparisonObject comparisonObject)
{
@ -1369,7 +1606,9 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
}
}
//Measures / KPIs
#endregion
#region Measures / KPIs
private void DeleteMeasure(ComparisonObject comparisonObject)
{
@ -1440,6 +1679,84 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
#endregion
#region CalculationItems
private void DeleteCalculationItem(ComparisonObject comparisonObject)
{
if ((comparisonObject.ComparisonObjectType == ComparisonObjectType.CalculationItem || comparisonObject.ComparisonObjectType == ComparisonObjectType.Kpi) &&
comparisonObject.MergeAction == MergeAction.Delete)
{
foreach (Table tableTarget in _targetTabularModel.Tables)
{
CalculationItem calculationItemTarget = tableTarget.CalculationItems.FindByName(comparisonObject.TargetObjectInternalName);
if (calculationItemTarget != null)
{
// CalculationItem may have already been deleted if parent table was deleted
tableTarget.DeleteCalculationItem(comparisonObject.TargetObjectInternalName);
break;
}
}
OnValidationMessage(new ValidationMessageEventArgs($"Delete calculation item {comparisonObject.TargetObjectInternalName}.", ValidationMessageType.CalculationItem, ValidationMessageStatus.Informational));
}
}
private void CreateCalculationItem(ComparisonObject comparisonObject, string tableName)
{
if ((comparisonObject.ComparisonObjectType == ComparisonObjectType.CalculationItem || comparisonObject.ComparisonObjectType == ComparisonObjectType.Kpi) &&
comparisonObject.MergeAction == MergeAction.Create)
{
foreach (Table tableInTarget in _targetTabularModel.Tables)
{
CalculationItem calculationItemInTarget = tableInTarget.CalculationItems.FindByName(comparisonObject.SourceObjectInternalName);
if (calculationItemInTarget != null)
{
OnValidationMessage(new ValidationMessageEventArgs($"Unable to create calculation item {comparisonObject.SourceObjectInternalName} because name already exists in target model.", ValidationMessageType.CalculationItem, ValidationMessageStatus.Warning));
return;
}
}
Table tableSource = _sourceTabularModel.Tables.FindByName(tableName);
Table tableTarget = _targetTabularModel.Tables.FindByName(tableName);
if (tableTarget == null)
{
OnValidationMessage(new ValidationMessageEventArgs($"Unable to create calculation item {comparisonObject.SourceObjectInternalName} because (considering changes) target table {tableName} does not exist.", ValidationMessageType.CalculationItem, ValidationMessageStatus.Warning));
return;
}
else if (!tableTarget.IsCalculationGroup)
{
OnValidationMessage(new ValidationMessageEventArgs($"Unable to create calculation item {comparisonObject.SourceObjectInternalName} because the target table {tableName} is not a calculation group table.", ValidationMessageType.CalculationItem, ValidationMessageStatus.Warning));
return;
}
//If we get here, can create calculationItem/kpi
CalculationItem calculationItemSource = tableSource.CalculationItems.FindByName(comparisonObject.SourceObjectInternalName);
tableTarget.CreateCalculationItem(calculationItemSource.TomCalculationItem);
OnValidationMessage(new ValidationMessageEventArgs($"Create calculation item {comparisonObject.SourceObjectInternalName}.", ValidationMessageType.CalculationItem, ValidationMessageStatus.Informational));
}
}
private void UpdateCalculationItem(ComparisonObject comparisonObject, string tableName)
{
if ((comparisonObject.ComparisonObjectType == ComparisonObjectType.CalculationItem || comparisonObject.ComparisonObjectType == ComparisonObjectType.Kpi) &&
comparisonObject.MergeAction == MergeAction.Update)
{
Table tableSource = _sourceTabularModel.Tables.FindByName(tableName);
Table tableTarget = _targetTabularModel.Tables.FindByName(tableName);
CalculationItem calculationItemSource = tableSource.CalculationItems.FindByName(comparisonObject.SourceObjectInternalName);
tableTarget.UpdateCalculationItem(calculationItemSource.TomCalculationItem);
OnValidationMessage(new ValidationMessageEventArgs($"Update calculation item {comparisonObject.SourceObjectInternalName}.", ValidationMessageType.CalculationItem, ValidationMessageStatus.Informational));
}
}
#endregion
#endregion
/// <summary>
/// Update target tabular model with changes defined by actions in ComparisonObject instances.
/// </summary>

View File

@ -44,7 +44,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
}
else
{
if (_comparisonObjectType == ComparisonObjectType.Relationship || _comparisonObjectType == ComparisonObjectType.Measure || _comparisonObjectType == ComparisonObjectType.Kpi)
if (_comparisonObjectType == ComparisonObjectType.Relationship || _comparisonObjectType == ComparisonObjectType.Measure || _comparisonObjectType == ComparisonObjectType.Kpi || _comparisonObjectType == ComparisonObjectType.CalculationItem)
{
return " " + _sourceObject.Name;
}
@ -114,7 +114,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
}
else
{
if (_comparisonObjectType == ComparisonObjectType.Relationship || _comparisonObjectType == ComparisonObjectType.Measure || _comparisonObjectType == ComparisonObjectType.Kpi)
if (_comparisonObjectType == ComparisonObjectType.Relationship || _comparisonObjectType == ComparisonObjectType.Measure || _comparisonObjectType == ComparisonObjectType.Kpi || _comparisonObjectType == ComparisonObjectType.CalculationItem)
{
return " " + _targetObject.Name;
}
@ -172,36 +172,42 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
switch (this.ComparisonObjectType)
{
//tabular objects
case ComparisonObjectType.DataSource:
case ComparisonObjectType.Model:
sortKey = "A";
break;
case ComparisonObjectType.Expression:
case ComparisonObjectType.DataSource:
sortKey = "B";
break;
case ComparisonObjectType.Table:
case ComparisonObjectType.Expression:
sortKey = "C";
break;
case ComparisonObjectType.Relationship:
case ComparisonObjectType.Table:
sortKey = "D";
break;
case ComparisonObjectType.Measure:
case ComparisonObjectType.Relationship:
sortKey = "E";
break;
case ComparisonObjectType.Kpi:
case ComparisonObjectType.Measure:
sortKey = "F";
break;
case ComparisonObjectType.Action:
case ComparisonObjectType.Kpi:
sortKey = "G";
break;
case ComparisonObjectType.Perspective:
case ComparisonObjectType.CalculationItem:
sortKey = "H";
break;
case ComparisonObjectType.Culture:
case ComparisonObjectType.Action:
sortKey = "I";
break;
case ComparisonObjectType.Role:
case ComparisonObjectType.Perspective:
sortKey = "J";
break;
case ComparisonObjectType.Culture:
sortKey = "K";
break;
case ComparisonObjectType.Role:
sortKey = "L";
break;
default:
sortKey = "Z";

View File

@ -0,0 +1,65 @@
using Microsoft.AnalysisServices.Tabular;
using Tom=Microsoft.AnalysisServices.Tabular;
namespace BismNormalizer.TabularCompare.TabularMetadata
{
/// <summary>
/// Abstraction of a tabular model [model] with properties and methods for comparison purposes.
/// </summary>
public class Model : TabularObject
{
#region Private Members
private TabularModel _parentTabularModel;
private Tom.Model _tomModel;
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the Model class using multiple parameters.
/// </summary>
/// <param name="parentTabularModel">TabularModel object that the Model object belongs to.</param>
/// <param name="tomModel">Tabular Object Model Model object abtstracted by the Model class.</param>
public Model(TabularModel parentTabularModel, Tom.Model tomModel) : base(tomModel)
{
_parentTabularModel = parentTabularModel;
_tomModel = tomModel;
PopulateProperties();
}
#endregion
#region Properties
/// <summary>
/// TabularModel object that the Model object belongs to.
/// </summary>
public TabularModel ParentTabularModel => _parentTabularModel;
/// <summary>
/// Tabular Object Model Model object abtstracted by the Model class.
/// </summary>
public Tom.Model TomModel => _tomModel;
public override string ToString() => this.GetType().FullName;
#endregion
private void PopulateProperties()
{
string customObjectDefinition = "{ ";
if (!string.IsNullOrEmpty(_tomModel.Description))
{
customObjectDefinition += $"\"description\": \"{_tomModel.Description}\", ";
}
customObjectDefinition += $"\"defaultMode\": \"{_tomModel.DefaultMode.ToString().ToLower()}\", ";
customObjectDefinition += $"\"discourageImplicitMeasures\": {_tomModel.DiscourageImplicitMeasures.ToString().ToLower()} }}";
base.SetCustomObjectDefinition(customObjectDefinition);
}
}
}

View File

@ -0,0 +1,48 @@
//using System;
//using System.Collections.Generic;
//using System.IO;
//using System.Linq;
//using System.Text;
//using System.Globalization;
//using Microsoft.AnalysisServices.Tabular;
//using Tom=Microsoft.AnalysisServices.Tabular;
//namespace BismNormalizer.TabularCompare.TabularMetadata
//{
// /// <summary>
// /// Abstraction of a tabular model refreshPolicy with properties and methods for comparison purposes.
// /// </summary>
// public class RefreshPolicy : TabularObject
// {
// private Table _parentTable;
// private Tom.RefreshPolicy _tomRefreshPolicy;
// /// <summary>
// /// Initializes a new instance of the RefreshPolicy class using multiple parameters.
// /// </summary>
// /// <param name="parentTable">Table object that the refreshPolicy belongs to.</param>
// /// <param name="tomRefreshPolicy">Tabular Object Model RefreshPolicy object abtstracted by the RefreshPolicy class.</param>
// public RefreshPolicy(Table parentTable, Tom.RefreshPolicy tomRefreshPolicy) : base(tomRefreshPolicy)
// {
// _parentTable = parentTable;
// _tomRefreshPolicy = tomRefreshPolicy;
// }
// /// <summary>
// /// Table object that the Relationship oject belongs to.
// /// </summary>
// public Table ParentTable => _parentTable;
// /// <summary>
// /// Tabular Object Model RefreshPolicy object abtstracted by the RefreshPolicy class.
// /// </summary>
// public Tom.RefreshPolicy TomRefreshPolicy => _tomRefreshPolicy;
// /// <summary>
// /// Name of the table that the RefreshPolicy oject belongs to.
// /// </summary>
// public string TableName => _tomRefreshPolicy.Table.Name;
// public override string ToString() => this.GetType().FullName;
// }
//}

View File

@ -0,0 +1,98 @@
//using System;
//using System.Collections.Generic;
//namespace BismNormalizer.TabularCompare.TabularMetadata
//{
// /// <summary>
// /// Represents a collection of RefreshPolicy objects.
// /// </summary>
// public class RefreshPolicyCollection : List<RefreshPolicy>
// {
// /// <summary>
// /// Find an object in the collection by name.
// /// </summary>
// /// <param name="name"></param>
// /// <returns>RefreshPolicy object if found. Null if not found.</returns>
// public RefreshPolicy FindByName(string name)
// {
// foreach (RefreshPolicy refreshPolicy in this)
// {
// if (refreshPolicy.Name == name)
// {
// return refreshPolicy;
// }
// }
// return null;
// }
// /// <summary>
// /// A Boolean specifying whether the collection contains object by name.
// /// </summary>
// /// <param name="name"></param>
// /// <returns>True if the object is found, or False if it's not found.</returns>
// public bool ContainsName(string name)
// {
// foreach (RefreshPolicy refreshPolicy in this)
// {
// if (refreshPolicy.Name == name)
// {
// return true;
// }
// }
// return false;
// }
// /// <summary>
// /// A Boolean specifying whether the collection contains object by name searching without case sensitivity.
// /// </summary>
// /// <param name="name"></param>
// /// <returns>True if the object is found, or False if it's not found.</returns>
// public bool ContainsNameCaseInsensitive(string name)
// {
// foreach (RefreshPolicy refreshPolicy in this)
// {
// if (refreshPolicy.Name.ToUpper() == name.ToUpper())
// {
// return true;
// }
// }
// return false;
// }
// /// <summary>
// /// Returns a collection of RefreshPolicy objects filtered by the parent table's name.
// /// </summary>
// /// <param name="tableName"></param>
// /// <returns>RefreshPolicyCollection</returns>
// public RefreshPolicyCollection FilterByTableName(string tableName)
// {
// RefreshPolicyCollection returnRefreshPolicys = new RefreshPolicyCollection();
// foreach (RefreshPolicy refreshPolicy in this)
// {
// if (refreshPolicy.TableName == tableName)
// {
// returnRefreshPolicys.Add(refreshPolicy);
// }
// }
// return returnRefreshPolicys;
// }
// /// <summary>
// /// Removes an object from the collection by its name.
// /// </summary>
// /// <param name="name"></param>
// /// <returns>True if the object was removed, or False if was not found.</returns>
// public bool RemoveByName(string name)
// {
// foreach (RefreshPolicy refreshPolicy in this)
// {
// if (refreshPolicy.Name == name)
// {
// this.Remove(refreshPolicy);
// return true;
// }
// }
// return false;
// }
// }
//}

View File

@ -20,6 +20,9 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
private string _dataSourceName;
private RelationshipCollection _relationships = new RelationshipCollection();
private MeasureCollection _measures = new MeasureCollection();
private bool _isCalculationGroup;
private ModeType _tableModeType;
private CalculationItemCollection _calculationItems = new CalculationItemCollection();
/// <summary>
/// Initializes a new instance of the Table class using multiple parameters.
@ -59,6 +62,18 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
/// </summary>
public MeasureCollection Measures => _measures;
/// <summary>
/// True if the table is a calculation group.
/// </summary>
public bool IsCalculationGroup => _isCalculationGroup;
public ModeType TableModeType => _tableModeType;
/// <summary>
/// Collection of calculation items for the Table object.
/// </summary>
public CalculationItemCollection CalculationItems => _calculationItems;
/// <summary>
/// Tabular Object Model Table object abtstracted by the Table class.
/// </summary>
@ -68,16 +83,18 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
{
base.RemovePropertyFromObjectDefinition("measures");
_isCalculationGroup = (_tomTable.CalculationGroup != null);
_partitionsDefinition = "";
_dataSourceName = "";
bool hasMOrQueryPartition = false;
bool hasMQueryOrPolicyPartition = false;
//Associate table with a DataSource if possible. It's not possible if calc table or if M expression refers to a shared expression, or multiple data sources
foreach (Partition partition in _tomTable.Partitions)
{
_tableModeType = partition.Mode;
if (partition.SourceType == PartitionSourceType.M)
{
hasMOrQueryPartition = true;
hasMQueryOrPolicyPartition = true;
//Check M dependency tree to see if all partitions refer only to a single DataSource
CalcDependencyCollection calcDependencies = _parentTabularModel.MDependencies.DependenciesReferenceFrom(CalcDependencyObjectType.Partition, _tomTable.Name, partition.Name);
@ -105,17 +122,24 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
//If old partition, find the primary partition (first one) to determine DataSource. Technically it is possible for different partitions in the same table to point to different DataSources, but the Tabular Designer in VS doesn't support it. If set manually in .bim file, the UI still associates with the first partition (e.g. when processing table by itself, or deletinig the DataSource gives a warning message listing associated tables).
if (partition.SourceType == PartitionSourceType.Query)
{
hasMOrQueryPartition = true;
hasMQueryOrPolicyPartition = true;
_dataSourceName = ((QueryPartitionSource)partition.Source).DataSource.Name;
break;
}
//Might be a policy partition.
if (partition.SourceType == PartitionSourceType.PolicyRange)
{
hasMQueryOrPolicyPartition = true;
break;
}
}
if (hasMOrQueryPartition)
if (hasMQueryOrPolicyPartition || _isCalculationGroup)
{
_partitionsDefinition = base.RetrievePropertyFromObjectDefinition("partitions");
//Option to hide partitions only applies to M and query partitions (calculated tables hold dax defintitions in their partitions)
//Option to hide partitions only applies to M, query and policy partitions (calculated tables hold dax defintitions in their partitions)
if (!_parentTabularModel.ComparisonInfo.OptionsInfo.OptionPartitions)
{
base.RemovePropertyFromObjectDefinition("partitions");
@ -136,6 +160,15 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
{
_measures.Add(new Measure(this, measure, measure.KPI != null));
}
//Find calc items
if (_isCalculationGroup)
{
foreach (Tom.CalculationItem calcItem in _tomTable.CalculationGroup.CalculationItems)
{
_calculationItems.Add(new CalculationItem(this, calcItem));
}
}
}
#region Relationship collection methods
@ -174,8 +207,12 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
/// Find all direct relationships that filter this table. This is all ACTIVE relationships where 1) this is FROM table, or 2) this is TO table with CrossFilteringBehavior=BothDirections
/// </summary>
/// <returns>All the associated Relationships.</returns>
public List<Relationship> FindFilteringRelationships()
public List<Relationship> FindFilteredRelationships(bool checkSecurityBehavior = false)
{
//T1[C1]->T2[C2]
//FromTableName: T1 *** this.Name
//ToTableName: T2
//Considers DIRECT relationships for this table ONLY (1 level).
List<Relationship> filteringRelationships = new List<Relationship>();
foreach (Table table in _parentTabularModel.Tables)
@ -184,7 +221,36 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
{
if (relationship.TomRelationship.IsActive &&
(relationship.FromTableName == this.Name ||
(relationship.ToTableName == this.Name && relationship.TomRelationship.CrossFilteringBehavior == CrossFilteringBehavior.BothDirections)
(relationship.ToTableName == this.Name && relationship.TomRelationship.CrossFilteringBehavior == CrossFilteringBehavior.BothDirections && (!checkSecurityBehavior || (checkSecurityBehavior && relationship.TomRelationship.SecurityFilteringBehavior == SecurityFilteringBehavior.BothDirections)))
)
)
{
filteringRelationships.Add(relationship);
}
}
}
return filteringRelationships;
}
/// <summary>
/// Find all direct relationships that filter this table. This is all ACTIVE relationships where 1) this is FROM table, or 2) this is TO table with CrossFilteringBehavior=BothDirections
/// </summary>
/// <returns>All the associated Relationships.</returns>
public List<Relationship> FindFilteringRelationships(bool checkSecurityBehavior = false)
{
//T1[C1]->T2[C2]
//FromTableName: T1
//ToTableName: T2 *** this.Name
//Considers DIRECT relationships for this table ONLY (1 level).
List<Relationship> filteringRelationships = new List<Relationship>();
foreach (Table table in _parentTabularModel.Tables)
{
foreach (Relationship relationship in table.Relationships)
{
if (relationship.TomRelationship.IsActive &&
(relationship.ToTableName == this.Name ||
(relationship.FromTableName == this.Name && relationship.TomRelationship.CrossFilteringBehavior == CrossFilteringBehavior.BothDirections && (!checkSecurityBehavior || (checkSecurityBehavior && relationship.TomRelationship.SecurityFilteringBehavior == SecurityFilteringBehavior.BothDirections)))
)
)
{
@ -311,6 +377,12 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
return false;
}
if (this.IsCalculationGroup || _parentTabularModel.Tables.FindByName(tabularRelationshipSource.ToTable.Name).IsCalculationGroup)
{
warningMessage = $"Unable to create Relationship {relationshipName} because one or more tables is a calculation group.";
return false;
}
// Delete the target relationship with same tables/columns if still there. Not using RemoveByInternalName in case internal name is actually different.
if (this.Relationships.ContainsName(relationshipSource.Name))
{
@ -403,10 +475,75 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
CreateMeasure(tomMeasureSource);
}
// CalculationItems
/// <summary>
/// Delete calculation item associated with the Table object.
/// </summary>
/// <param name="name">Name of the calculationItem to be deleted.</param>
public void DeleteCalculationItem(string name)
{
if (_tomTable.CalculationGroup.CalculationItems.ContainsName(name))
{
_tomTable.CalculationGroup.CalculationItems.Remove(name);
}
// shell model
if (_calculationItems.ContainsName(name))
{
_calculationItems.RemoveByName(name);
}
}
/// <summary>
/// Create calculationItem associated with the Table object.
/// </summary>
/// <param name="tomCalculationItemSource">Tabular Object Model CalculationItem object from the source tabular model to be abstracted in the target.</param>
public void CreateCalculationItem(Tom.CalculationItem tomCalculationItemSource)
{
if (_tomTable.CalculationGroup.CalculationItems.ContainsName(tomCalculationItemSource.Name))
{
_tomTable.CalculationGroup.CalculationItems.Remove(tomCalculationItemSource.Name);
}
Tom.CalculationItem tomCalculationItemTarget = new Tom.CalculationItem();
tomCalculationItemSource.CopyTo(tomCalculationItemTarget);
_tomTable.CalculationGroup.CalculationItems.Add(tomCalculationItemTarget);
// shell model
_calculationItems.Add(new CalculationItem(this, tomCalculationItemTarget));
}
/// <summary>
/// Update calculationItem associated with the Table object.
/// </summary>
/// <param name="tomCalculationItemSource">Tabular Object Model CalculationItem object from the source tabular model to be abstracted in the target.</param>
public void UpdateCalculationItem(Tom.CalculationItem tomCalculationItemSource)
{
if (_calculationItems.ContainsName(tomCalculationItemSource.Name))
{
DeleteCalculationItem(tomCalculationItemSource.Name);
}
CreateCalculationItem(tomCalculationItemSource);
}
#endregion
#region Other public methods
/// <summary>
/// For option when retain storage mode in composite models.
/// </summary>
/// <param name="modeType"></param>
public void ResetStorageMode(ModeType modeType)
{
foreach (Partition partition in _tomTable.Partitions)
{
partition.Mode = modeType;
}
_tableModeType = modeType;
}
/// <summary>
/// A Boolean specifying whether the table contains a column with the same name searching without case sensitivity.
/// </summary>

View File

@ -66,7 +66,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
/// Returns a collection of Table objects that do not have a DataSource associated with them. These can be calculated tables or tables with M partitions that do not refer to a DataSource.
/// </summary>
/// <returns></returns>
public TableCollection WithoutDataSource(Model model)
public TableCollection WithoutDataSource(Microsoft.AnalysisServices.Tabular.Model model)
{
TableCollection tablesWithDataSource = new TableCollection();
foreach (Microsoft.AnalysisServices.Tabular.DataSource dataSource in model.DataSources)

View File

@ -22,6 +22,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
private ComparisonInfo _comparisonInfo;
private Server _server;
private Database _database;
private Model _model;
private DataSourceCollection _dataSources = new DataSourceCollection();
private TableCollection _tables = new TableCollection();
private ExpressionCollection _expressions = new ExpressionCollection();
@ -68,6 +69,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
InitializeCalcDependencies();
//Shell model
_model = new Model(this, _database.Model);
foreach (Tom.DataSource dataSource in _database.Model.DataSources)
{
if (dataSource.Type == DataSourceType.Provider || dataSource.Type == DataSourceType.Structured)
@ -212,6 +214,15 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
set { _database = value; }
}
/// <summary>
/// Model object.
/// </summary>
public Model Model
{
get { return _model; }
set { _model = value; }
}
/// <summary>
/// Collection of DataSources for the TabularModel object.
/// </summary>
@ -291,6 +302,25 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
#endregion
#region Model
/// <summary>
/// Update Model associated with the TabularModel object.
/// </summary>
/// <param name="dataSourceSource">Model object from the source tabular model to be updated in the target.</param>
/// <param name="dataSourceTarget">Model object in the target tabular model to be updated.</param>
public void UpdateModel(Model modelSource, Model modelTarget)
{
modelTarget.TomModel.Description = modelSource.TomModel.Description;
if (!_comparisonInfo.OptionsInfo.OptionRetainStorageMode)
{
modelTarget.TomModel.DefaultMode = modelSource.TomModel.DefaultMode;
}
modelTarget.TomModel.DiscourageImplicitMeasures = modelSource.TomModel.DiscourageImplicitMeasures;
}
#endregion
#region DataSources
/// <summary>
@ -416,6 +446,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
}
tomTableTarget.Measures.Clear(); //Measures will be added separately later
tomTableTarget.CalculationGroup?.CalculationItems.Clear(); //Calculation items will be added separately later
_database.Model.Tables.Add(tomTableTarget);
_tables.Add(new Table(this, tomTableTarget));
@ -430,6 +461,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
{
bool canRetainPartitions = CanRetainPartitions(tableSource, tableTarget, out retainPartitionsMessage);
Tom.Table tomTableTargetOrig = tableTarget.TomTable.Clone();
ModeType tableTargetModeType = tableTarget.TableModeType;
List<SingleColumnRelationship> tomRelationshipsToAddBack = DeleteTable(tableTarget.Name);
CreateTable(tableSource);
@ -462,50 +494,70 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
{
tableTarget.CreateMeasure(tomMeasureToAddBack);
}
//add back calculationItems
if (tomTableTargetOrig.CalculationGroup != null)
{
foreach (Tom.CalculationItem tomCalculationItemToAddBack in tomTableTargetOrig.CalculationGroup.CalculationItems)
{
tableTarget.CreateCalculationItem(tomCalculationItemToAddBack);
}
}
else
{
//add back storage mode if option selected
if (_comparisonInfo.OptionsInfo.OptionRetainStorageMode)
{
tableTarget.ResetStorageMode(tableTargetModeType);
}
}
}
public bool CanRetainPartitions(Table tableSource, Table tableTarget, out string retainPartitionsMessage)
{
//Initialize variables
retainPartitionsMessage = "";
//only applies to db deployment, and need option checked
if (!_comparisonInfo.OptionsInfo.OptionRetainPartitions)
return false;
//both tables need to have M or query partitions. Also type needs to match (won't copy query partition to M table). If a table has no partitions, do nothing.
PartitionSourceType sourceTypeTarget = PartitionSourceType.None;
PartitionSourceType sourceTypeSource = PartitionSourceType.None;
foreach (Partition partition in tableSource.TomTable.Partitions)
{
sourceTypeTarget = partition.SourceType;
sourceTypeSource = partition.SourceType;
break;
}
if (!(sourceTypeTarget == PartitionSourceType.M || sourceTypeTarget == PartitionSourceType.Query))
PartitionSourceType sourceTypeTarget = PartitionSourceType.None;
foreach (Partition partitionTarget in tableTarget.TomTable.Partitions)
{
sourceTypeTarget = partitionTarget.SourceType;
break;
}
//Verify necessary options are checked
if (!_comparisonInfo.OptionsInfo.OptionRetainPartitions)
return false;
if (_comparisonInfo.OptionsInfo.OptionRetainPolicyPartitions && sourceTypeTarget != PartitionSourceType.PolicyRange)
return false;
//both tables need to have M or query partitions, or target can be policy partitions. Also type needs to match (won't copy query partition to M table). If a table has no partitions, do nothing.
if (!(sourceTypeSource == PartitionSourceType.M || sourceTypeSource == PartitionSourceType.Query || sourceTypeSource == PartitionSourceType.PolicyRange))
{
retainPartitionsMessage = $"Retain partitions not applicable to partition types.";
return false;
}
PartitionSourceType sourceTypeOrig = PartitionSourceType.None;
foreach (Partition partitionOrig in tableTarget.TomTable.Partitions)
{
sourceTypeOrig = partitionOrig.SourceType;
break;
}
if (!(sourceTypeOrig == PartitionSourceType.M || sourceTypeOrig == PartitionSourceType.Query))
if (!(sourceTypeTarget == PartitionSourceType.M || sourceTypeTarget == PartitionSourceType.Query || sourceTypeTarget == PartitionSourceType.PolicyRange))
{
retainPartitionsMessage = $"Retain partitions not applicable to partition types.";
return false;
}
if (sourceTypeOrig != sourceTypeTarget)
if ((sourceTypeTarget != sourceTypeSource) && sourceTypeTarget != PartitionSourceType.PolicyRange)
{
retainPartitionsMessage = $"Retain partitions not applied because source partition type is {sourceTypeTarget.ToString()} and target partition type is {sourceTypeOrig.ToString()}.";
retainPartitionsMessage = $"Retain partitions not applied because source partition type is {sourceTypeSource.ToString()} and target partition type is {sourceTypeTarget.ToString()}.";
return false;
}
if (tableSource.PartitionsDefinition == tableTarget.PartitionsDefinition)
{
retainPartitionsMessage = "Source & target partition definitions match, so retain partitions not necessary.";
retainPartitionsMessage = "Source & target partition definitions already match, so retain partitions not necessary.";
return false;
}
@ -592,7 +644,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
//beginTable might be the From or the To table in TOM Relationship object; it depends on CrossFilterDirection.
RelationshipChainsFromRoot referencedTableCollection = new RelationshipChainsFromRoot();
foreach (Relationship filteringRelationship in beginTable.FindFilteringRelationships())
foreach (Relationship filteringRelationship in beginTable.FindFilteredRelationships())
{
// EndTable can be either the From or the To table of a Relationship object depending on CrossFilteringBehavior
string endTableName = GetEndTableName(beginTable, filteringRelationship, out bool biDi);
@ -661,7 +713,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
chainsFromRoot.Add(link);
Table beginTable = link.EndTable; //EndTable is now the begin table as iterating next level ...
foreach (Relationship filteringRelationship in beginTable.FindFilteringRelationships())
foreach (Relationship filteringRelationship in beginTable.FindFilteredRelationships())
{
// EndTable can be either the From or the To table of a Relationship object depending on direction of CrossFilteringBehavior
string endTableName = GetEndTableName(beginTable, filteringRelationship, out bool biDi);
@ -694,7 +746,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
#endregion
#region Variation Cleanup
#region Variation / Aggregations Cleanup
/// <summary>
/// Remove variations referring to objects that don't exist.
@ -769,7 +821,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
}
}
//Check if any tables that have ShowAsVariationsOnly = true really have variatinos pointing at them
//Check if any tables that have ShowAsVariationsOnly = true really have variations pointing at them
foreach (Table table in _tables)
{
if (table.TomTable.ShowAsVariationsOnly == true && !targetVariationTablesRemaining.Contains(table.Name))
@ -779,6 +831,225 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
}
}
/// <summary>
/// Remove aggregations referring to objects that don't exist.
/// </summary>
public void CleanUpAggregations()
{
//modelTablesWithRls to be used for Rule 11 below:
List<string> modelTablesWithRls = new List<string>();
foreach (Role role in _roles)
{
foreach (TablePermission tablePermission in role.TomRole.TablePermissions)
{
if (!String.IsNullOrEmpty(tablePermission.FilterExpression))
{
modelTablesWithRls.Add(tablePermission.Name);
}
}
}
foreach (Table table in _tables)
{
bool foundViolation = false;
string warningMessage = "";
foreach (Column column in table.TomTable.Columns)
{
if (!foundViolation)
{
/* Check aggs refer to valid base tables/columns
*/
if (column.AlternateOf?.BaseTable != null)
{
if (!_database.Model.Tables.ContainsName(column.AlternateOf.BaseTable.Name))
{
//Base table doesn't exist
foundViolation = true;
warningMessage = $"Removed aggregations on table {table.Name} because summarization {column.AlternateOf.Summarization.ToString()} on column {column.Name} (considering changes) refers to detail table that does not exist [table:{column.AlternateOf.BaseTable.Name}].\n";
break;
}
}
else if (column.AlternateOf?.BaseColumn != null)
{
if (_database.Model.Tables.ContainsName(column.AlternateOf.BaseColumn.Table?.Name))
{
//the referenced table is there, how about the referenced column?
if (!_database.Model.Tables.Find(column.AlternateOf.BaseColumn.Table.Name).Columns.ContainsName(column.AlternateOf.BaseColumn.Name))
{
//Base column does not exist
foundViolation = true;
warningMessage = $"Removed aggregations on table {table.Name} because summarization {column.AlternateOf.Summarization.ToString()} on column {column.Name} (considering changes) refers to detail column that does not exist [table:{column.AlternateOf.BaseColumn.Table.Name}/column:{column.AlternateOf.BaseColumn.Name}].\n";
break;
}
}
else
{
//Base table does not exist
foundViolation = true;
warningMessage = $"Removed aggregations on table {table.Name} because summarization {column.AlternateOf.Summarization.ToString()} on column {column.Name} (considering changes) refers to detail table that does not exist [table:{column.AlternateOf.BaseColumn.Table.Name}].\n";
break;
}
}
string detailTableName = null;
if (!foundViolation && column.AlternateOf != null)
{
detailTableName = (column.AlternateOf.BaseTable != null ? column.AlternateOf.BaseTable.Name : column.AlternateOf.BaseColumn.Table.Name);
}
Table detailTable = _tables.FindByName(detailTableName);
if (!foundViolation && column.AlternateOf != null && column.AlternateOf.Summarization != SummarizationType.GroupBy && modelTablesWithRls.Count > 0 && detailTable != null)
{
/* Rule 11: RLS expressions that can filter the agg table, must also be able to filter the detail table(s) using an active relationship
*/
//Get list of filtering RLS tables that filter the agg table
List<string> rlsTablesFilteringAgg = new List<string>(); //RLS tables that filter the agg table
//beginTable might be the From or the To table in TOM Relationship object; it depends on CrossFilterDirection.
RelationshipChainsFromRoot referencedTableCollection = new RelationshipChainsFromRoot();
foreach (Relationship filteringRelationship in table.FindFilteredRelationships(checkSecurityBehavior: true))
{
// EndTable can be either the From or the To table of a Relationship object depending on CrossFilteringBehavior/SecurityBehavior
string endTableName = GetEndTableName(table, filteringRelationship, out bool biDi);
RelationshipLink rootLink = new RelationshipLink(table, _tables.FindByName(endTableName), true, "", false, filteringRelationship, biDi);
ValidateLinkForAggsRls(rootLink, referencedTableCollection, modelTablesWithRls, rlsTablesFilteringAgg);
}
//If the agg table itself has RLS on it, then consider it a table that is filtering the agg too
if (modelTablesWithRls.Contains(table.Name))
{
rlsTablesFilteringAgg.Add(table.Name);
}
if (rlsTablesFilteringAgg.Count > 0)
{
//Get list of filtering RLS tables on the detail table
List<string> rlsTablesFilteringDetail = new List<string>(); //RLS tables that filter the detail table
//beginTable might be the From or the To table in TOM Relationship object; it depends on CrossFilterDirection.
referencedTableCollection = new RelationshipChainsFromRoot();
foreach (Relationship filteringRelationship in detailTable.FindFilteredRelationships(checkSecurityBehavior: true))
{
// EndTable can be either the From or the To table of a Relationship object depending on CrossFilteringBehavior/SecurityBehavior
string endTableName = GetEndTableName(detailTable, filteringRelationship, out bool biDi);
RelationshipLink rootLink = new RelationshipLink(detailTable, _tables.FindByName(endTableName), true, "", false, filteringRelationship, biDi);
ValidateLinkForAggsRls(rootLink, referencedTableCollection, modelTablesWithRls, rlsTablesFilteringDetail);
}
//For each agg table, check any RLS filter tables also covers the detail table
foreach (string rlsTableFilteringAgg in rlsTablesFilteringAgg)
{
if (!rlsTablesFilteringDetail.Contains(rlsTableFilteringAgg))
{
foundViolation = true;
warningMessage = $"Removed aggregations on table {table.Name} because summarization {column.AlternateOf.Summarization.ToString()} on column {column.Name} (considering changes) RLS filter on table {rlsTableFilteringAgg} that filters the agg, but does not filter detail table {detailTableName}.\n";
break;
}
}
}
}
if (!foundViolation && column.AlternateOf != null)
{
/* Rule 10: Relationships between aggregation tables and other (non-aggregation) tables are not allowed if the aggregation table is on the filtering side of a relationship (active or inactive relationships).
This rule applies whether relationships are weak or strong, whether BiDi or not [including to-many BiDi, not just to-one]
*/
//beginTable might be the From or the To table in TOM Relationship object; it depends on CrossFilterDirection.
RelationshipChainsFromRoot referencedTableCollection = new RelationshipChainsFromRoot();
foreach (Relationship filteringRelationship in table.FindFilteringRelationships())
{
// EndTable can be either the From or the To table of a Relationship object depending on CrossFilteringBehavior/SecurityBehavior
string endTableName = GetEndTableName(table, filteringRelationship, out bool biDi);
Table endTable = _tables.FindByName(endTableName);
if (endTable != null)
{
bool endTableContainsAggs = false;
foreach (Column col in endTable.TomTable.Columns)
{
if (col.AlternateOf != null)
{
//End table has at least 1 agg so we are good
endTableContainsAggs = true;
break;
}
}
if (!endTableContainsAggs)
{
foundViolation = true;
warningMessage = $"Removed aggregations on table {table.Name} because summarization {column.AlternateOf.Summarization.ToString()} on column {column.Name} (considering changes) the agg table is on the filtering side of a relationship to a table ({endTable.Name}) that does not contain aggregations, which is not allowed.\n";
break;
}
}
}
}
if (!foundViolation && column.AlternateOf != null && detailTable != null)
{
/* Rule 3: Chained aggregations are disallowed
*/
foreach (Column detailColumn in detailTable.TomTable.Columns)
{
if (detailColumn.AlternateOf != null)
{
foundViolation = true;
warningMessage = $"Removed aggregations on table {table.Name} because summarization {column.AlternateOf.Summarization.ToString()} on column {column.Name} (considering changes) the detail table {detailTableName} also contains aggregations, which is not allowed.\n";
break;
}
}
}
}
}
//Clear all aggs on the agg table
if (foundViolation)
{
_parentComparison.OnValidationMessage(new ValidationMessageEventArgs(warningMessage, ValidationMessageType.AggregationDependency, ValidationMessageStatus.Warning));
foreach (Column column in table.TomTable.Columns)
{
if (column.AlternateOf != null)
{
column.AlternateOf = null;
}
}
}
}
}
private void ValidateLinkForAggsRls(RelationshipLink link, RelationshipChainsFromRoot chainsFromRoot, List<string> modelTablesWithRls, List<string> rlsTablesFiltering)
{
if (link.FilteringRelationship.TomRelationship.IsActive)
{
if (modelTablesWithRls.Contains(link.EndTable.Name) && !rlsTablesFiltering.Contains(link.EndTable.Name))
{
rlsTablesFiltering.Add(link.EndTable.Name);
}
//Add the link to the chain and re-iterate ...
chainsFromRoot.Add(link);
Table beginTable = link.EndTable; //EndTable is now the begin table as iterating next level ...
foreach (Relationship filteringRelationship in beginTable.FindFilteredRelationships(checkSecurityBehavior: true))
{
// EndTable can be either the From or the To table of a Relationship object depending on direction of CrossFilteringBehavior
string endTableName = GetEndTableName(beginTable, filteringRelationship, out bool biDi);
//Need to check if endTableName has already been covered by TablePath to avoid CrossFilteringBehavior leading both ways and never ending loop
if (!link.TablePath.Contains("'" + endTableName + "'"))
{
RelationshipLink newLink = new RelationshipLink(beginTable, _tables.FindByName(endTableName), false, link.TablePath, link.PrecedingPathBiDiInvoked, filteringRelationship, biDi);
ValidateLinkForAggsRls(newLink, chainsFromRoot, modelTablesWithRls, rlsTablesFiltering);
}
}
}
}
#endregion
#region Backup / Restore
@ -1275,7 +1546,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
break;
}
//If namedObjectTarget is null, the model object doesn't exist in target, so can ignore
//If namedObjectTarget is null, the model object does not exist in target, so can ignore
if (namedObjectTarget != null)
{
//Does the translation already exist in cultureTarget?
@ -1683,7 +1954,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
traceEvent.Columns.Add(Amo.TraceColumn.IntegerData);
traceEvent.Columns.Add(Amo.TraceColumn.SessionID);
traceEvent.Columns.Add(Amo.TraceColumn.Spid);
trace.Update();
trace.Update(Amo.UpdateOptions.Default, Amo.UpdateMode.CreateOrReplace);
trace.OnEvent += new TraceEventHandler(Trace_OnEvent);
trace.Start();
@ -1707,8 +1978,20 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
// Show row count for each table
foreach (ProcessingTable table in _tablesToProcess)
{
int rowCount = _connectionInfo.DirectQuery ? 0 : Core.Comparison.FindRowCount(_server, table.Name, _database.Name);
_parentComparison.OnDeploymentMessage(new DeploymentMessageEventArgs(table.Name, $"Success. {String.Format("{0:#,###0}", rowCount)} rows transferred.", DeploymentStatus.Success));
string message = "";
if (
this._tables.FindByName(table.Name)?.TableModeType == ModeType.DirectQuery ||
(this._tables.FindByName(table.Name)?.TableModeType == ModeType.Default && _database.Model.DefaultMode == ModeType.DirectQuery)
)
{
message = "Success. 0 rows transferred (DirectQuery).";
}
else
{
int rowCount = Core.Comparison.FindRowCount(_server, table.Name, _database.Name);
message = $"Success. {String.Format("{0:#,###0}", rowCount)} rows transferred.";
}
_parentComparison.OnDeploymentMessage(new DeploymentMessageEventArgs(table.Name, message, DeploymentStatus.Success));
}
_parentComparison.OnDeploymentComplete(new DeploymentCompleteEventArgs(DeploymentStatus.Success, null));
}
@ -1785,7 +2068,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
PartitionRowCounter partition = processingTable.FindPartition(partitionNodeList[0].InnerText);
partition.RowCount = e.IntegerData;
_parentComparison.OnDeploymentMessage(new DeploymentMessageEventArgs(processingTable.Name, $"Retreived {String.Format("{0:#,###0}", processingTable.GetRowCount())} rows ...", DeploymentStatus.Deploying));
_parentComparison.OnDeploymentMessage(new DeploymentMessageEventArgs(processingTable.Name, $"Retrieved {String.Format("{0:#,###0}", processingTable.GetRowCount())} rows ...", DeploymentStatus.Deploying));
}
}
@ -1860,10 +2143,10 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
private void FinalValidation()
{
if (_connectionInfo.DirectQuery && _dataSources.Count > 1)
{
throw new InvalidOperationException("Target model contains multiple data sources, which are not allowed for Direct Query models. Re-run comparison and (considering changes) ensure there is a single connection in the target model.");
}
//if (_connectionInfo.DirectQuery && _dataSources.Count > 1)
//{
// throw new InvalidOperationException("Target model contains multiple data sources, which are not allowed for Direct Query models. Re-run comparison and (considering changes) ensure there is a single connection in the target model.");
//}
}
public override string ToString() => this.GetType().FullName;

View File

@ -24,6 +24,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
public TabularObject(NamedMetadataObject namedMetaDataObject)
{
_name = namedMetaDataObject.Name;
if (namedMetaDataObject is Tom.Model) return; //Model has custom JSON string
//Serialize json
SerializeOptions options = new SerializeOptions();
@ -40,9 +41,23 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
_objectDefinition = token.ToString(Formatting.Indented);
}
//todo: remove with Giri's fix
//Remove return characters
if (namedMetaDataObject is Tom.NamedExpression || namedMetaDataObject is Tom.Table)
{
_objectDefinition = _objectDefinition.Replace("\\r", "");
}
//Order table columns
if (namedMetaDataObject is Tom.Table)
{
if (((Tom.Table)namedMetaDataObject).CalculationGroup != null)
{
JToken token = JToken.Parse(_objectDefinition);
RemovePropertyFromObjectDefinition(token, "calculationItems");
_objectDefinition = token.ToString(Formatting.Indented);
}
_objectDefinition = SortArray(_objectDefinition, "columns");
_objectDefinition = SortArray(_objectDefinition, "partitions");
}
@ -111,6 +126,14 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
_objectDefinition = jObj.ToString(Formatting.Indented);
}
/// <summary>
/// Set a custom JSON string. An example is for the model class which contains properties that cannot be set.
/// </summary>
public void SetCustomObjectDefinition(string customObjectDefinition)
{
_objectDefinition = JToken.Parse(customObjectDefinition).ToString();
}
/// <summary>
/// Retrieve a JSON property definition from the full object definition. An example is partitions.
/// </summary>

View File

@ -58,16 +58,16 @@
this.btnOptions = new System.Windows.Forms.ToolStripButton();
this.btnReportDifferences = new System.Windows.Forms.ToolStripButton();
this.scDifferenceResults = new System.Windows.Forms.SplitContainer();
this.pnlProgressBar = new System.Windows.Forms.Panel();
this.progressBar = new System.Windows.Forms.ProgressBar();
this.lblProgressBar = new System.Windows.Forms.Label();
this.treeGridComparisonResults = new BismNormalizer.TabularCompare.UI.TreeGridViewComparison();
this.TreeGridImageList = new System.Windows.Forms.ImageList(this.components);
this.scObjectDefinitions = new System.Windows.Forms.SplitContainer();
this.txtSourceObjectDefinition = new BismNormalizer.TabularCompare.UI.SynchronizedScrollRichTextBox();
this.label4 = new System.Windows.Forms.Label();
this.txtTargetObjectDefinition = new BismNormalizer.TabularCompare.UI.SynchronizedScrollRichTextBox();
this.label5 = new System.Windows.Forms.Label();
this.progressBar = new System.Windows.Forms.ToolStripProgressBar();
this.pnlProgressBar = new System.Windows.Forms.StatusStrip();
this.toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel();
this.treeGridComparisonResults = new BismNormalizer.TabularCompare.UI.TreeGridViewComparison();
this.txtSourceObjectDefinition = new BismNormalizer.TabularCompare.UI.SynchronizedScrollRichTextBox();
this.txtTargetObjectDefinition = new BismNormalizer.TabularCompare.UI.SynchronizedScrollRichTextBox();
this.pnlHeader.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.spltSourceTarget)).BeginInit();
this.spltSourceTarget.Panel1.SuspendLayout();
@ -78,12 +78,12 @@
this.scDifferenceResults.Panel1.SuspendLayout();
this.scDifferenceResults.Panel2.SuspendLayout();
this.scDifferenceResults.SuspendLayout();
this.pnlProgressBar.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.treeGridComparisonResults)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.scObjectDefinitions)).BeginInit();
this.scObjectDefinitions.Panel1.SuspendLayout();
this.scObjectDefinitions.Panel2.SuspendLayout();
this.scObjectDefinitions.SuspendLayout();
this.pnlProgressBar.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.treeGridComparisonResults)).BeginInit();
this.SuspendLayout();
//
// pnlHeader
@ -92,8 +92,9 @@
this.pnlHeader.Controls.Add(this.toolStrip1);
this.pnlHeader.Dock = System.Windows.Forms.DockStyle.Top;
this.pnlHeader.Location = new System.Drawing.Point(0, 0);
this.pnlHeader.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.pnlHeader.Name = "pnlHeader";
this.pnlHeader.Size = new System.Drawing.Size(653, 55);
this.pnlHeader.Size = new System.Drawing.Size(980, 85);
this.pnlHeader.TabIndex = 46;
//
// spltSourceTarget
@ -101,6 +102,7 @@
this.spltSourceTarget.Dock = System.Windows.Forms.DockStyle.Fill;
this.spltSourceTarget.IsSplitterFixed = true;
this.spltSourceTarget.Location = new System.Drawing.Point(0, 25);
this.spltSourceTarget.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.spltSourceTarget.Name = "spltSourceTarget";
//
// spltSourceTarget.Panel1
@ -112,16 +114,18 @@
//
this.spltSourceTarget.Panel2.Controls.Add(this.txtTarget);
this.spltSourceTarget.Panel2.Controls.Add(this.label2);
this.spltSourceTarget.Size = new System.Drawing.Size(653, 30);
this.spltSourceTarget.SplitterDistance = 321;
this.spltSourceTarget.Size = new System.Drawing.Size(980, 60);
this.spltSourceTarget.SplitterDistance = 481;
this.spltSourceTarget.SplitterWidth = 6;
this.spltSourceTarget.TabIndex = 45;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(2, 8);
this.label1.Location = new System.Drawing.Point(3, 12);
this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(41, 13);
this.label1.Size = new System.Drawing.Size(60, 20);
this.label1.TabIndex = 39;
this.label1.Text = "Source";
//
@ -131,9 +135,10 @@
| System.Windows.Forms.AnchorStyles.Right)));
this.txtSource.BackColor = System.Drawing.SystemColors.Control;
this.txtSource.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.txtSource.Location = new System.Drawing.Point(49, 7);
this.txtSource.Location = new System.Drawing.Point(74, 11);
this.txtSource.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.txtSource.Name = "txtSource";
this.txtSource.Size = new System.Drawing.Size(269, 20);
this.txtSource.Size = new System.Drawing.Size(401, 26);
this.txtSource.TabIndex = 41;
this.txtSource.KeyDown += new System.Windows.Forms.KeyEventHandler(this.txt_KeyDown);
//
@ -143,9 +148,10 @@
| System.Windows.Forms.AnchorStyles.Right)));
this.txtTarget.BackColor = System.Drawing.SystemColors.Control;
this.txtTarget.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.txtTarget.Location = new System.Drawing.Point(45, 7);
this.txtTarget.Location = new System.Drawing.Point(68, 11);
this.txtTarget.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.txtTarget.Name = "txtTarget";
this.txtTarget.Size = new System.Drawing.Size(272, 20);
this.txtTarget.Size = new System.Drawing.Size(408, 26);
this.txtTarget.TabIndex = 42;
this.txtTarget.KeyDown += new System.Windows.Forms.KeyEventHandler(this.txt_KeyDown);
//
@ -154,9 +160,10 @@
this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(3, 8);
this.label2.Location = new System.Drawing.Point(4, 12);
this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(38, 13);
this.label2.Size = new System.Drawing.Size(55, 20);
this.label2.TabIndex = 40;
this.label2.Text = "Target";
//
@ -173,7 +180,8 @@
this.btnReportDifferences});
this.toolStrip1.Location = new System.Drawing.Point(0, 0);
this.toolStrip1.Name = "toolStrip1";
this.toolStrip1.Size = new System.Drawing.Size(653, 25);
this.toolStrip1.Padding = new System.Windows.Forms.Padding(0, 0, 2, 0);
this.toolStrip1.Size = new System.Drawing.Size(980, 25);
this.toolStrip1.TabIndex = 46;
this.toolStrip1.Text = "toolStrip1";
//
@ -325,14 +333,15 @@
this.btnReportDifferences.Image = ((System.Drawing.Image)(resources.GetObject("btnReportDifferences.Image")));
this.btnReportDifferences.ImageTransparentColor = System.Drawing.Color.Magenta;
this.btnReportDifferences.Name = "btnReportDifferences";
this.btnReportDifferences.Size = new System.Drawing.Size(124, 20);
this.btnReportDifferences.Size = new System.Drawing.Size(124, 22);
this.btnReportDifferences.Text = "Report Differences";
this.btnReportDifferences.Click += new System.EventHandler(this.btnReportDifferences_Click);
//
// scDifferenceResults
//
this.scDifferenceResults.Dock = System.Windows.Forms.DockStyle.Fill;
this.scDifferenceResults.Location = new System.Drawing.Point(0, 55);
this.scDifferenceResults.Location = new System.Drawing.Point(0, 85);
this.scDifferenceResults.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.scDifferenceResults.Name = "scDifferenceResults";
this.scDifferenceResults.Orientation = System.Windows.Forms.Orientation.Horizontal;
//
@ -344,75 +353,11 @@
// scDifferenceResults.Panel2
//
this.scDifferenceResults.Panel2.Controls.Add(this.scObjectDefinitions);
this.scDifferenceResults.Size = new System.Drawing.Size(653, 510);
this.scDifferenceResults.SplitterDistance = 371;
this.scDifferenceResults.Size = new System.Drawing.Size(980, 784);
this.scDifferenceResults.SplitterDistance = 570;
this.scDifferenceResults.SplitterWidth = 6;
this.scDifferenceResults.TabIndex = 2;
//
// pnlProgressBar
//
this.pnlProgressBar.BackColor = System.Drawing.SystemColors.Control;
this.pnlProgressBar.Controls.Add(this.progressBar);
this.pnlProgressBar.Controls.Add(this.lblProgressBar);
this.pnlProgressBar.Location = new System.Drawing.Point(60, 21);
this.pnlProgressBar.Name = "pnlProgressBar";
this.pnlProgressBar.Size = new System.Drawing.Size(280, 54);
this.pnlProgressBar.TabIndex = 1;
this.pnlProgressBar.Visible = false;
//
// progressBar
//
this.progressBar.Location = new System.Drawing.Point(120, 16);
this.progressBar.Name = "progressBar";
this.progressBar.Size = new System.Drawing.Size(137, 18);
this.progressBar.Step = 1;
this.progressBar.TabIndex = 1;
//
// lblProgressBar
//
this.lblProgressBar.AutoSize = true;
this.lblProgressBar.Location = new System.Drawing.Point(12, 16);
this.lblProgressBar.Name = "lblProgressBar";
this.lblProgressBar.Size = new System.Drawing.Size(101, 13);
this.lblProgressBar.TabIndex = 0;
this.lblProgressBar.Text = "Generating report ...";
//
// treeGridComparisonResults
//
this.treeGridComparisonResults.AllowUserToAddRows = false;
this.treeGridComparisonResults.AllowUserToDeleteRows = false;
this.treeGridComparisonResults.AllowUserToResizeRows = false;
this.treeGridComparisonResults.AutoSizeRowsMode = System.Windows.Forms.DataGridViewAutoSizeRowsMode.AllCells;
dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Control;
dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.WindowText;
dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
this.treeGridComparisonResults.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1;
this.treeGridComparisonResults.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.treeGridComparisonResults.Comparison = null;
dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
dataGridViewCellStyle2.BackColor = System.Drawing.SystemColors.Window;
dataGridViewCellStyle2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.ControlText;
dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight;
dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
this.treeGridComparisonResults.DefaultCellStyle = dataGridViewCellStyle2;
this.treeGridComparisonResults.Dock = System.Windows.Forms.DockStyle.Fill;
this.treeGridComparisonResults.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
this.treeGridComparisonResults.ImageList = this.TreeGridImageList;
this.treeGridComparisonResults.Location = new System.Drawing.Point(0, 0);
this.treeGridComparisonResults.Name = "treeGridComparisonResults";
this.treeGridComparisonResults.RowHeadersVisible = false;
this.treeGridComparisonResults.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.CellSelect;
this.treeGridComparisonResults.Size = new System.Drawing.Size(653, 371);
this.treeGridComparisonResults.TabIndex = 0;
this.treeGridComparisonResults.Unloading = false;
this.treeGridComparisonResults.DataError += new System.Windows.Forms.DataGridViewDataErrorEventHandler(this.treeGridComparisonResults_DataError);
this.treeGridComparisonResults.MouseUp += new System.Windows.Forms.MouseEventHandler(this.treeGridComparisonResults_MouseUp);
//
// TreeGridImageList
//
this.TreeGridImageList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("TreeGridImageList.ImageStream")));
@ -445,6 +390,7 @@
//
this.scObjectDefinitions.Dock = System.Windows.Forms.DockStyle.Fill;
this.scObjectDefinitions.Location = new System.Drawing.Point(0, 0);
this.scObjectDefinitions.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.scObjectDefinitions.Name = "scObjectDefinitions";
//
// scObjectDefinitions.Panel1
@ -458,10 +404,93 @@
this.scObjectDefinitions.Panel2.BackColor = System.Drawing.SystemColors.Control;
this.scObjectDefinitions.Panel2.Controls.Add(this.txtTargetObjectDefinition);
this.scObjectDefinitions.Panel2.Controls.Add(this.label5);
this.scObjectDefinitions.Size = new System.Drawing.Size(653, 135);
this.scObjectDefinitions.SplitterDistance = 331;
this.scObjectDefinitions.Size = new System.Drawing.Size(980, 208);
this.scObjectDefinitions.SplitterDistance = 496;
this.scObjectDefinitions.SplitterWidth = 6;
this.scObjectDefinitions.TabIndex = 0;
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(6, 2);
this.label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(181, 20);
this.label4.TabIndex = 0;
this.label4.Text = "Source Object Definition";
//
// label5
//
this.label5.AutoSize = true;
this.label5.Location = new System.Drawing.Point(4, 2);
this.label5.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(176, 20);
this.label5.TabIndex = 1;
this.label5.Text = "Target Object Definition";
//
// progressBar
//
this.progressBar.Name = "progressBar";
this.progressBar.Size = new System.Drawing.Size(150, 16);
//
// pnlProgressBar
//
this.pnlProgressBar.ImageScalingSize = new System.Drawing.Size(36, 36);
this.pnlProgressBar.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.toolStripStatusLabel1,
this.progressBar});
this.pnlProgressBar.Location = new System.Drawing.Point(0, 548);
this.pnlProgressBar.Name = "pnlProgressBar";
this.pnlProgressBar.Padding = new System.Windows.Forms.Padding(1, 0, 21, 0);
this.pnlProgressBar.Size = new System.Drawing.Size(980, 22);
this.pnlProgressBar.TabIndex = 49;
this.pnlProgressBar.Text = "Comparison Status";
this.pnlProgressBar.Visible = false;
//
// toolStripStatusLabel1
//
this.toolStripStatusLabel1.Name = "toolStripStatusLabel1";
this.toolStripStatusLabel1.Size = new System.Drawing.Size(0, 17);
//
// treeGridComparisonResults
//
this.treeGridComparisonResults.AllowUserToAddRows = false;
this.treeGridComparisonResults.AllowUserToDeleteRows = false;
this.treeGridComparisonResults.AllowUserToResizeRows = false;
this.treeGridComparisonResults.AutoSizeRowsMode = System.Windows.Forms.DataGridViewAutoSizeRowsMode.AllCells;
dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Control;
dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.WindowText;
dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
this.treeGridComparisonResults.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1;
this.treeGridComparisonResults.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.treeGridComparisonResults.Comparison = null;
dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
dataGridViewCellStyle2.BackColor = System.Drawing.SystemColors.Window;
dataGridViewCellStyle2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.ControlText;
dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight;
dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
this.treeGridComparisonResults.DefaultCellStyle = dataGridViewCellStyle2;
this.treeGridComparisonResults.Dock = System.Windows.Forms.DockStyle.Fill;
this.treeGridComparisonResults.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
this.treeGridComparisonResults.ImageList = this.TreeGridImageList;
this.treeGridComparisonResults.Location = new System.Drawing.Point(0, 0);
this.treeGridComparisonResults.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.treeGridComparisonResults.Name = "treeGridComparisonResults";
this.treeGridComparisonResults.RowHeadersVisible = false;
this.treeGridComparisonResults.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.CellSelect;
this.treeGridComparisonResults.Size = new System.Drawing.Size(980, 570);
this.treeGridComparisonResults.TabIndex = 0;
this.treeGridComparisonResults.Unloading = false;
this.treeGridComparisonResults.DataError += new System.Windows.Forms.DataGridViewDataErrorEventHandler(this.treeGridComparisonResults_DataError);
this.treeGridComparisonResults.MouseUp += new System.Windows.Forms.MouseEventHandler(this.treeGridComparisonResults_MouseUp);
//
// txtSourceObjectDefinition
//
this.txtSourceObjectDefinition.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
@ -469,25 +498,17 @@
| System.Windows.Forms.AnchorStyles.Right)));
this.txtSourceObjectDefinition.BackColor = System.Drawing.Color.White;
this.txtSourceObjectDefinition.Font = new System.Drawing.Font("Consolas", 9.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.txtSourceObjectDefinition.Location = new System.Drawing.Point(0, 16);
this.txtSourceObjectDefinition.Location = new System.Drawing.Point(0, 25);
this.txtSourceObjectDefinition.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.txtSourceObjectDefinition.Name = "txtSourceObjectDefinition";
this.txtSourceObjectDefinition.ReadOnly = true;
this.txtSourceObjectDefinition.Size = new System.Drawing.Size(331, 119);
this.txtSourceObjectDefinition.Size = new System.Drawing.Size(494, 181);
this.txtSourceObjectDefinition.TabIndex = 1;
this.txtSourceObjectDefinition.Text = "";
this.txtSourceObjectDefinition.WordWrap = false;
this.txtSourceObjectDefinition.vScroll += new BismNormalizer.TabularCompare.UI.SynchronizedScrollRichTextBox.vScrollEventHandler(this.txtSourceObjectDefinition_vScroll);
this.txtSourceObjectDefinition.KeyUp += new System.Windows.Forms.KeyEventHandler(this.txtSourceObjectDefinition_KeyUp);
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(4, 1);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(122, 13);
this.label4.TabIndex = 0;
this.label4.Text = "Source Object Definition";
//
// txtTargetObjectDefinition
//
this.txtTargetObjectDefinition.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
@ -495,33 +516,26 @@
| System.Windows.Forms.AnchorStyles.Right)));
this.txtTargetObjectDefinition.BackColor = System.Drawing.Color.White;
this.txtTargetObjectDefinition.Font = new System.Drawing.Font("Consolas", 9.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.txtTargetObjectDefinition.Location = new System.Drawing.Point(0, 16);
this.txtTargetObjectDefinition.Location = new System.Drawing.Point(0, 25);
this.txtTargetObjectDefinition.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.txtTargetObjectDefinition.Name = "txtTargetObjectDefinition";
this.txtTargetObjectDefinition.ReadOnly = true;
this.txtTargetObjectDefinition.Size = new System.Drawing.Size(313, 119);
this.txtTargetObjectDefinition.Size = new System.Drawing.Size(469, 181);
this.txtTargetObjectDefinition.TabIndex = 2;
this.txtTargetObjectDefinition.Text = "";
this.txtTargetObjectDefinition.WordWrap = false;
this.txtTargetObjectDefinition.vScroll += new BismNormalizer.TabularCompare.UI.SynchronizedScrollRichTextBox.vScrollEventHandler(this.txtTargetObjectDefinition_vScroll);
this.txtTargetObjectDefinition.KeyUp += new System.Windows.Forms.KeyEventHandler(this.txtTargetObjectDefinition_KeyUp);
//
// label5
//
this.label5.AutoSize = true;
this.label5.Location = new System.Drawing.Point(3, 1);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(119, 13);
this.label5.TabIndex = 1;
this.label5.Text = "Target Object Definition";
//
// ComparisonControl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.scDifferenceResults);
this.Controls.Add(this.pnlHeader);
this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.Name = "ComparisonControl";
this.Size = new System.Drawing.Size(653, 565);
this.Size = new System.Drawing.Size(980, 869);
this.Load += new System.EventHandler(this.BismNormalizer_Load);
this.pnlHeader.ResumeLayout(false);
this.pnlHeader.PerformLayout();
@ -534,18 +548,19 @@
this.toolStrip1.ResumeLayout(false);
this.toolStrip1.PerformLayout();
this.scDifferenceResults.Panel1.ResumeLayout(false);
this.scDifferenceResults.Panel1.PerformLayout();
this.scDifferenceResults.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.scDifferenceResults)).EndInit();
this.scDifferenceResults.ResumeLayout(false);
this.pnlProgressBar.ResumeLayout(false);
this.pnlProgressBar.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.treeGridComparisonResults)).EndInit();
this.scObjectDefinitions.Panel1.ResumeLayout(false);
this.scObjectDefinitions.Panel1.PerformLayout();
this.scObjectDefinitions.Panel2.ResumeLayout(false);
this.scObjectDefinitions.Panel2.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.scObjectDefinitions)).EndInit();
this.scObjectDefinitions.ResumeLayout(false);
this.pnlProgressBar.ResumeLayout(false);
this.pnlProgressBar.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.treeGridComparisonResults)).EndInit();
this.ResumeLayout(false);
}
@ -559,9 +574,6 @@
private System.Windows.Forms.Label label4;
private SynchronizedScrollRichTextBox txtTargetObjectDefinition;
private System.Windows.Forms.Label label5;
private System.Windows.Forms.Panel pnlProgressBar;
private System.Windows.Forms.ProgressBar progressBar;
private System.Windows.Forms.Label lblProgressBar;
private System.Windows.Forms.Panel pnlHeader;
public System.Windows.Forms.ImageList TreeGridImageList;
private System.Windows.Forms.SplitContainer spltSourceTarget;
@ -588,5 +600,8 @@
private System.Windows.Forms.ToolStripButton btnOptions;
private System.Windows.Forms.ToolStripButton btnReportDifferences;
private System.Windows.Forms.ToolStripMenuItem hideSkipObjectsWithSameDefinitionToolStripMenuItem;
private System.Windows.Forms.StatusStrip pnlProgressBar;
private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel1;
private System.Windows.Forms.ToolStripProgressBar progressBar;
}
}

View File

@ -131,7 +131,7 @@ namespace BismNormalizer.TabularCompare.UI
//Blank file not saved to yet
return;
}
_comparisonInfo = ComparisonInfo.DeserializeBsmnFile(fileName);
_comparisonInfo = ComparisonInfo.DeserializeBsmnFile(fileName, "BISM Normalizer");
PopulateSourceTargetTextBoxes();
}

View File

@ -194,6 +194,9 @@
3gAAAABJRU5ErkJggg==
</value>
</data>
<metadata name="pnlProgressBar.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>405, 17</value>
</metadata>
<metadata name="TreeGridImageList.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
@ -201,354 +204,353 @@
<value>
AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w
LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAAA4
UQAAAk1TRnQBSQFMAgEBFwEAAXwBAgF8AQIBEAEAARABAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFA
AwABYAMAAQEBAAEgBgABYP8AKwADnwH/A58B/wOfAf8DnwH/ZAADbgG7A4AB/4QAA58B/wOfAf8DnwH/
A58B/zAAAwcBCgNWAcEDLgFHEAADNAFUA1YBuwM6AWEQAAMHAQkDYAGZgAADoQH/A58B/wOfAf8DoQH/
NwAB/wNTAa0QAANYAcADDAH8Az0BaRQAA4AB/wgAA4AB/wMUARoEAANVAYADUAF3ZAADoQH/A58B/wOf
Af8DnwH/GAADEgEZAyoBQRQAA0wBkQMAAf8MAAMEAQUDAAH/A0cBggMGAQgUAAN4Ad4DFAEaBAADUAF3
A0wBbwQAA34B8wQAAzABQGAAA6EB/wOfAf8DnwH/A58B/xgAAzkBYAMAAf8DSwGOEAADIwE0AwAB/wNZ
AcQDWQHSA1sB0ANaAcoDAAH/AxoBJRgAA2gBqgNnAaoMAANzAcwDewHmVAADoQH/A58B/wOfAf8DnwH/
ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADy
UAAAAk1TRnQBSQFMAgEBFwEAAZQBAgGUAQIBEAEAARABAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFA
AwABYAMAAQEBAAEgBgABYP8AKwADnwH/A58B/wOfAf8DnwH/ZAADWwG7A4AB/4QAA58B/wOfAf8DnwH/
A58B/zAAAwcBCgNWAcEDLgFHEAADNAFUA1YBuwM6AWEQAAMHAQkDTgGZgAADoQH/A58B/wOfAf8DoQH/
NwAB/wNTAa0QAANYAcADDwH8Az0BaRQAA4AB/wgAA4AB/wMTARoEAANHAYADQwF3ZAADoQH/A58B/wOf
Af8DnwH/GAADEgEZAyoBQRQAA0wBkQMAAf8MAAMEAQUDAAH/A0cBggMGAQgUAANoAd4DEwEaBAADQwF3
A0ABbwQAA3MB8wQAAyoBQGAAA6EB/wOfAf8DnwH/A58B/xgAAzkBYAMAAf8DSwGOEAADIwE0AwAB/wNZ
AcQDWQHSA1sB0ANaAcoDAAH/AxoBJRgAA1UBqgNVAaoMAANiAcwDbwHmVAADoQH/A58B/wOfAf8DnwH/
A58B/wOhAf8DnwH/A58B/wOfAf8DnwH/A6EB/wOfAf8DnwH/A58B/wgAAzwBZwMAAf8DPwFvCAADWQHJ
AyoBQANZAcQDAAH/A1cBxQNXAcUDAAH/A1kB1xwAA0UBYgOAAf8MAAM9AVUDYAGZVAADnwH/A6EB/wOf
AyoBQANZAcQDAAH/A1cBxQNXAcUDAAH/A1kB1xwAAzoBYgOAAf8MAAM1AVUDTgGZVAADnwH/A6EB/wOf
Af8DnwH/A6EB/wOhAf8DoQH/A58B/wOhAf8DoQH/A6EB/wOfAf8DoQH/A58B/wwAA0kBiQMAAf8EAAM4
AVwDAAH/AwoBDgNEAXkDAAH/CwAB/wNKAY0cAAMUARoDgAH/DAADgAH/A2QBolQAA58B/wOfAf8DoQH/
A58B/wOhAf8DnwH/A6EB/wOfAf8DnwH/A58B/wOfAf8DnwH/A6EB/wOfAf8QAANJAe8DWQHJAx4B+gNI
AYgEAAMfASwDAAH/Ax4BKwMdASoDAAH/Ax0BKSAAA4AB/wMqATgEAANdAZEDgAH/BAADYAGZA3sB6kwA
AVwDAAH/AwoBDgNEAXkDAAH/CwAB/wNKAY0cAAMTARoDgAH/DAADgAH/A1IBolQAA58B/wOfAf8DoQH/
A58B/wOhAf8DnwH/A6EB/wOfAf8DnwH/A58B/wOfAf8DnwH/A6EB/wOfAf8QAANMAe8DWQHJAyEB+gNI
AYgEAAMfASwDAAH/Ax4BKwMdASoDAAH/Ax0BKSAAA4AB/wMmATgEAANNAZEDgAH/BAADTgGZA28B6kwA
A58B/wOhAf8DnwH/A6EB/wOfAf8DoQH/A6EB/wOfAf8DoQH/A58B/wOfAf8DoQH/A58B/wOfAf8QAAMx
AU4DAAH/A1oB1QwAAx4B+gNKAYsDSgGKAygB+CQAA3wB7gNQAXd4AAOfAf8DnwH/A58B/wOfAf8YAAMl
ATcDQgF0AzgBXQM7AWUDAAH/A1MBsAMoAT0DQgF2Aw4BEwNCAXUDAAH/AwAB/wNIAYYgAANJAWsDewHm
A3gB3QNVAYB0AAOfAf8DnwH/A6EB/wOhAf8YAANKAYwDAAH/AwAB/wMAAf4DAAH+AwwB/AMAAf8DAAH/
A1YBuwMMARADAAH/AwAB/wMdASooAAN/Afd4AAOhAf8DnwH/A58B/wOhAf8YAAMEAQUDCQEMAwABAQMK
AQ0DAAH/AzsBZAQAAwUBBwMPARQDBAQGAQgDBwEJLAADfAHueAADoQH/A6EB/wOhAf8DnwH/JAADAgED
AwAB/wM3AVtEAANHAWYDGgEiA3MBzHAAA58B/wOfAf8DnwH/A6EB/3gAAz0BVQNJAWt4AAMBAQL/AN0A
AU4DAAH/A1oB1QwAAyEB+gNKAYsDSgGKAzEB+CQAA3IB7gNDAXd4AAOfAf8DnwH/A58B/wOfAf8YAAMl
ATcDQgF0AzgBXQM7AWUDAAH/A1MBsAMoAT0DQgF2Aw4BEwNCAXUDAAH/AwAB/wNIAYYgAAM+AWsDbwHm
A2kB3QNHAYB0AAOfAf8DnwH/A6EB/wOhAf8YAANKAYwDAAH/AwAB/wMAAf4DAAH+Aw8B/AMAAf8DAAH/
A1YBuwMMARADAAH/AwAB/wMdASooAAN3Afd4AAOhAf8DnwH/A58B/wOhAf8YAAMEAQUDCQEMAwABAQMK
AQ0DAAH/AzsBZAQAAwUBBwMPARQDBAQGAQgDBwEJLAADcgHueAADoQH/A6EB/wOhAf8DnwH/JAADAgED
AwAB/wM3AVtEAAM8AWYDGAEiA2IBzHAAA58B/wOfAf8DnwH/A6EB/3gAAzUBVQM+AWt4AAMBAQL/AN0A
Af4B8wHyAf8B/gHzAfIB/wH+AfMB8gH/Af4B8wHyAf8B/gHzAfIB/wH+AfMB8gH/Af4B8wHyAf8B/gHz
AfIB/wH+AfMB8gH/Af4B8wHyAf8B/gHzAfIB/wH+AfMB8gH/Af4B8wHyAf8B/gHzAfIB/wH+AfMB8gH/
Af4B8wHyAf8EAAMqAf8DKgH/AyoB/wMqAf8DKgH/AyoB/wMqAf8DKgH/EAABwAGVAUkB/wHAAZUBSQH/
hAAB/gHzAfIB/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMd
Af8DHQH/Af4B8wHyAf8EAAMqAf8D+AH/A/gB/wP4Af8D+AH/A/gB/wP4Af8DKgH/DAABwAGVAUkB/wGh
ATwBAAH/AcABlQFJAf8MAAPAAf8DkgH/A+oB/yAAA2MB6APAAf8UAAP+Af8D8QH/A8cB/wOPAf8DgAH/
A6IB/wPsAf8UAAH+AfMB8gH/Ax0B/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5
AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wMdAf8B/gHzAfIB/wQAAyoB/wP4Af8D+AH/
A/gB/wP4Af8D+gH/A/wB/wPQAf8IAAHAAZUBSQH/AaEBPAEAAf8BwAGVAUkB/xAAA5IB/wOSAf8DkgH/
A2MB6BgAA+sB/wOwAf8DDQEREAAD/QH/A9YB/wOVAf8DlQH/A50B/wOWAf8DiQH/A1MB/wPVAf8QAAH+
AfMB8gH/Ax0B/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5
AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wMdAf8B/gHzAfIB/wQAAyoB/wP4Af8D+AH/A/gB/wP8Af8BxgGf
AVkB/wGhATwBAAH/AaEBPAEAAf8BoQE8AQAB/wGzAVwBFQH/AaEBPAEAAf8BwAGVAUkB/xQAA+sB/wOQ
Af4B8wHyAf8EAAMnAf8DJwH/AycB/wMnAf8DJwH/AycB/wMnAf8DJwH/EAABwAGVAUYB/wHAAZUBRgH/
hAAB/gHzAfIB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMa
Af8DGgH/Af4B8wHyAf8EAAMnAf8D+AH/A/gB/wP4Af8D+AH/A/gB/wP4Af8DJwH/DAABwAGVAUYB/wGh
ATkBAAH/AcABlQFGAf8MAAPAAf8DkgH/A+oB/yAAA18B6APAAf8UAAP+Af8D8QH/A8cB/wOPAf8DgAH/
A6IB/wPsAf8UAAH+AfMB8gH/AxoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5
AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wMaAf8B/gHzAfIB/wQAAycB/wP4Af8D+AH/
A/gB/wP4Af8D+gH/A/wB/wPQAf8IAAHAAZUBRgH/AaEBOQEAAf8BwAGVAUYB/xAAA5IB/wOSAf8DkgH/
A18B6BgAA+sB/wOwAf8DDQEREAAD/QH/A9YB/wOVAf8DlQH/A50B/wOWAf8DiQH/A1AB/wPVAf8QAAH+
AfMB8gH/AxoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5
AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wMaAf8B/gHzAfIB/wQAAycB/wP4Af8D+AH/A/gB/wP8Af8BxgGf
AVYB/wGhATkBAAH/AaEBOQEAAf8BoQE5AQAB/wGzAVkBEgH/AaEBOQEAAf8BwAGVAUYB/xQAA+sB/wOQ
Af8DkgH/A5IB/wNSAakMAAMQARYD3wH/A50B/wMxAU0UAAPXAf8DogH/A6IB/wOiAf8DnQH/A5wB/wOc
Af8DnAH/A10B/wPSAf8MAAH+AfMB8gH/Ax0B/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5
AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wMdAf8B/gHzAfIB/wQAAyoB/wP4
Af8D+AH/A/oB/wHGAZ8BWQH/AbcBZAEkAf8B8gHwAesB/wP4Af8B8gHwAesB/wG3AWQBJAH/AbMBXAEV
Af8DnAH/A1oB/wPSAf8MAAH+AfMB8gH/AxoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5
AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wMaAf8B/gHzAfIB/wQAAycB/wP4
Af8D+AH/A/oB/wHGAZ8BVgH/AbcBYQEhAf8B8gHwAesB/wP4Af8B8gHwAesB/wG3AWEBIQH/AbMBWQES
Af8cAANdAcoDkgH/A5AB/wOgAf8DMQFNBAADDQERA8AB/wOSAf8DUgGpFAAD9AH/A64B/wOuAf8DsAH/
A7AB/wOuAf8DqAH/A6IB/wOcAf8DnAH/A1MB/wPsAf8IAAH+AfMB8gH/Ax0B/wH5AuoB/wH5AuoB/wH5
AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wMd
Af8B/gHzAfIB/wQAAyoB/wP4Af8D+AH/A/wB/wGhATwBAAH/AfIB8AHrAf8D+AH/A/gB/wP4Af8B8gHw
AesB/wGhATwBAAH/A8QB/wMqAf8DKgH/FAADVgGrA5AB/wOSAf8DnQH/Az4BawPAAf8DkAH/A2MB6AMD
A7AB/wOuAf8DqAH/A6IB/wOcAf8DnAH/A1AB/wPsAf8IAAH+AfMB8gH/AxoB/wH5AuoB/wH5AuoB/wH5
AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wMa
Af8B/gHzAfIB/wQAAycB/wP4Af8D+AH/A/wB/wGhATkBAAH/AfIB8AHrAf8D+AH/A/gB/wP4Af8B8gHw
AesB/wGhATkBAAH/A8QB/wMnAf8DJwH/FAADVgGrA5AB/wOSAf8DnQH/Az4BawPAAf8DkAH/A18B6AMD
AQQUAAPdAf8DvAH/A8IB/wPGAf8DxwH/A8EB/wO8Af8DrgH/A6IB/wOcAf8DiQH/A6IB/wgAAf4B8wHy
Af8DHQH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/
AfkC6gH/AfkC6gH/AfkC6gH/Ax0B/wH+AfMB8gH/BAADKgH/A/gB/wP4Af8D/QH/AaEBPAEAAf8D+AH/
A/gB/wP4Af8D+AH/A/gB/wGhATwBAAH/A/0B/wP4Af8DKgH/GAADPgFrA50B/wOSAf8DkAH/A5IB/wPr
Af8DGgH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/
AfkC6gH/AfkC6gH/AfkC6gH/AxoB/wH+AfMB8gH/BAADJwH/A/gB/wP4Af8D/QH/AaEBOQEAAf8D+AH/
A/gB/wP4Af8D+AH/A/gB/wGhATkBAAH/A/0B/wP4Af8DJwH/GAADPgFrA50B/wOSAf8DkAH/A5IB/wPr
Af8DAwQEAQUUAAPWAf8DwgH/A/4B/wP+Af8D/gH/A/0B/wP8Af8D/AH/A/wB/wP6Af8DjwH/A4AB/wgA
Af4B8wHyAf8DHQH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/
AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/Ax0B/wH+AfMB8gH/BAADKgH/A/gB/wP4Af8D/AH/AaEBPAEA
Af8B8gHwAesB/wP4Af8D+AH/A/gB/wHyAfAB6wH/AaEBPAEAAf8D/AH/A/gB/wMqAf8cAANcAckDkgH/
Af4B8wHyAf8DGgH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/
AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AxoB/wH+AfMB8gH/BAADJwH/A/gB/wP4Af8D/AH/AaEBOQEA
Af8B8gHwAesB/wP4Af8D+AH/A/gB/wHyAfAB6wH/AaEBOQEAAf8D/AH/A/gB/wMnAf8cAANcAckDkgH/
A5AB/wOdAf8DDQERHAAD1gH/A88N/wP+Af8D/gH/A/0B/wP8Af8D+wH/A48B/wOOAf8IAAH+AfMB8gH/
Ax0B/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5
AuoB/wH5AuoB/wH5AuoB/wMdAf8B/gHzAfIB/wQAAyoB/wMqAf8DKgH/A2QB/wHGAZ8BWQH/AbkBiAE3
Af8B8gHwAesB/wP4Af8B8gHwAesB/wG5AYgBNwH/AcYBnwFZAf8D+gH/A/gB/wMqAf8YAANSAakDkgH/
AxoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5
AuoB/wH5AuoB/wH5AuoB/wMaAf8B/gHzAfIB/wQAAycB/wMnAf8DJwH/A2EB/wHGAZ8BVgH/AbkBiAE0
Af8B8gHwAesB/wP4Af8B8gHwAesB/wG5AYgBNAH/AcYBnwFWAf8D+gH/A/gB/wMnAf8YAANSAakDkgH/
A5IB/wOQAf8DkgH/A1wByRAAAwsBDwgAA+kB/wPZAf8D7AH/A/AB/wPwAf8D7AH/A+EB/wPXAf8DwAH/
A6gB/wOPAf8DxgH/CAAB/gHzAfIB/wMdAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLq
Af8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8DHQH/Af4B8wHyAf8YAAHGAZ8BWQH/
AaEBPAEAAf8BoQE8AQAB/wGhATwBAAH/AcYBnwFZAf8D/AH/A/gB/wP4Af8DKgH/FAADXAHJA5IB/wOQ
A6gB/wOPAf8DxgH/CAAB/gHzAfIB/wMaAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLq
Af8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8DGgH/Af4B8wHyAf8YAAHGAZ8BVgH/
AaEBOQEAAf8BoQE5AQAB/wGhATkBAAH/AcYBnwFWAf8D/AH/A/gB/wP4Af8DJwH/FAADXAHJA5IB/wOQ
Af8DnQH/Az4BawPAAf8DkgH/A0oBjBgAA/cB/wPZAf8D7AH/A/EB/wPwAf8D7AH/A+AB/wPWAf8DwAH/
A6gB/wOcAf8D8AH/CAAB/gHzAfIB/wMdAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLq
Af8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8DHQH/Af4B8wHyAf8cAAPEAf8D/QH/
A/wB/wP6Af8D+AH/A/gB/wP4Af8DKgH/EAAD6gH/A5IB/wOQAf8DkAH/A1IBqQQAAw0BEQPgAf8DoAH/
A6gB/wOcAf8D8AH/CAAB/gHzAfIB/wMaAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLq
Af8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8DGgH/Af4B8wHyAf8cAAPEAf8D/QH/
A/wB/wP6Af8D+AH/A/gB/wP4Af8DJwH/EAAD6gH/A5IB/wOQAf8DkAH/A1IBqQQAAw0BEQPgAf8DoAH/
AzEBTRQAA/4B/wPsAf8D4AH/A/EB/wPxAf8D6gH/A+IB/wPSAf8DwQH/A6gB/wPXAf8D/gH/CAAB/gHz
AfIB/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/
Af4B8wHyAf8cAAMqAf8D+AH/A/gB/wP4Af8D+AH/AyoB/wMqAf8DigH/EAADkgH/A5IB/wOQAf8DYwHo
AfIB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/
Af4B8wHyAf8cAAMnAf8D+AH/A/gB/wP4Af8D+AH/AycB/wMnAf8DigH/EAADkgH/A5IB/wOQAf8DXwHo
EAAD6gH/A7AB/wMNAREUAAP9Af8D7AH/A90B/wPjAf8D5gH/A9wB/wPLAf8DuwH/A9YB/wP9Af8MAAH+
AfMB8gH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMd
Af8B/gHzAfIB/xwAAyoB/wP4Af8D+AH/A/gB/wP4Af8DKgH/A4oB/xQAA8AB/wOSAf8D6gH/GAADYwHo
A8AB/xgAA/0B/wP3Af8D6QH/A9wB/wPVAf8D4gH/A+8B/wP+Af8QAAH+AfMB8gH/Ax0B/wMdAf8DHQH/
Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8B/gHzAfIB/xwAAyoB/wMq
Af8DKgH/AyoB/wMqAf8DigH/EwABAXgAAf4B8wHyAf8B/gHzAfIB/wH+AfMB8gH/Af4B8wHyAf8B/gHz
AfMB8gH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMa
Af8B/gHzAfIB/xwAAycB/wP4Af8D+AH/A/gB/wP4Af8DJwH/A4oB/xQAA8AB/wOSAf8D6gH/GAADXwHo
A8AB/xgAA/0B/wP3Af8D6QH/A9wB/wPVAf8D4gH/A+8B/wP+Af8QAAH+AfMB8gH/AxoB/wMaAf8DGgH/
AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8B/gHzAfIB/xwAAycB/wMn
Af8DJwH/AycB/wMnAf8DigH/EwABAXgAAf4B8wHyAf8B/gHzAfIB/wH+AfMB8gH/Af4B8wHyAf8B/gHz
AfIB/wH+AfMB8gH/Af4B8wHyAf8B/gHzAfIB/wH+AfMB8gH/Af4B8wHyAf8B/gHzAfIB/wH+AfMB8gH/
Af4B8wHyAf8B/gHzAfIB/wH+AfMB8gH/Af4B8wHyAf//AAEAAf8BAAP/AQAD/wEAA/8BAAP/AQAD/wEA
A/8BAAP/AQAD/wEAA/8BAAP/AQAD/wEAA/8BAAP/AQAD/wEAA/8BAAL/AysBQgMrAUIDMwFSAzcBWgM3
AVsDSgGJA0sBjgNKAY0DPQFnAzcBWwM3AVsDNAFUAycBOgMRARcIAAMrAUIDKwFCAysBQgMyAVADNwFa
A0gBgwNLAY4DSgGNA0QBegM3AVsDNwFbFAABJQG5AdMB/wEAAYwBqQH/AQABjAGpAf8BAAGMAakB/wEA
A0gBgwNLAY4DSgGNA0QBegM3AVsDNwFbFAABIgG5AdMB/wEAAYwBqQH/AQABjAGpAf8BAAGMAakB/wEA
AYwBqQH/AQABjAGpAf8BAAGMAakB/wEAAYwBqQH/AQABjAGpAf8BAAGMAakB/wEAAYwBqQH/AQABjAGp
Af8BAAGMAakB/wEAAYwBqQH/AQABjAGpAf8BJQG5AdMB/wEAAbkB0wH/AQABjAGpAf8BAAGMAakB/wEA
Af8BAAGMAakB/wEAAYwBqQH/AQABjAGpAf8BIgG5AdMB/wEAAbkB0wH/AQABjAGpAf8BAAGMAakB/wEA
AYwBqQH/AQABjAGpAf8BAAGMAakB/wEAAYwBqQH/AQABjAGpAf8BAAGMAakB/wEAAYwBqQH/AQABjAGp
Af8BAAGMAakB/wEAAYwBqQH/AQABjAGpAf8BAAGMAakB/wEAAbkB0wH/AysBQgMdAf8DHQH/Ax0B/wMd
Af8DHQH/Ax0B/wMdAf8DQAFxAx0B/wMdAf8DNAH/A1wB6gNWAbQDPgFrBAADLgFIAyUB/wMlAf8DJQH/
AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AzcBWxQAARQBtAHRAf8BAwHJAekB/wEAAcYB8QH/AQABwgHw
Af8BAAGMAakB/wEAAYwBqQH/AQABjAGpAf8BAAGMAakB/wEAAbkB0wH/AysBQgMaAf8DGgH/AxoB/wMa
Af8DGgH/AxoB/wMaAf8DQAFxAxoB/wMaAf8DMQH/A1wB6gNWAbQDPgFrBAADLgFIAyIB/wMiAf8DIgH/
AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AzcBWxQAAREBtAHRAf8BAAHJAekB/wEAAcYB8QH/AQABwgHw
Af8BAAHBAfAB/wEAAcAB8AH/AQABwQHwAf8BAAHBAfAB/wEAAcEB8AH/AQABwQHwAf8BAAHCAfAB/wEA
AcMB8AH/AQABwwHwAf8BAAHEAfEB/wEAAcMB5AH/ARQBtAHRAf8BAAG0AdEB/wEAAckB6QH/AQABxgHx
AcMB8AH/AQABwwHwAf8BAAHEAfEB/wEAAcMB5AH/AREBtAHRAf8BAAG0AdEB/wEAAckB6QH/AQABxgHx
Af8BAAHCAfAB/wEAAcEB8AH/AQABwAHwAf8BAAHBAfAB/wEAAcEB8AH/AQABwQHwAf8BAAHBAfAB/wEA
AcIB8AH/AQABwwHwAf8BAAHDAfAB/wEAAcQB8QH/AQABwwHkAf8BAAG0AdEB/wMrAUIDHQH/Ax0B/wMd
Af8DHQH/Ax0B/wMdAf8DHQH/A0gBgwMdAf8DHQH/Ax0B/wMdAf8DHQH/A0oBjQM/AW0DNQFVAyUB/wH5
AuoB/wH5AuoB/wH5AuoB/wMlAf8B+QLqAf8B+QLqAf8B+QLqAf8DJQH/AzcBWwM3AVsDNwFbDAABTgHI
AcIB8AH/AQABwwHwAf8BAAHDAfAB/wEAAcQB8QH/AQABwwHkAf8BAAG0AdEB/wMrAUIDGgH/AxoB/wMa
Af8DGgH/AxoB/wMaAf8DGgH/A0gBgwMaAf8DGgH/AxoB/wMaAf8DGgH/A0oBjQM/AW0DNQFVAyIB/wH5
AuoB/wH5AuoB/wH5AuoB/wMiAf8B+QLqAf8B+QLqAf8B+QLqAf8DIgH/AzcBWwM3AVsDNwFbDAABSwHI
AdwB/wEAAa4BzwH/AQABzQHyAf8BAAHIAfEB/wEAAcUB8QH/AQABxAHxAf8BAAHDAfAB/wMAAf8DAAH/
AQABxgHxAf8BAAHIAfEB/wEAAcoB8gH/AQABygHyAf8BAAHMAfEB/wEAAasBzQH/AU4ByAHcAv8BAAL/
AQABxgHxAf8BAAHIAfEB/wEAAcoB8gH/AQABygHyAf8BAAHMAfEB/wEAAasBzQH/AUsByAHcAv8BAAL/
AQABrgHPAf8BAAHNAfIB/wEAAcgB8QH/AQABxQHxAf8BAAHEAfEB/wEAAcMB8AH/AwAB/wMAAf8BAAHG
AfEB/wEAAcgB8QH/AQABygHyAf8BAAHKAfIB/wEAAcwB8QH/AQABqwHNAv8BAAL/AysBQgMdAf8DHQH/
Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DTAGTAx0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/AzcBWwM2AVgDJQH/
AfkC6gH/AfkC6gH/AfkC6gH/AyUB/wH5AuoB/wH5AuoB/wH5AuoB/wMlAf8DNwFbAyUB/wNAAW4MAAHi
Ae4B8gH/ARQBtAHRAf8BBAHGAeQB/wEEAdEB8wH/AQABzQHyAf8BAAHLAfIB/wEAAcoB8gH/AwAB/wMA
Af8BAAHNAfMB/wEEAdEB8wH/AQUB0gH0Af8BBgHTAfQB/wEDAcUB4wH/ARQBtAHRAf8B4gHuAfIC/wEA
AfEB/wEAAcgB8QH/AQABygHyAf8BAAHKAfIB/wEAAcwB8QH/AQABqwHNAv8BAAL/AysBQgMaAf8DGgH/
AxoB/wMaAf8DGgH/AxoB/wMaAf8DTAGTAxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AzcBWwM2AVgDIgH/
AfkC6gH/AfkC6gH/AfkC6gH/AyIB/wH5AuoB/wH5AuoB/wH5AuoB/wMiAf8DNwFbAyIB/wNAAW4MAAHi
Ae4B8gH/AREBtAHRAf8BAQHGAeQB/wEBAdEB8wH/AQABzQHyAf8BAAHLAfIB/wEAAcoB8gH/AwAB/wMA
Af8BAAHNAfMB/wEBAdEB8wH/AQIB0gH0Af8BAwHTAfQB/wEAAcUB4wH/AREBtAHRAf8B4gHuAfIC/wEA
Av8BAAG0AdEB/wEAAcYB5AH/AQAB0QHzAf8BAAHNAfIB/wEAAcsB8gH/AQABygHyAf8DAAH/AwAB/wEA
Ac0B8wH/AQAB0QHzAf8BAAHSAfQB/wEAAdMB9AH/AQABxQHjAf8BAAG0AdEC/wEAAv8DNQFVAx0B/wMd
Af8DHQH/A2IB6QMdAf8DHQH/Ax0B/wNRAZwDHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DNwFbAzIBUAMl
Af8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wM3AVsDJQH/A0oBihAAAYABzQHfAf8BAAGs
Ac0B/wESAdsB9AH/AQ4B2QH1Af8BCwHXAfUB/wEKAdYB9AH/AQoB1gH0Af8BDAHXAfUB/wEOAdkB9QH/
AQ4B2QH1Af8BEAHbAfUB/wERAdoB8wH/AQABqgHLAf8BgAHNAd8B/wQAAf8BAAP/AQAC/wEAAawBzQH/
Ac0B8wH/AQAB0QHzAf8BAAHSAfQB/wEAAdMB9AH/AQABxQHjAf8BAAG0AdEC/wEAAv8DNQFVAxoB/wMa
Af8DGgH/A2IB6QMaAf8DGgH/AxoB/wNRAZwDGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DNwFbAzIBUAMi
Af8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wM3AVsDIgH/A0oBihAAAYABzQHfAf8BAAGs
Ac0B/wEPAdsB9AH/AQsB2QH1Af8BCAHXAfUB/wEHAdYB9AH/AQcB1gH0Af8BCQHXAfUB/wELAdkB9QH/
AQsB2QH1Af8BDQHbAfUB/wEOAdoB8wH/AQABqgHLAf8BgAHNAd8B/wQAAf8BAAP/AQAC/wEAAawBzQH/
AQAB2wH0Af8BAAHZAfUB/wEAAdcB9QH/AQAB1gH0Af8BAAHWAfQB/wEAAdcB9QH/AQAB2QH1Af8BAAHZ
AfUB/wEAAdsB9QH/AQAB2gHzAf8BAAGqAcsC/wEAA/8BAAL/AzYBWAMdAf8DHQH/A10B0wNUAaYDXQHT
Ax0B/wMdAf8DWgG9Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/AzcBWwMxAU4DNwFaA0ABbgNKAY0DUgGk
A1wB6gNiAe8DYgHvAV8CXgHtA1wBzANIAYUDJQH/A0gBhQM3AVsDNwFbAzcBWwQAAeoB8gH1Af8BFAG0
AdEB/wEJAcYB3wH/AR0B5gH4Af8BHAHkAfcB/wEbAeMB9wH/AQABRgFPAf8BAAFGAU8B/wEZAeIB9wH/
ARkB4gH3Af8BGgHjAfcB/wEIAccB4AH/ARQBtAHRAf8B6gHyAfUB/wQAAf8BAAP/AQAC/wEAAbQB0QH/
AQABxgHfAf8BAAHmAfgB/wEAAeQB9wH/AQAB4wH3Af8BAAEXASAB/wEAARcBIAH/AQAB4gH3Af8BAAHi
AfcB/wEAAeMB9wH/AQABxwHgAf8BAAG0AdEC/wEAA/8BAAL/Ay4BRwMzAVIDPQFnA0YBfwNOAZUDUgGj
AWICYAH1AXsCeQH6AXsCeQH6Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/AzcBWwMAAQEDOQFfAyUB/wNO
AZQDUgGnAyUB/wGKAoMB+gGIAoYB+QMlAf8BggGBAYAB9wNbAcgDJQH/A0gBhQMlAf8DJQH/AzcBWwgA
AY4B0wHjAf8BAAGqAcsB/wElAeoB9gH/ASkB8AH6Af8BJwHuAfkB/wMAAf8DAAH/ASMB6gH5Af8BIwHq
AfkB/wEeAeUB9QH/AQABqgHLAf8BjgHTAeMB/wgAAf8BAAP/AQAD/wEAAv8BAAGqAcsB/wEAAeoB9gH/
AQAB8AH6Af8BAAHuAfkB/wMAAf8DAAH/AQAB6gH5Af8BAAHqAfkB/wEAAeUB9QH/AQABqgHLAv8BAAP/
AQAD/wEAAv8EAAMgAS8DTAGSAygB/wMdAf8DKAH/AXEBbwFuAfoDPwH/Ax0B/wMdAf8DHQH/Ax0B/wMd
Af8DHQH/Ax0B/wM4AV4EAAM5AV8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/
A0gBhQNOAZUDJQH/AzcBWwgAAe8B9AH2Af8BFAG0AdEB/wEHAcIB2wH/AS0B8wH7Af8BLAHxAfoB/wMA
Af8DAAH/ASgB7wH6Af8BKAHvAfoB/wEJAcYB3wH/ARQBtAHRAf8B7wH0AfYB/wgAAf8BAAP/AQAD/wEA
Av8BAAG0AdEB/wEAAcIB2wH/AQAB8wH7Af8BAAHxAfoB/wMAAf8DAAH/AQAB7wH6Af8BAAHvAfoB/wEA
AcYB3wH/AQABtAHRAv8BAAP/AQAD/wEAAv8EAAM4AVwDKAH/Ax0B/wMdAf8DHQH/AygB/wNfAegDHQH/
Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/A0oBiwQAAzkBXwMlAf8B+QLqAf8B+QLqAf8DJQH/AfkC6gH/
AfkC6gH/AyUB/wH5AuoB/wH5AuoB/wMlAf8DXgHZAyUB/wMlAf8DNwFbDAABnAHXAeUB/wEAAakBywH/
AScB6gH0Af8BLwH0AfsB/wMAAf8DAAH/AS0B8gH6Af8BJgHqAfYB/wEAAakBywH/AZwB1wHlAf8MAAH/
AQAD/wEAA/8BAAP/AQAC/wEAAakBywH/AQAB6gH0Af8BAAH0AfsB/wMAAf8DAAH/AQAB8gH6Af8BAAHq
AfYB/wEAAakBywL/AQAD/wEAA/8BAAP/AQAC/wQAAzgBXAMdAf8DHQH/Ax0B/wMdAf8DHQH/A1QBrwMd
Af8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DRAF7BAADOQFfAyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/
AyUB/wMlAf8DJQH/AyUB/wNiAekDYgHpAyUB/wNIAYYQAAEUAbQB0QH/AQQBvwHZAf8BOQH2AfsB/wMA
Af8DAAH/ATAB9QH7Af8BCwHGAd0B/wEUAbQB0QH/AfUB9gH3Af8MAAH/AQAD/wEAA/8BAAP/AQAC/wEA
AbQB0QH/AQABvwHZAf8BCgH2AfsB/wMAAf8DAAH/AQEB9QH7Af8BAAHGAd0B/wEAAbQB0QL/AQAD/wEA
A/8BAAP/AQAC/wQAAzgBXAMoAf8DHQH/Ax0B/wMdAf8DKAH/A1YBqwNeAdADXAHqA1wB6gNcAeoDXAHq
AygB/wMdAf8DNwFbBAADOQFfAyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wNh
AdwDJQH/AyUB/wM9AWcQAAGrAd0B6QH/AQABqQHKAf8BJgHoAfMB/wEwAfUB+wH/ATAB9QH7Af8BKQHt
AfYB/wEAAakBywH/AasB3QHpAf8QAAH/AQAD/wEAA/8BAAP/AQAD/wEAAv8BAAGpAcoB/wEAAegB8wH/
AQEB9QH7Af8BAQH1AfsB/wEAAe0B9gH/AQABqQHLAv8BAAP/AQAD/wEAA/8BAAP/AQAC/wQAAyABLwNM
AZIDKAH/Ax0B/wMoAf8DVAGvA1YBqwNVAa0DXwHoA1wB6gNcAeoDXAHqA1wB6gMdAf8DNwFbBAADQwF4
AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wNOAZUDXQHTAyUB/wM3AVsUAAEV
AbQB0QH/AQIBuwHWAf8BMAH1AfsB/wEwAfUB+wH/AQoBxgHdAf8BFQG0AdEB/xQAAf8BAAP/AQAD/wEA
A/8BAAP/AQAC/wEAAbQB0QH/AQABuwHWAf8BAQH1AfsB/wEBAfUB+wH/AQABxgHdAf8BAAG0AdEC/wEA
A/8BAAP/AQAD/wEAA/8BAAL/CAADSwGOA0wBkgNXAboDVgGrA1YBqwNWAasDVgGrA2EB5ANcAeoDXAHq
A1wB6gNcAeoDHQH/AzcBWwQAA0kBhwM+AWsDNwFbAzcBWwM3AVsDOgFiA1ABngNSAaMDUgGjA1IBowNS
AaMDTgGVAyUB/wMlAf8DNwFbFAABuwHiAe0B/wEAAagBygH/ASMB5QHxAf8BKQHsAfUB/wEAAakBywH/
AbsB4gHtAf8UAAH/AQAD/wEAA/8BAAP/AQAD/wEAA/8BAAL/AQABqAHKAf8BAAHlAfEB/wEAAewB9QH/
AQABqQHLAv8BAAP/AQAD/wEAA/8BAAP/AQAD/wEAAv8IAAM+AWsDSgGMAygB/wNWAbEDWAG5A1YBqwNW
AasDYQHhA1wB6gNcAeoDXAHqAygB/wNKAYwDLgFIEAADNwFbAyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/
AyUB/wMlAf8DJQH/AyUB/wM2AVkYAAEVAbQB0QH/AQABuQHVAf8BCgHGAd0B/wEVAbQB0QH/GAAB/wEA
A/8BAAP/AQAD/wEAA/8BAAP/AQAC/wEAAbQB0QH/AQABuQHVAf8BAAHGAd0B/wEAAbQB0QL/AQAD/wEA
A/8BAAP/AQAD/wEAA/8BAAL/DAADPQFoA1YBtANcAeoDNAH/Ax0B/wMdAf8DHQH/Ax0B/wM0Af8DXAHq
A1cBugM+AWsUAAM3AVsDJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AzQBVBgA
AckB6QHxAf8BBQGvAc4B/wEFAa8BzgH/AckB6QHxAf8YAAH/AQAD/wEAA/8BAAP/AQAD/wEAA/8BAAP/
AQAC/wEAAa8BzgH/AQABrwHOAv8BAAP/AQAD/wEAA/8BAAP/AQAD/wEAA/8BAAL/EAADDQERAycBOgMz
AVMDNwFbAzsBZQNKAY0DSwGOA0gBhAM4AV4DEgEZGAADNwFbAzcBWwNJAYcDSwGOA0sBjgNKAY0DRQF8
AzcBWwM3AVsDNwFbAzUBVwM3AVr/AEEAAyoBQAMqAUADKgFAAyoBQAMqAUADKgFAAyoBQAMqAUADKgFA
AyoBQAMqAUADKgFAAyoBQAMmATgDDAEQBAADKgFAAyoBQAMqAUADKgFAAyoBQAMqAUADKgFAAyoBQAMq
AUADKgFAAyoBQAMqAUADKgFAAyYBOAMMARAUAAHWAb4BrQH/AbUBmgGEAf8BnAEfAQQB/wGUARMBAAH/
AZQBEwEAAf8BpQEnARUB/wG9AaYBlAH/AdYBvgG1Af8gAAH3AfMB7wH/AdYBvgG1Af8BrQGOAVsB/wGU
AUkBMgH/AYwBQQEqAf8BlAFVAUMB/wHGAbYBrQH/FAADXAHfA1wB3wNcAd8DXAHfA1wB3wNcAd8DXAHf
A1wB3wNcAd8DXAHfA1wB3wNcAd8DXAHfA1sBwwMmATgEAANcAd8DXAHfA1wB3wNcAd8DXAHfA1wB3wNc
Ad8DXAHfA1wB3wNcAd8DXAHfA1wB3wNcAd8DWwHDAyYBOBAAAcYBrgGcAf8BrQGGARUB/wGtAZIBJQH/
AdYBzwHOAf8B5wHzAfcB/wHnAe8B9wH/AcYBwwG9Af8BpQGGAR0B/wGlASMBDQH/AcYBrgGcAf8YAAH3
AesB5wH/Ab0BmgGMAf8BnAFNATIB/wGcAU0BMgH/AZwBUQE6Af8BnAFNAToB/wGUAUUBMgH/AVsBMQEZ
Af8BrQGaAYwB/xAAAacCpgH/ArMBsgH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/
A7oB/wGxArAB/wNcAd8DKgFABAABpwKmAf8CswGyAf8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/
A7oB/wO6Af8DugH/AbECsAH/A1wB3wMqAUAMAAHOAa4BnAH/AbUBmgGMAf8B7wHrAecF/wH3AfsC/wHv
AfsB9wH/Ae8C9wH/Au8B9wH/AecB8wL/Ab0BtgG1Af8BlAETAQAB/wHGAa4BnAH/FAABvQGaAYwB/wGl
AVUBOgH/AaUBVQE6Af8BpQFVAToB/wGcAVEBOgH/AZwBUQE6Af8BnAFRAToB/wGcAVEBOgH/AYQBNQEi
Af8BrQGWAYwB/wwAA5EB/wPEAf8D4wH/A+MB/wPjAf8C4QHgAf8B2AHUAdIB/wHaAdYB1QH/A+MB/wPj
Af8D4wH/A+MB/wO6Af8DXAHfAyoBQAQAA5EB/wPEAf8D4wH/A+MB/wPjAf8D4wH/A+MB/wPjAf8D4wH/
A+MB/wPjAf8D4wH/A7oB/wNcAd8DKgFACAAB1gG6Aa0B/wG1AZIBJQH/A/cJ/wHeAbYBpQH/AaUCAAH/
AaUCAAH/AdYBvgGtAf8B9wP/Ae8B9wL/Ab0BrgGlAf8BnAEXAQAB/wHWAb4BtQH/DAAB5wHHAbUB/wGt
AV0BQwH/Aa0BXQFDAf8BrQFdAUsB/wGtAV0BSwH/Aa0BXQFDAf8BpQFZAUMB/wGlAVUBOgH/AZwBUQE6
Af8BnAFRAToB/wFbATEBGQH/AcYBtgGtAf8IAAORAf8DxAH/A+MB/wPjAf8D4wH/AdkB1AHSAf8BowGI
ASQB/wGuAZcBjAH/A+MB/wPjAf8D4wH/A+MB/wO6Af8DXAHfAyoBQAQAA5EB/wPEAf8D4wH/A+MB/wPj
Af8D4wH/A+MB/wPjAf8D4wH/A+MB/wPjAf8D4wH/A7oB/wNcAd8DKgFACAABvQGeAYQB/wHeAccBvQ3/
Ae8B1wHGAf8BrQIAAf8BpQIAAf8B9wHvAecF/wHvAesB7wH/AecB8wH3Af8BnAEnARUB/wG1AZIBJQH/
DAABzgGiAYwB/wGtAYYBSwH/AbUBigFTAf8BvQGOAVMB/wG9AY4BUwH/AbUBigFTAf8BtQGGAUsB/wGt
AV0BQwH/AaUBVQE6Af8BnAFRAToB/wGUAUUBMgH/AZQBVQFDAf8IAAORAf8BxgLHAf8B5gLnAf8B5gLn
Af8B5gLnAf8B2AHSAc8B/wGPARQBAgH/AZ4BgAEaAf8B5gLnAf8B5gLnAf8B5gLnAf8B5gLnAf8DvAH/
A1wB3wMqAUAEAAORAf8BxgLHAf8B5gLnAf8B5gLnAf8B5gLnAf8B5gLnAf8B5gLnAf8B5gLnAf8B5gLn
Af8B5gLnAf8B5gLnAf8B5gLnAf8DvAH/A1wB3wMqAUAEAAHWAboBrQH/Ab0BmgGEAv8B+w7/Ae8B4wHW
Af8BvQIAAf8BtQEPAQAB/wH3AfsB9wX/AfcB8wH3Af8B7wHzAfcB/wHGAb4BvQH/AZwBHwEEAf8MAAHG
AZoBhAH/Ab0BigFTAv8B+wP/AvcC/wHzAe8B/wH3AesB5wH/AfcB4wHeAf8B9wHjAdYB/wH3AeMB3gH/
AfcB2wHOAf8BnAFJATIB/wGMAUEBKgH/CAADkQH/AcoCywH/AewC7QH/AewC7QH/AewC7QH/Ad0B2AHV
Af8BkQEWAQMB/wGgAYIBHAH/AewC7QH/AewC7QH/AewC7QH/AewC7QH/A78B/wNcAd8DKgFABAADkQH/
AcoCywH/AewC7QH/AewC7QH/AewC7QH/AewC7QH/AewC7QH/AewC7QH/AewC7QH/AewC7QH/AewC7QH/
AewC7QH/A78B/wNcAd8DKgFABAABzgGuAZwB/wHOAaYBjBH/Ae8B3wHWAf8BvQEDAQAB/wG1AQ8BAAH/
AfcC7wn/Ae8B8wH3Af8B1gHbAd4B/wGcAR8BBAH/DAABxgGaAYQB/wHGAZYBWw7/AfsD/wHzAe8B/wH3
AesB5wH/AfcB4wHeAf8B9wHfAdYB/wGcAUkBMgH/AZQBSQEyAf8IAAORAf8D0AH/AegB3gHaAf8ByAGj
AZYB/wHBAZcBiAH/AbsBjwEoAf8BnwEQAQAB/wGkARgBAwH/AcEBlwGIAf8BwQGXAYgB/wHOAa8BowH/
Ae8B6gHnAf8DwwH/A1wB3wMqAUAEAAORAf8BygLLAf8B4QHYAdQB/wHHAaEBlAH/AcEBlwGIAf8BwQGX
AYgB/wHBAZcBiAH/AcEBlwGIAf8BwQGXAYgB/wHBAZcBiAH/AcwBrQGhAf8B5gHiAeEB/wO/Af8DXAHf
AyoBQAQAAc4BpgGUAf8BzgGqAZQR/wHvAdsBzgH/AcYBAwEAAf8BtQELAQAB/wHvAesB5wn/A/cB/wHe
AeMB5wH/AZwBIwENAf8MAAHeAbIBnAH/Ac4BngGEAf8B5wG2AZwB/wHnAb4BrQH/AecBvgGtAf8B5wG2
AZwB/wHWAaYBjAH/Ac4BmgGEAf8BtQGKAUsB/wGlAVkBQwH/AZwBSQEyAf8BrQGOAVsB/wgAA5EB/wPT
Af8B6wHfAdkB/wHEAZcBhwH/AbwBiQEgAf8BuAGDARkB/wGiAQ8BAAH/AacBFQEAAf8BvAGJASAB/wG8
AYkBIAH/AcwBpgGYAf8B9AHtAeoB/wPGAf8DXAHfAyoBQAQAA5EB/wHMAs0B/wHjAdcB0gH/AcMBlgGG
Af8BvAGJASAB/wG8AYkBIAH/AbwBiQEgAf8BvAGJASAB/wG8AYkBIAH/AbwBiQEgAf8ByQGjAZYB/wHp
AeQB4QH/A8EB/wNcAd8DKgFABAABxgGiAYwB/wHOAaoBlBH/Ad4BwwGtAf8BvQEHAQAB/wG9AR8BAAH/
AfcC7wb/AfsC/wH3AfsC/wHeAdcB3gH/AZwBHwEEAf8MAAHvAc8BvQH/Ac4BngGEAf8B5wG2AZwB/wHn
Ab4BrQH/AecBvgGtAf8B5wG2AZwB/wHWAaYBjAH/Ac4BmgGEAf8BtQGKAUsB/wGlAVkBQwH/AZwBUQE6
Af8B1gG+AbUB/wgAA5EB/wPTAf8B+AH2AfUB/wHyAeoB5wH/AfEB6AHkAf8B4gHUAc0B/wGWARcBBAH/
AaUBgwEbAf8B8QHoAeQB/wHxAegB5AH/AfMB7QHqAf8B+gH5AfcB/wPGAf8DXAHfAyoBQAQAA5EB/wPS
Af8B9wH1AfMB/wHyAeoB5wH/AfEB6AHkAf8B8QHoAeQB/wHxAegB5AH/AfEB6AHkAf8B8QHoAeQB/wHx
AegB5AH/AfMB7AHpAf8B+AH3AfYB/wPFAf8DXAHfAyoBQAQAAdYBsgGcAf8BzgGiAYwR/wH3AesB5wH/
Ae8B2wHWAf8B9wHrAecK/wH7Av8B9wP/Ac4BvgG9Af8BpQEnAQ0B/wwAAf8B8wHvAf8B3gG2AaUB/wHW
AaYBjAH/AecBvgGtAf8B7wG+Aa0B/wHnAbIBnAH/AdYBpgGUAf8BxgGWAYQB/wG1AYoBUwH/AaUBWQFD
Af8BvQGaAYwB/wH3Au8B/wgAA5EB/wPUAf8C/AH7Af8C/AH7Af8C/AH7Af8B6wHlAeEB/wGVARoBBwH/
AaYBiAEhAf8C/AH7Af8C/AH7Af8C/AH7Af8C/AH7Af8DxwH/A1wB3wMqAUAEAAORAf8D0wH/AvsB+gH/
AvsB+gH/AvsB+gH/AvsB+gH/AvsB+gH/AvsB+gH/AvsB+gH/AvsB+gH/AvsB+gH/AvsB+gH/A8YB/wNc
Ad8DKgFACAAB1gGmAYwB/wHvAecB3g7/AfsC/wHWAaIBhAH/AdYBogGEAv8B+w7/AaUBJwENAf8BxgGm
AZQB/xAAAfcB6wHnAf8B3gG2AaUB/wHWAaIBjAH/AdYBqgGUAf8B3gGuAZQB/wHOAaIBjAH/Ab0BkgFb
Af8BtQGGAUsB/wG9AZoBjAH/AfcB6wHnAf8MAAORAf8D1AH/A/wB/wP8Af8D/AH/Ae0B6AHlAf8BogGC
ARsB/wGxAZcBiQH/A/wB/wP8Af8D/AH/A/wB/wPHAf8DXAHfAyoBQAQAA5EB/wPTAf8C+wH6Af8C+wH6
Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8DxgH/A1wB3wMq
AUAIAAHWAbYBnAH/AecBsgGUDf8B9wHvAecB/wHGARcBAAH/Ab0BDwEAAf8B9wLvCf8BxgGuAZwB/wGt
AYYBFQH/AdYBwwG1Af8UAAH/Ae8B5wH/Ae8BzwG9Af8B3gGyAZwB/wHOAZ4BjAH/AcYBmgGEAf8B1gGm
AZQB/wHWAboBrQL/AvcB/xAAA5EB/wPUAf8D/AH/A/wB/wP8Af8B+AL2Af8B4gHZAdUB/wHnAd8B2wH/
A/wB/wP8Af8D/AH/A/wB/wPHAf8DXAHfAyoBQAQAA5EB/wPTAf8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6
Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8DxgH/A1wB3wMqAUAMAAHeAbIBlAH/
Ae8BxwGtDv8C9wL/AfcB7wb/AvcB/wHOAbYBpQH/Aa0BhgEVAf8B1gHDAbUB/0gAAqEBoAH/A8EB/wPU
Af8D1AH/A9QB/wPUAf8D1AH/A9QB/wPUAf8D1AH/A9QB/wPUAf8DuwH/A1wB3wMqAUAEAAKhAaAB/wPA
Af8D0wH/A9MB/wPTAf8D0wH/A9MB/wPTAf8D0wH/A9MB/wPTAf8D0wH/A7oB/wNcAd8DKgFAEAAB3gG2
AZwB/wHnAbYBlAH/AfcB3wHGAv8B5wHeAv8B6wHnAf8B9wHnAd4B/wHnAc8BxgH/Ac4BqgGUAf8BvQGa
AYQB/wHWAcMBtQH/TAABvAG7AboB/wKhAaAB/wORAf8DkQH/A5EB/wORAf8DkQH/A5EB/wORAf8DkQH/
A5EB/wORAf8BpwKmAf8DXAHfAyoBQAQAAbwBuwG6Af8CoQGgAf8DkQH/A5EB/wORAf8DkQH/A5EB/wOR
Af8DkQH/A5EB/wORAf8DkQH/AacCpgH/A1wB3wMqAUAUAAHeAb4BrQH/Ad4BtgGcAf8B5wG2AZwB/wHn
AbIBlAH/Ad4BqgGMAf8B1gGuAZwB/wHWAb4BtQH//wDtAAEQAY8BFQH/ARABjwEVAf8BEAGPARUB/wEQ
AY8BFQH/GAAD2gH/A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wNbAcQkAAEWASoBtwH/AQABAgGm
Af8BRgFVAcQB/yAAA18B6AEWASoBtwH/DAAB/wFGATQC/wFGATQB/0gAARABjwEVAf8BEAGPARUB/wEQ
AY8BFQH/ARABjwEVAf8YAAPaAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8D2gH/A2UB9AMBAQIDAAEB
BwABARUAAQIBpgH/AQABAgGmAf8BAAECAaYB/wNfAegYAAFGAVUBxAH/AQYBHAGyAf8DDQERDAAB/wFG
ATQB/wgAAf8BRgE0Af9AAAEQAY8BFQH/ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/GAAD2gH/Ax0B/wMd
Af8DHQH/Ax0B/wMdAf8DHQH/A9oB/wNRAZwEAAMBAQIDBAEFAwABARQAAUYBVQHEAf8BAAECAaYB/wEA
AQIBpgH/AQABAgGmAf8DUgGpDAADEAEWATgBSAG+Af8BAAEPAawB/wMxAU0YAAH/AUYBNAL/AUYBNAL/
AUYBNAH/PAABEAGPARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/xgAA9oB/wMdAf8DHQH/Ax0B/wMd
Af8DHQH/Ax0B/wPaAf8DYAHrA10B0gNZAcIDWwHGA1QBrAMbASYUAANdAcoBAAECAaYB/wEAAQIBpgH/
AQABDwGsAf8DMQFNBAADDQERARYBKgG3Af8BAAECAaYB/wNSAakYAAH/AUYBNAL/AUYBNAL/AUYBNAL/
AUYBNAL/AUYBNAH/OAABEAGPARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/xgAA9oB/wPaAf8D2gH/
A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A0MBdwMAAQEYAANWAasBAAECAaYB/wEA
AQIBpgH/AQABDwGsAf8DPgFrARYBKgG3Af8BAAECAaYB/wNfAegDAwEEHAAB/wFGATQC/wFGATQC/wFG
ATQC/wFGATQC/wFGATQB/yAAARABjwEVAf8BEAGPARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/wEQ
AY8BFQH/ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/ARABjwEV
Af8BEAGPARUB/wQAA9oB/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8D2gH/
AzUBVwMEAQUDDwEUAwABARQAAz4BawEAAQ8BrAH/AQABAgGmAf8BAAECAaYB/wEAAQIBpgH/AUYBVQHE
Af8DAwQEAQUgAAH/AUYBNAL/AUYBNAL/AUYBNAL/AUYBNAL/AUYBNAH/HAABEAGPARUB/wEQAY8BFQH/
ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/ARABjwEVAf8BEAGP
ARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/BAAD2gH/Ax0B/wMdAf8DHQH/Ax0B/wMd
Af8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wPaAf8DCgEOJAADXAHJAQABAgGmAf8BAAECAaYB/wEAAQ8BrAH/
Aw0BESwAAf8BRgE0Av8BRgE0Av8BRgE0Av8BRgE0Av8BRgE0Af8YAAEQAY8BFQH/ARABjwEVAf8BEAGP
ARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/
ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/ARABjwEVAf8EAAPaAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMd
Af8DHQH/Ax0B/wMdAf8DHQH/A9oB/wMvAUoDBQEHAyABLgMsAUMUAANSAakBAAECAaYB/wEAAQIBpgH/
AQABAgGmAf8BAAECAaYB/wNcAckQAAMLAQ8cAAH/AUYBNAL/AUYBNAL/AUYBNAL/AUYBNAL/AUYBNAH/
FAABEAGPARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/wEQ
AY8BFQH/ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/BAAD2gH/
A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/
EAADXAHJAQABAgGmAf8BAAECAaYB/wEAAQ8BrAH/Az4BawEWASoBtwH/AQABAgGmAf8DSgGMMAAB/wFG
ATQC/wFGATQC/wFGATQB/ywAARABjwEVAf8BEAGPARUB/wEQAY8BFQH/ARABjwEVAf8YAAPaAf8DHQH/
Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wPaAf8MAAFG
AVUBxAH/AQABAgGmAf8BAAECAaYB/wEAAQIBpgH/A1IBqQQAAw0BEQE4AUgBvgH/AQABDwGsAf8DMQFN
MAAB/wFGATQB/wgAAf8BRgE0Av8BRgE0Af8gAAEQAY8BFQH/ARABjwEVAf8BEAGPARUB/wEQAY8BFQH/
GAAD2gH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMd
Af8D2gH/DQABAgGmAf8BAAECAaYB/wEAAQIBpgH/A18B6BAAAUYBVQHEAf8BBgEcAbIB/wMNARE0AAH/
AUYBNAL/AUYBNAL/AUYBNAH/IAABEAGPARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/xgAA9oB/wMd
Af8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/A9oB/wwA
ARYBKgG3Af8BAAECAaYB/wFGAVUBxAH/GAADXwHoARYBKgG3Af80AAH/AUYBNAL/AUYBNAH/JAABEAGP
ARUB/wEQAY8BFQH/ARABjwEVAf8BEAGPARUB/xgAA9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wPa
Af8D2gH/A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wcAAQGQAAEQAY8BFQH/ARABjwEVAf8BEAGP
ARUB/wEQAY8BFQH/+AADAQECnAADPQFpAz4BagNBAXIDNwFaAzcBWwM3AVsDOgFiA0oBiwNLAY4kAAP9
Af8D9wH/A/YB/wP2Af8D9gH/A/YB/wP2Af8D9gH/A/YB/wP3Af8D/QH/HAADigH/Az0B/wMlAf8DJQH/
AyUB/wMlAf8DPQH/A4oB/xAAA/UB/wP1Af8D9QH/A/UB/wP1Af8D9QH/A/UB/wP1Af8D9QH/A/UB/wP1
Af8D9QH/A/UB/wP1Af8D9QH/BAADPQFpAx0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wNKAYkkAAP3
Af8DhgH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wOGAf8D9wH/FAADuQH/AyUB/wMlAf8DJQH/
AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wO5Af8IAAP1Af8DHAH/AxwB/wMcAf8DHAH/AxwB/wMc
Af8DHAH/AxwB/wMcAf8DHAH/AxwB/wMcAf8DHAH/A/UB/wQAAzsBZQMdAf8B+QLqAf8B+QLqAf8B+QLq
Af8B+QLqAf8B+QLqAf8DHQH/Az4BawM3AVsDNwFbAzcBWwM3AVsUAAP2Af8DHQH/AfEB7wHwAf8B8QHv
AfAB/wHxAe8B8AH/AfEB7wHwAf8B8QHvAfAB/wHxAe8B8AH/AfEB7wHwAf8DHQH/A/YB/xAABP8DJQH/
AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8IAAP1Af8DHAH/Ae4C7wH/
Ae4C7wH/Ae4C7wH/AxwB/wHuAu8B/wHuAu8B/wHuAu8B/wMcAf8B7gLvAf8B7gLvAf8B7gLvAf8DHAH/
A/UB/wQAAy4BSAMdAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8DHQH/Ax0B/wMdAf8DHQH/
Ax0B/wM3AVsUAAP2Af8DHQH/AfEB7wHwAf8DHQH/AfEB7wHwAf8DHQH/AfEB7wHwAf8DHQH/AfEB7wHw
Af8DHQH/A/YB/xAABP8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wMl
Af8IAAP1Af8DHAH/Ae4C7wH/Ae4C7wH/Ae4C7wH/AxwB/wHuAu8B/wHuAu8B/wHuAu8B/wMcAf8B7gLv
Af8B7gLvAf8B7gLvAf8DHAH/A/UB/wQAAywBQwMdAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLq
Af8DHQH/AzcBWwM3AVsDNwFbAx0B/wM3AVsUAAP2Af8DHQH/AfEB7wHwAf8B8QHvAfAB/wHxAe8B8AH/
AfEB7wHwAf8B8QHvAfAB/wMdAf8B8QHvAfAB/wMdAf8D9gH/EAAE/wMlAf8DJQH/AyUB/wMlAf8DJQH/
AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wgAA/UB/wMcAf8DHAH/AxwB/wMcAf8DHAH/AxwB/wMc
Af8DHAH/AxwB/wMcAf8DHAH/AxwB/wMcAf8D9QH/BAADKwFCAx0B/wMdAf8DHQH/Ax0B/wMdAf8DHQH/
Ax0B/wM3AVsEAAM3AVsDHQH/AzcBWxQAA/YB/wMdAf8B8QHvAfAB/wMdAf8B8QHvAfAB/wMdAf8B8QHv
AfAB/wMdAf8B8QHvAfAB/wMdAf8D9gH/EAAE/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/
AyUB/wMlAf8DJQH/AyUB/wgAA/UB/wMcAf8B7gLvAf8B7gLvAf8B7gLvAf8DHAH/Ae4C7wH/Ae4C7wH/
Ae4C7wH/AxwB/wHuAu8B/wHuAu8B/wHuAu8B/wMcAf8D9QH/BAADKAE8Ax0B/wMdAf8DHQH/Ax0B/wMd
Af8DHQH/Ax0B/wM3AVsEAAM3AVsDHQH/A0ABcRQAA/YB/wMdAf8B8QHvAfAB/wHxAe8B8AH/AfEB7wHw
Af8B8QHvAfAB/wHxAe8B8AH/AfEB7wHwAf8B8QHvAfAB/wMdAf8D9gH/EAAE/wMlAf8DJQH/AyUB/wMl
Af8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wgAA/UB/wMcAf8B7gLvAf8B7gLvAf8B7gLv
Af8DHAH/Ae4C7wH/Ae4C7wH/Ae4C7wH/AxwB/wHuAu8B/wHuAu8B/wHuAu8B/wMcAf8D9QH/BAADKgFB
AyoBQQMoAT0DJQE3AycBOwMuAUgDNgFZAzcBWwM3AVsEAAM3AVsDHQH/A0oBjRQAA/YB/wMdAf8B8QHv
AfAB/wMdAf8B8QHvAfAB/wMdAf8B8QHvAfAB/wMdAf8B8QHvAfAB/wMdAf8D9gH/EAAE/wMlAf8DJQH/
AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8DJQH/AyUB/wgAA/UB/wMcAf8DHAH/AxwB/wMc
Af8DHAH/AxwB/wMcAf8DHAH/AxwB/wMcAf8DHAH/AxwB/wMcAf8D9QH/HAADNQFWAzcBWwM3AVsDNwFb
AzcBWwMdAf8DSwGOA0oBjANHAYEMAAP2Af8DHQH/AfEB7wHwAf8B8QHvAfAB/wHxAe8B8AH/AfEB7wHw
Af8B8QHvAfAB/wHxAe8B8AH/AfEB7wHwAf8DHQH/A/YB/xAABP8DJQH/AyUB/wMlAf8DJQH/AyUB/wMl
Af8DJQH/AyUB/wMlAf8DJQH/AyUB/wMlAf8IAAP1Af8DHAH/Ae4C7wH/Ae4C7wH/Ae4C7wH/AxwB/wHu
Au8B/wHuAu8B/wHuAu8B/wMcAf8B7gLvAf8B7gLvAf8B7gLvAf8DHAH/A/UB/xwAAykBPgMdAf8DHQH/
Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DPQFoDAAD9gH/Ax0B/wHxAe8B8AH/Ax0B/wHxAe8B8AH/Ax0B/wHx
Ae8B8AH/Ax0B/wHxAe8B8AH/Ax0B/wP2Af8QAAT/AyUB/wMwAf8DqQH/AuEB4gH/A/gB/wP4Af8D+AH/
A/gB/wLhAeIB/wOpAf8DMAH/AyUB/wgAA/UB/wMcAf8B7gLvAf8B7gLvAf8B7gLvAf8DHAH/Ae4C7wH/
Ae4C7wH/Ae4C7wH/AxwB/wHuAu8B/wHuAu8B/wHuAu8B/wMcAf8D9QH/HAADJQE3Ax0B/wH5AuoB/wH5
AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wMdAf8DOwFjDAAD9gH/Ax0B/wHxAe8B8AH/AfEB7wHwAf8B8QHv
AfAB/wHxAe8B8AH/AfEB7wHwAf8B8QHvAfAB/wHxAe8B8AH/Ax0B/wP2Af8QAAPzAf8DMAH/A+0B/wP4
Af8D+AH/A/gB/wP4Af8D+AH/A/gB/wP4Af8D+AH/A+0B/wMlAf8IAAP1Af8DHAH/AxwB/wMcAf8DHAH/
AxwB/wMcAf8DHAH/AxwB/wMcAf8DHAH/AxwB/wMcAf8DHAH/A/UB/xwAAyUBNwMdAf8B+QLqAf8B+QLq
Af8B+QLqAf8B+QLqAf8B+QLqAf8DHQH/Ay8BSgwAA/YB/wMdAf8B8QHvAfAB/wMdAf8DHQH/Ax0B/wMd
Af8DHQH/AfEB7wHwAf8DHQH/A/YB/xQAAzAB/wPtAf8D+AH/A/gB/wP4Af8D+AH/A/gB/wP4Af8D+AH/
A/gB/wPtAf8DJQH/CAAD9QH/AxwB/wMcAf8DHAH/AxwB/wMcAf8DHAH/AxwB/wMcAf8DHAH/AxwB/wMc
Af8DHAH/AxwB/wP1Af8cAAMlATcDHQH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/AfkC6gH/Ax0B/wMr
AUIMAAP2Af8DHQH/AfEB7wHwAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wHxAe8B8AH/Ax0B/wP2Af8UAAO5
Af8DMAH/A6kB/wLhAeIB/wP4Af8D+AH/A/gB/wP4Af8C4QHiAf8DqQH/AzAB/wO5Af8IAAP1Af8DHAH/
AxwB/wMcAf8DHAH/AxwB/wMcAf8DHAH/AxwB/wMcAf8DHAH/AxwB/wMcAf8DHAH/A/UB/xwAAyoBQAMd
Af8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DKwFCDAAD9gH/Ax0B/wHxAe8B8AH/AfEB7wHwAf8B8QHv
AfAB/wHxAe8B8AH/AfEB7wHwAf8B8QHvAfAB/wHxAe8B8AH/Ax0B/wP2Af8YAAPoAf8DigH/Az0B/wMl
Af8DJQH/AyUB/wMlAf8DPQH/A4oB/wPoAf8MAAP1Af8D9QH/A/UB/wP1Af8D9QH/A/UB/wP1Af8D9QH/
A/UB/wP1Af8D9QH/A/UB/wP1Af8D9QH/A/UB/xwAAysBQgMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMd
Af8DMwFTDAAD9wH/A4YB/wMdAf8DHQH/Ax0B/wMdAf8DHQH/Ax0B/wMdAf8DhgH/A/cB/6QAAysBQgMu
AUcDPgFqA0cBgQNKAYkDNwFbAzcBWwM3AVoDNwFaDAAD/QH/A/cB/wP2Af8D9gH/A/YB/wP2Af8D9gH/
A/YB/wP2Af8D9wH/A/0B/wwAAUIBTQE+BwABPgMAASgDAAFAAwABYAMAAQEBAAEBBgABAxYAA/8BAAb/
AgAB/AE/Av8B5wH/AgAB/AE/AfwBeAHzAf8CAAH8AT8B/gF4AfsBJwIAAfwBPwE+AXAB+QErAgAB/AE/
AR4BAQH5Ac8CAAGAAQEBjAEDAfkBzwIAAYABAQHIATMB+QHPAgABgAEBAeEBAwH8AZMCAAGAAQEB4wGH
AfwB/wIAAfwBPwEAAQcB+AF/AgAB/AE/AQABBwH+Af8CAAH8AT8BAgEPAf4B/wIAAfwBPwHjAf8B/gE/
AgAB/AE/A/8BPwIAAf8BfwT/AgAI/wIAAYABeQT/AgABgAFxAccB+QHwAR8CAAGAAWMBwwHxAeABDwIA
AYABBwHBAcMB4AEHAgABgAEPAeABhwHAAQMCAAGAAQEB8AEHAcABAwIAAYABAQH4AQcBwAEDAgABgAEB
AfwBHwHAAQMCAAGAAQEB+AEeAcABAwIAAfwBAQHwAQ8BwAEDAgAB/gEBAeABhwHAAQMCAAH+AQEB4QHj
AeABBwIAAf4BAwHjAfMB8AEPAgAB/gEHAb8D/wIACP8DAAEDAQABHwUAAQEBAAEfBwABBwcAAQcHAAEH
AYABAQYAAYABAQYAAcABAwIAAYABAAGAAQABwAEDAgABgAEAAYABAAHgAQcCAAGAAQABgAEAAfABBwIA
AYABAAGAAQAB8AEPAgABgAEAAYABAAH4AR8CAAHAAQABgAEAAfgBHwIAAcABAAHwAQAB/AE/AgAB4AEB
AfABAAH8AT8CAAHwAQMB8AEACv8BAAEBAQABAQHwAQ8B8AEfAQABAQEAAQEB4AEHAeABDwEAAQEBAAEB
AcABAwHgAQcBAAEBAQABAQGAAQEBwAEDAQABAQEAAQEBgAEBAcABAwEAAQEBAAEBAQABAQHAAQMBAAEB
AQABAQEAAQEBwAEDAQABAQEAAQEBAAEBAcABAwEAAQEBAAEBAQABAQHAAQMBAAEBAQABAQEAAQEBwAED
AQABAQEAAQEBgAEBAeABBwEAAQEBAAEBAYABAQHwAQ8BAAEBAQABAQHAAQMC/wEAAQEBAAEBAeABBwL/
AQABAQEAAQEB8AEfDv8B/AE/AQABfwHHAfkBzwH/AfwBPwEAARcBwwHxAdsB/wH8AT8BAAFHAcEBwwHx
Af8B/AE/AQABAwHgAYcB4AH/AfwBPwEAAQMB8AEHAfABfwGAAQECAAH4AQcB+AE/AYABAQEAAQcB/AEf
AfwBHwGAAQECAAH4AR4B/gEPAYABAQIAAfABDwH/AR8B/AE/AgAB4AGHAf8BswH8AT8CAAHhAeMB/wHj
AfwBPwIAAeMB8wH/AecB/AE/AgABvwP/AfwBPwf/AX8E/wEAAX8BwAEHAfABDwEAAQEBAAF/AcABBwHA
AQMBAAEBAQABBwHAAQcBgAEDAQABAQEAAQcBwAEHAYABAwEAAQEBAAEHAcABBwGAAQMBAAEBAQABRwHA
AQcBgAEDAQABAQEAAUcBwAEHAYABAwEAAQEBAAFHAcABBwGAAQMBAAEBAfwBAQHAAQcBgAEDAQABAQH8
AQEBwAEHAYABAwEAAQEB/AEBAcABBwGAAQMBAAEBAfwBAQHAAQcBwAEDAQABAQH8AQEBwAEHAcABAwEA
AQEB/AEBAcABBwHgAQcBAAEBAfwBAQHAAQcE/wH8AQEBwAEHCw==
AfUB/wEAAdsB9QH/AQAB2gHzAf8BAAGqAcsC/wEAA/8BAAL/AzYBWAMaAf8DGgH/A10B0wNUAaYDXQHT
AxoB/wMaAf8DWgG9AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AzcBWwMxAU4DNwFaA0ABbgNKAY0DUgGk
A1wB6gNiAe8DYgHvA10B7QNcAcwDSAGFAyIB/wNIAYUDNwFbAzcBWwM3AVsEAAHqAfIB9QH/AREBtAHR
Af8BBgHGAd8B/wEaAeYB+AH/ARkB5AH3Af8BGAHjAfcB/wEAAUMBTAH/AQABQwFMAf8BFgHiAfcB/wEW
AeIB9wH/ARcB4wH3Af8BBQHHAeAB/wERAbQB0QH/AeoB8gH1Af8EAAH/AQAD/wEAAv8BAAG0AdEB/wEA
AcYB3wH/AQAB5gH4Af8BAAHkAfcB/wEAAeMB9wH/AQABFAEdAf8BAAEUAR0B/wEAAeIB9wH/AQAB4gH3
Af8BAAHjAfcB/wEAAccB4AH/AQABtAHRAv8BAAP/AQAC/wMuAUcDMwFSAz0BZwNGAX8DTgGVA1IBowFc
AloB9QF4AnYB+gF4AnYB+gMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wM3AVsDAAEBAzkBXwMiAf8DTgGU
A1IBpwMiAf8BgQJ9AfoBhQKDAfkDIgH/AX8BfQF6AfcDWwHIAyIB/wNIAYUDIgH/AyIB/wM3AVsIAAGO
AdMB4wH/AQABqgHLAf8BIgHqAfYB/wEmAfAB+gH/ASQB7gH5Af8DAAH/AwAB/wEgAeoB+QH/ASAB6gH5
Af8BGwHlAfUB/wEAAaoBywH/AY4B0wHjAf8IAAH/AQAD/wEAA/8BAAL/AQABqgHLAf8BAAHqAfYB/wEA
AfAB+gH/AQAB7gH5Af8DAAH/AwAB/wEAAeoB+QH/AQAB6gH5Af8BAAHlAfUB/wEAAaoBywL/AQAD/wEA
A/8BAAL/BAADIAEvA0wBkgMlAf8DGgH/AyUB/wFuAWwBawH6AzwB/wMaAf8DGgH/AxoB/wMaAf8DGgH/
AxoB/wMaAf8DOAFeBAADOQFfAyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wNI
AYUDTgGVAyIB/wM3AVsIAAHvAfQB9gH/AREBtAHRAf8BBAHCAdsB/wEqAfMB+wH/ASkB8QH6Af8DAAH/
AwAB/wElAe8B+gH/ASUB7wH6Af8BBgHGAd8B/wERAbQB0QH/Ae8B9AH2Af8IAAH/AQAD/wEAA/8BAAL/
AQABtAHRAf8BAAHCAdsB/wEAAfMB+wH/AQAB8QH6Af8DAAH/AwAB/wEAAe8B+gH/AQAB7wH6Af8BAAHG
Ad8B/wEAAbQB0QL/AQAD/wEAA/8BAAL/BAADOAFcAyUB/wMaAf8DGgH/AxoB/wMlAf8DXwHoAxoB/wMa
Af8DGgH/AxoB/wMaAf8DGgH/AxoB/wNKAYsEAAM5AV8DIgH/AfkC6gH/AfkC6gH/AyIB/wH5AuoB/wH5
AuoB/wMiAf8B+QLqAf8B+QLqAf8DIgH/A14B2QMiAf8DIgH/AzcBWwwAAZwB1wHlAf8BAAGpAcsB/wEk
AeoB9AH/ASwB9AH7Af8DAAH/AwAB/wEqAfIB+gH/ASMB6gH2Af8BAAGpAcsB/wGcAdcB5QH/DAAB/wEA
A/8BAAP/AQAD/wEAAv8BAAGpAcsB/wEAAeoB9AH/AQAB9AH7Af8DAAH/AwAB/wEAAfIB+gH/AQAB6gH2
Af8BAAGpAcsC/wEAA/8BAAP/AQAD/wEAAv8EAAM4AVwDGgH/AxoB/wMaAf8DGgH/AxoB/wNUAa8DGgH/
AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/A0QBewQAAzkBXwMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMi
Af8DIgH/AyIB/wMiAf8DYgHpA2IB6QMiAf8DSAGGEAABEQG0AdEB/wEBAb8B2QH/ATYB9gH7Af8DAAH/
AwAB/wEtAfUB+wH/AQgBxgHdAf8BEQG0AdEB/wH1AfYB9wH/DAAB/wEAA/8BAAP/AQAD/wEAAv8BAAG0
AdEB/wEAAb8B2QH/AQcB9gH7Af8DAAH/AwAB/wEAAfUB+wH/AQABxgHdAf8BAAG0AdEC/wEAA/8BAAP/
AQAD/wEAAv8EAAM4AVwDJQH/AxoB/wMaAf8DGgH/AyUB/wNWAasDXgHQA1wB6gNcAeoDXAHqA1wB6gMl
Af8DGgH/AzcBWwQAAzkBXwMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DYQHc
AyIB/wMiAf8DPQFnEAABqwHdAekB/wEAAakBygH/ASMB6AHzAf8BLQH1AfsB/wEtAfUB+wH/ASYB7QH2
Af8BAAGpAcsB/wGrAd0B6QH/EAAB/wEAA/8BAAP/AQAD/wEAA/8BAAL/AQABqQHKAf8BAAHoAfMB/wEA
AfUB+wH/AQAB9QH7Af8BAAHtAfYB/wEAAakBywL/AQAD/wEAA/8BAAP/AQAD/wEAAv8EAAMgAS8DTAGS
AyUB/wMaAf8DJQH/A1QBrwNWAasDVQGtA18B6ANcAeoDXAHqA1wB6gNcAeoDGgH/AzcBWwQAA0MBeAMi
Af8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DTgGVA10B0wMiAf8DNwFbFAABEgG0
AdEB/wEAAbsB1gH/AS0B9QH7Af8BLQH1AfsB/wEHAcYB3QH/ARIBtAHRAf8UAAH/AQAD/wEAA/8BAAP/
AQAD/wEAAv8BAAG0AdEB/wEAAbsB1gH/AQAB9QH7Af8BAAH1AfsB/wEAAcYB3QH/AQABtAHRAv8BAAP/
AQAD/wEAA/8BAAP/AQAC/wgAA0sBjgNMAZIDVwG6A1YBqwNWAasDVgGrA1YBqwNhAeQDXAHqA1wB6gNc
AeoDXAHqAxoB/wM3AVsEAANJAYcDPgFrAzcBWwM3AVsDNwFbAzoBYgNQAZ4DUgGjA1IBowNSAaMDUgGj
A04BlQMiAf8DIgH/AzcBWxQAAbsB4gHtAf8BAAGoAcoB/wEgAeUB8QH/ASYB7AH1Af8BAAGpAcsB/wG7
AeIB7QH/FAAB/wEAA/8BAAP/AQAD/wEAA/8BAAP/AQAC/wEAAagBygH/AQAB5QHxAf8BAAHsAfUB/wEA
AakBywL/AQAD/wEAA/8BAAP/AQAD/wEAA/8BAAL/CAADPgFrA0oBjAMlAf8DVgGxA1gBuQNWAasDVgGr
A2EB4QNcAeoDXAHqA1wB6gMlAf8DSgGMAy4BSBAAAzcBWwMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMi
Af8DIgH/AyIB/wMiAf8DNgFZGAABEgG0AdEB/wEAAbkB1QH/AQcBxgHdAf8BEgG0AdEB/xgAAf8BAAP/
AQAD/wEAA/8BAAP/AQAD/wEAAv8BAAG0AdEB/wEAAbkB1QH/AQABxgHdAf8BAAG0AdEC/wEAA/8BAAP/
AQAD/wEAA/8BAAP/AQAC/wwAAz0BaANWAbQDXAHqAzEB/wMaAf8DGgH/AxoB/wMaAf8DMQH/A1wB6gNX
AboDPgFrFAADNwFbAyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wM0AVQYAAHJ
AekB8QH/AQIBrwHOAf8BAgGvAc4B/wHJAekB8QH/GAAB/wEAA/8BAAP/AQAD/wEAA/8BAAP/AQAD/wEA
Av8BAAGvAc4B/wEAAa8BzgL/AQAD/wEAA/8BAAP/AQAD/wEAA/8BAAP/AQAC/xAAAw0BEQMnAToDMwFT
AzcBWwM7AWUDSgGNA0sBjgNIAYQDOAFeAxIBGRgAAzcBWwM3AVsDSQGHA0sBjgNLAY4DSgGNA0UBfAM3
AVsDNwFbAzcBWwM1AVcDNwFa/wBBAAMqAUADKgFAAyoBQAMqAUADKgFAAyoBQAMqAUADKgFAAyoBQAMq
AUADKgFAAyoBQAMqAUADJgE4AwwBEAQAAyoBQAMqAUADKgFAAyoBQAMqAUADKgFAAyoBQAMqAUADKgFA
AyoBQAMqAUADKgFAAyoBQAMmATgDDAEQFAAB1gG+Aa0B/wG1AZoBhAH/AZwBHAEBAf8BlAEQAQAB/wGU
ARABAAH/AaUBJAESAf8BvQGmAZQB/wHWAb4BtQH/IAAB9wHzAe8B/wHWAb4BtQH/Aa0BjgFYAf8BlAFG
AS8B/wGMAT4BJwH/AZQBUgFAAf8BxgG2Aa0B/xQAA1wB3wNcAd8DXAHfA1wB3wNcAd8DXAHfA1wB3wNc
Ad8DXAHfA1wB3wNcAd8DXAHfA1wB3wNbAcMDJgE4BAADXAHfA1wB3wNcAd8DXAHfA1wB3wNcAd8DXAHf
A1wB3wNcAd8DXAHfA1wB3wNcAd8DXAHfA1sBwwMmATgQAAHGAa4BnAH/Aa0BhgESAf8BrQGSASIB/wHW
Ac8BzgH/AecB8wH3Af8B5wHvAfcB/wHGAcMBvQH/AaUBhgEaAf8BpQEgAQoB/wHGAa4BnAH/GAAB9wHr
AecB/wG9AZoBjAH/AZwBSgEvAf8BnAFKAS8B/wGcAU4BNwH/AZwBSgE3Af8BlAFCAS8B/wFYAS4BFgH/
Aa0BmgGMAf8QAAGnAqYB/wKzAbIB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6
Af8BsQKwAf8DXAHfAyoBQAQAAacCpgH/ArMBsgH/A7oB/wO6Af8DugH/A7oB/wO6Af8DugH/A7oB/wO6
Af8DugH/A7oB/wGxArAB/wNcAd8DKgFADAABzgGuAZwB/wG1AZoBjAH/Ae8B6wHnBf8B9wH7Av8B7wH7
AfcB/wHvAvcB/wLvAfcB/wHnAfMC/wG9AbYBtQH/AZQBEAEAAf8BxgGuAZwB/xQAAb0BmgGMAf8BpQFS
ATcB/wGlAVIBNwH/AaUBUgE3Af8BnAFOATcB/wGcAU4BNwH/AZwBTgE3Af8BnAFOATcB/wGEATIBHwH/
Aa0BlgGMAf8MAAORAf8DxAH/A+MB/wPjAf8D4wH/AuEB4AH/AdgB1AHSAf8B2gHWAdUB/wPjAf8D4wH/
A+MB/wPjAf8DugH/A1wB3wMqAUAEAAORAf8DxAH/A+MB/wPjAf8D4wH/A+MB/wPjAf8D4wH/A+MB/wPj
Af8D4wH/A+MB/wO6Af8DXAHfAyoBQAgAAdYBugGtAf8BtQGSASIB/wP3Cf8B3gG2AaUB/wGlAgAB/wGl
AgAB/wHWAb4BrQH/AfcD/wHvAfcC/wG9Aa4BpQH/AZwBFAEAAf8B1gG+AbUB/wwAAecBxwG1Af8BrQFa
AUAB/wGtAVoBQAH/Aa0BWgFIAf8BrQFaAUgB/wGtAVoBQAH/AaUBVgFAAf8BpQFSATcB/wGcAU4BNwH/
AZwBTgE3Af8BWAEuARYB/wHGAbYBrQH/CAADkQH/A8QB/wPjAf8D4wH/A+MB/wHZAdQB0gH/AaMBiAEh
Af8BrgGXAYwB/wPjAf8D4wH/A+MB/wPjAf8DugH/A1wB3wMqAUAEAAORAf8DxAH/A+MB/wPjAf8D4wH/
A+MB/wPjAf8D4wH/A+MB/wPjAf8D4wH/A+MB/wO6Af8DXAHfAyoBQAgAAb0BngGEAf8B3gHHAb0N/wHv
AdcBxgH/Aa0CAAH/AaUCAAH/AfcB7wHnBf8B7wHrAe8B/wHnAfMB9wH/AZwBJAESAf8BtQGSASIB/wwA
Ac4BogGMAf8BrQGGAUgB/wG1AYoBUAH/Ab0BjgFQAf8BvQGOAVAB/wG1AYoBUAH/AbUBhgFIAf8BrQFa
AUAB/wGlAVIBNwH/AZwBTgE3Af8BlAFCAS8B/wGUAVIBQAH/CAADkQH/AcYCxwH/AeYC5wH/AeYC5wH/
AeYC5wH/AdgB0gHPAf8BjwERAQAB/wGeAYABFwH/AeYC5wH/AeYC5wH/AeYC5wH/AeYC5wH/A7wB/wNc
Ad8DKgFABAADkQH/AcYCxwH/AeYC5wH/AeYC5wH/AeYC5wH/AeYC5wH/AeYC5wH/AeYC5wH/AeYC5wH/
AeYC5wH/AeYC5wH/AeYC5wH/A7wB/wNcAd8DKgFABAAB1gG6Aa0B/wG9AZoBhAL/AfsO/wHvAeMB1gH/
Ab0CAAH/AbUBDAEAAf8B9wH7AfcF/wH3AfMB9wH/Ae8B8wH3Af8BxgG+Ab0B/wGcARwBAQH/DAABxgGa
AYQB/wG9AYoBUAL/AfsD/wL3Av8B8wHvAf8B9wHrAecB/wH3AeMB3gH/AfcB4wHWAf8B9wHjAd4B/wH3
AdsBzgH/AZwBRgEvAf8BjAE+AScB/wgAA5EB/wHKAssB/wHsAu0B/wHsAu0B/wHsAu0B/wHdAdgB1QH/
AZEBEwEAAf8BoAGCARkB/wHsAu0B/wHsAu0B/wHsAu0B/wHsAu0B/wO/Af8DXAHfAyoBQAQAA5EB/wHK
AssB/wHsAu0B/wHsAu0B/wHsAu0B/wHsAu0B/wHsAu0B/wHsAu0B/wHsAu0B/wHsAu0B/wHsAu0B/wHs
Au0B/wO/Af8DXAHfAyoBQAQAAc4BrgGcAf8BzgGmAYwR/wHvAd8B1gH/Ab0CAAH/AbUBDAEAAf8B9wLv
Cf8B7wHzAfcB/wHWAdsB3gH/AZwBHAEBAf8MAAHGAZoBhAH/AcYBlgFYDv8B+wP/AfMB7wH/AfcB6wHn
Af8B9wHjAd4B/wH3Ad8B1gH/AZwBRgEvAf8BlAFGAS8B/wgAA5EB/wPQAf8B6AHeAdoB/wHIAaMBlgH/
AcEBlwGIAf8BuwGPASUB/wGfAQ0BAAH/AaQBFQEAAf8BwQGXAYgB/wHBAZcBiAH/Ac4BrwGjAf8B7wHq
AecB/wPDAf8DXAHfAyoBQAQAA5EB/wHKAssB/wHhAdgB1AH/AccBoQGUAf8BwQGXAYgB/wHBAZcBiAH/
AcEBlwGIAf8BwQGXAYgB/wHBAZcBiAH/AcEBlwGIAf8BzAGtAaEB/wHmAeIB4QH/A78B/wNcAd8DKgFA
BAABzgGmAZQB/wHOAaoBlBH/Ae8B2wHOAf8BxgIAAf8BtQEIAQAB/wHvAesB5wn/A/cB/wHeAeMB5wH/
AZwBIAEKAf8MAAHeAbIBnAH/Ac4BngGEAf8B5wG2AZwB/wHnAb4BrQH/AecBvgGtAf8B5wG2AZwB/wHW
AaYBjAH/Ac4BmgGEAf8BtQGKAUgB/wGlAVYBQAH/AZwBRgEvAf8BrQGOAVgB/wgAA5EB/wPTAf8B6wHf
AdkB/wHEAZcBhwH/AbwBiQEdAf8BuAGDARYB/wGiAQwBAAH/AacBEgEAAf8BvAGJAR0B/wG8AYkBHQH/
AcwBpgGYAf8B9AHtAeoB/wPGAf8DXAHfAyoBQAQAA5EB/wHMAs0B/wHjAdcB0gH/AcMBlgGGAf8BvAGJ
AR0B/wG8AYkBHQH/AbwBiQEdAf8BvAGJAR0B/wG8AYkBHQH/AbwBiQEdAf8ByQGjAZYB/wHpAeQB4QH/
A8EB/wNcAd8DKgFABAABxgGiAYwB/wHOAaoBlBH/Ad4BwwGtAf8BvQEEAQAB/wG9ARwBAAH/AfcC7wb/
AfsC/wH3AfsC/wHeAdcB3gH/AZwBHAEBAf8MAAHvAc8BvQH/Ac4BngGEAf8B5wG2AZwB/wHnAb4BrQH/
AecBvgGtAf8B5wG2AZwB/wHWAaYBjAH/Ac4BmgGEAf8BtQGKAUgB/wGlAVYBQAH/AZwBTgE3Af8B1gG+
AbUB/wgAA5EB/wPTAf8B+AH2AfUB/wHyAeoB5wH/AfEB6AHkAf8B4gHUAc0B/wGWARQBAQH/AaUBgwEY
Af8B8QHoAeQB/wHxAegB5AH/AfMB7QHqAf8B+gH5AfcB/wPGAf8DXAHfAyoBQAQAA5EB/wPSAf8B9wH1
AfMB/wHyAeoB5wH/AfEB6AHkAf8B8QHoAeQB/wHxAegB5AH/AfEB6AHkAf8B8QHoAeQB/wHxAegB5AH/
AfMB7AHpAf8B+AH3AfYB/wPFAf8DXAHfAyoBQAQAAdYBsgGcAf8BzgGiAYwR/wH3AesB5wH/Ae8B2wHW
Af8B9wHrAecK/wH7Av8B9wP/Ac4BvgG9Af8BpQEkAQoB/wwAAf8B8wHvAf8B3gG2AaUB/wHWAaYBjAH/
AecBvgGtAf8B7wG+Aa0B/wHnAbIBnAH/AdYBpgGUAf8BxgGWAYQB/wG1AYoBUAH/AaUBVgFAAf8BvQGa
AYwB/wH3Au8B/wgAA5EB/wPUAf8C/AH7Af8C/AH7Af8C/AH7Af8B6wHlAeEB/wGVARcBBAH/AaYBiAEe
Af8C/AH7Af8C/AH7Af8C/AH7Af8C/AH7Af8DxwH/A1wB3wMqAUAEAAORAf8D0wH/AvsB+gH/AvsB+gH/
AvsB+gH/AvsB+gH/AvsB+gH/AvsB+gH/AvsB+gH/AvsB+gH/AvsB+gH/AvsB+gH/A8YB/wNcAd8DKgFA
CAAB1gGmAYwB/wHvAecB3g7/AfsC/wHWAaIBhAH/AdYBogGEAv8B+w7/AaUBJAEKAf8BxgGmAZQB/xAA
AfcB6wHnAf8B3gG2AaUB/wHWAaIBjAH/AdYBqgGUAf8B3gGuAZQB/wHOAaIBjAH/Ab0BkgFYAf8BtQGG
AUgB/wG9AZoBjAH/AfcB6wHnAf8MAAORAf8D1AH/A/wB/wP8Af8D/AH/Ae0B6AHlAf8BogGCARgB/wGx
AZcBiQH/A/wB/wP8Af8D/AH/A/wB/wPHAf8DXAHfAyoBQAQAA5EB/wPTAf8C+wH6Af8C+wH6Af8C+wH6
Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8DxgH/A1wB3wMqAUAIAAHW
AbYBnAH/AecBsgGUDf8B9wHvAecB/wHGARQBAAH/Ab0BDAEAAf8B9wLvCf8BxgGuAZwB/wGtAYYBEgH/
AdYBwwG1Af8UAAH/Ae8B5wH/Ae8BzwG9Af8B3gGyAZwB/wHOAZ4BjAH/AcYBmgGEAf8B1gGmAZQB/wHW
AboBrQL/AvcB/xAAA5EB/wPUAf8D/AH/A/wB/wP8Af8B+AL2Af8B4gHZAdUB/wHnAd8B2wH/A/wB/wP8
Af8D/AH/A/wB/wPHAf8DXAHfAyoBQAQAA5EB/wPTAf8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6
Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8C+wH6Af8DxgH/A1wB3wMqAUAMAAHeAbIBlAH/Ae8BxwGt
Dv8C9wL/AfcB7wb/AvcB/wHOAbYBpQH/Aa0BhgESAf8B1gHDAbUB/0gAAqEBoAH/A8EB/wPUAf8D1AH/
A9QB/wPUAf8D1AH/A9QB/wPUAf8D1AH/A9QB/wPUAf8DuwH/A1wB3wMqAUAEAAKhAaAB/wPAAf8D0wH/
A9MB/wPTAf8D0wH/A9MB/wPTAf8D0wH/A9MB/wPTAf8D0wH/A7oB/wNcAd8DKgFAEAAB3gG2AZwB/wHn
AbYBlAH/AfcB3wHGAv8B5wHeAv8B6wHnAf8B9wHnAd4B/wHnAc8BxgH/Ac4BqgGUAf8BvQGaAYQB/wHW
AcMBtQH/TAABvAG7AboB/wKhAaAB/wORAf8DkQH/A5EB/wORAf8DkQH/A5EB/wORAf8DkQH/A5EB/wOR
Af8BpwKmAf8DXAHfAyoBQAQAAbwBuwG6Af8CoQGgAf8DkQH/A5EB/wORAf8DkQH/A5EB/wORAf8DkQH/
A5EB/wORAf8DkQH/AacCpgH/A1wB3wMqAUAUAAHeAb4BrQH/Ad4BtgGcAf8B5wG2AZwB/wHnAbIBlAH/
Ad4BqgGMAf8B1gGuAZwB/wHWAb4BtQH//wDtAAENAY8BEgH/AQ0BjwESAf8BDQGPARIB/wENAY8BEgH/
GAAD2gH/A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wNbAcQkAAETAScBtwH/AgABpgH/AUMBUgHE
Af8gAANfAegBEwEnAbcB/wwAAf8BQwExAv8BQwExAf9IAAENAY8BEgH/AQ0BjwESAf8BDQGPARIB/wEN
AY8BEgH/GAAD2gH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/A9oB/wNlAfQDAQECAwABAQcAAQEWAAGm
Af8CAAGmAf8CAAGmAf8DXwHoGAABQwFSAcQB/wEDARkBsgH/Aw0BEQwAAf8BQwExAf8IAAH/AUMBMQH/
QAABDQGPARIB/wENAY8BEgH/AQ0BjwESAf8BDQGPARIB/xgAA9oB/wMaAf8DGgH/AxoB/wMaAf8DGgH/
AxoB/wPaAf8DUQGcBAADAQECAwQBBQMAAQEUAAFDAVIBxAH/AgABpgH/AgABpgH/AgABpgH/A1IBqQwA
AxABFgE1AUUBvgH/AQABDAGsAf8DMQFNGAAB/wFDATEC/wFDATEC/wFDATEB/zwAAQ0BjwESAf8BDQGP
ARIB/wENAY8BEgH/AQ0BjwESAf8YAAPaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8D2gH/A2AB6wNd
AdIDWQHCA1sBxgNUAawDGwEmFAADXQHKAgABpgH/AgABpgH/AQABDAGsAf8DMQFNBAADDQERARMBJwG3
Af8CAAGmAf8DUgGpGAAB/wFDATEC/wFDATEC/wFDATEC/wFDATEC/wFDATEB/zgAAQ0BjwESAf8BDQGP
ARIB/wENAY8BEgH/AQ0BjwESAf8YAAPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wPa
Af8D2gH/A9oB/wNDAXcDAAEBGAADVgGrAgABpgH/AgABpgH/AQABDAGsAf8DPgFrARMBJwG3Af8CAAGm
Af8DXwHoAwMBBBwAAf8BQwExAv8BQwExAv8BQwExAv8BQwExAv8BQwExAf8gAAENAY8BEgH/AQ0BjwES
Af8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8BDQGPARIB/wEN
AY8BEgH/AQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8EAAPaAf8DGgH/AxoB/wMaAf8DGgH/
AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/A9oB/wM1AVcDBAEFAw8BFAMAAQEUAAM+AWsBAAEMAawB/wIA
AaYB/wIAAaYB/wIAAaYB/wFDAVIBxAH/AwMEBAEFIAAB/wFDATEC/wFDATEC/wFDATEC/wFDATEC/wFD
ATEB/xwAAQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwES
Af8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8BDQGPARIB/wQA
A9oB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8D2gH/AwoBDiQAA1wByQIA
AaYB/wIAAaYB/wEAAQwBrAH/Aw0BESwAAf8BQwExAv8BQwExAv8BQwExAv8BQwExAv8BQwExAf8YAAEN
AY8BEgH/AQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwES
Af8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8EAAPaAf8DGgH/
AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/A9oB/wMvAUoDBQEHAyABLgMsAUMUAANS
AakCAAGmAf8CAAGmAf8CAAGmAf8CAAGmAf8DXAHJEAADCwEPHAAB/wFDATEC/wFDATEC/wFDATEC/wFD
ATEC/wFDATEB/xQAAQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8BDQGPARIB/wENAY8BEgH/
AQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8BDQGP
ARIB/wQAA9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wPa
Af8D2gH/A9oB/xAAA1wByQIAAaYB/wIAAaYB/wEAAQwBrAH/Az4BawETAScBtwH/AgABpgH/A0oBjDAA
Af8BQwExAv8BQwExAv8BQwExAf8sAAENAY8BEgH/AQ0BjwESAf8BDQGPARIB/wENAY8BEgH/GAAD2gH/
AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8D2gH/
DAABQwFSAcQB/wIAAaYB/wIAAaYB/wIAAaYB/wNSAakEAAMNAREBNQFFAb4B/wEAAQwBrAH/AzEBTTAA
Af8BQwExAf8IAAH/AUMBMQL/AUMBMQH/IAABDQGPARIB/wENAY8BEgH/AQ0BjwESAf8BDQGPARIB/xgA
A9oB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/
A9oB/w4AAaYB/wIAAaYB/wIAAaYB/wNfAegQAAFDAVIBxAH/AQMBGQGyAf8DDQERNAAB/wFDATEC/wFD
ATEC/wFDATEB/yAAAQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwESAf8YAAPaAf8DGgH/AxoB/wMa
Af8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wPaAf8MAAETAScBtwH/
AgABpgH/AUMBUgHEAf8YAANfAegBEwEnAbcB/zQAAf8BQwExAv8BQwExAf8kAAENAY8BEgH/AQ0BjwES
Af8BDQGPARIB/wENAY8BEgH/GAAD2gH/A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/
A9oB/wPaAf8D2gH/A9oB/wPaAf8D2gH/BwABAZAAAQ0BjwESAf8BDQGPARIB/wENAY8BEgH/AQ0BjwES
Af/4AAMBAQKcAAM9AWkDPgFqA0EBcgM3AVoDNwFbAzcBWwM6AWIDSgGLA0sBjiQAA/0B/wP3Af8D9gH/
A/YB/wP2Af8D9gH/A/YB/wP2Af8D9gH/A/cB/wP9Af8cAAOKAf8DOgH/AyIB/wMiAf8DIgH/AyIB/wM6
Af8DigH/EAAD9QH/A/UB/wP1Af8D9QH/A/UB/wP1Af8D9QH/A/UB/wP1Af8D9QH/A/UB/wP1Af8D9QH/
A/UB/wP1Af8EAAM9AWkDGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/A0oBiSQAA/cB/wOGAf8DGgH/
AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/A4YB/wP3Af8UAAO5Af8DIgH/AyIB/wMiAf8DIgH/AyIB/wMi
Af8DIgH/AyIB/wMiAf8DIgH/A7kB/wgAA/UB/wMZAf8DGQH/AxkB/wMZAf8DGQH/AxkB/wMZAf8DGQH/
AxkB/wMZAf8DGQH/AxkB/wMZAf8D9QH/BAADOwFlAxoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5
AuoB/wMaAf8DPgFrAzcBWwM3AVsDNwFbAzcBWxQAA/YB/wMaAf8B8QHvAfAB/wHxAe8B8AH/AfEB7wHw
Af8B8QHvAfAB/wHxAe8B8AH/AfEB7wHwAf8B8QHvAfAB/wMaAf8D9gH/EAAE/wMiAf8DIgH/AyIB/wMi
Af8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wgAA/UB/wMZAf8B7gLvAf8B7gLvAf8B7gLv
Af8DGQH/Ae4C7wH/Ae4C7wH/Ae4C7wH/AxkB/wHuAu8B/wHuAu8B/wHuAu8B/wMZAf8D9QH/BAADLgFI
AxoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AzcBWxQA
A/YB/wMaAf8B8QHvAfAB/wMaAf8B8QHvAfAB/wMaAf8B8QHvAfAB/wMaAf8B8QHvAfAB/wMaAf8D9gH/
EAAE/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wgAA/UB/wMZ
Af8B7gLvAf8B7gLvAf8B7gLvAf8DGQH/Ae4C7wH/Ae4C7wH/Ae4C7wH/AxkB/wHuAu8B/wHuAu8B/wHu
Au8B/wMZAf8D9QH/BAADLAFDAxoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5AuoB/wMaAf8DNwFb
AzcBWwM3AVsDGgH/AzcBWxQAA/YB/wMaAf8B8QHvAfAB/wHxAe8B8AH/AfEB7wHwAf8B8QHvAfAB/wHx
Ae8B8AH/AxoB/wHxAe8B8AH/AxoB/wP2Af8QAAT/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMi
Af8DIgH/AyIB/wMiAf8DIgH/CAAD9QH/AxkB/wMZAf8DGQH/AxkB/wMZAf8DGQH/AxkB/wMZAf8DGQH/
AxkB/wMZAf8DGQH/AxkB/wP1Af8EAAMrAUIDGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AzcBWwQA
AzcBWwMaAf8DNwFbFAAD9gH/AxoB/wHxAe8B8AH/AxoB/wHxAe8B8AH/AxoB/wHxAe8B8AH/AxoB/wHx
Ae8B8AH/AxoB/wP2Af8QAAT/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMi
Af8DIgH/CAAD9QH/AxkB/wHuAu8B/wHuAu8B/wHuAu8B/wMZAf8B7gLvAf8B7gLvAf8B7gLvAf8DGQH/
Ae4C7wH/Ae4C7wH/Ae4C7wH/AxkB/wP1Af8EAAMoATwDGgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/
AzcBWwQAAzcBWwMaAf8DQAFxFAAD9gH/AxoB/wHxAe8B8AH/AfEB7wHwAf8B8QHvAfAB/wHxAe8B8AH/
AfEB7wHwAf8B8QHvAfAB/wHxAe8B8AH/AxoB/wP2Af8QAAT/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/
AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/CAAD9QH/AxkB/wHuAu8B/wHuAu8B/wHuAu8B/wMZAf8B7gLv
Af8B7gLvAf8B7gLvAf8DGQH/Ae4C7wH/Ae4C7wH/Ae4C7wH/AxkB/wP1Af8EAAMqAUEDKgFBAygBPQMl
ATcDJwE7Ay4BSAM2AVkDNwFbAzcBWwQAAzcBWwMaAf8DSgGNFAAD9gH/AxoB/wHxAe8B8AH/AxoB/wHx
Ae8B8AH/AxoB/wHxAe8B8AH/AxoB/wHxAe8B8AH/AxoB/wP2Af8QAAT/AyIB/wMiAf8DIgH/AyIB/wMi
Af8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/CAAD9QH/AxkB/wMZAf8DGQH/AxkB/wMZAf8DGQH/
AxkB/wMZAf8DGQH/AxkB/wMZAf8DGQH/AxkB/wP1Af8cAAM1AVYDNwFbAzcBWwM3AVsDNwFbAxoB/wNL
AY4DSgGMA0cBgQwAA/YB/wMaAf8B8QHvAfAB/wHxAe8B8AH/AfEB7wHwAf8B8QHvAfAB/wHxAe8B8AH/
AfEB7wHwAf8B8QHvAfAB/wMaAf8D9gH/EAAE/wMiAf8DIgH/AyIB/wMiAf8DIgH/AyIB/wMiAf8DIgH/
AyIB/wMiAf8DIgH/AyIB/wgAA/UB/wMZAf8B7gLvAf8B7gLvAf8B7gLvAf8DGQH/Ae4C7wH/Ae4C7wH/
Ae4C7wH/AxkB/wHuAu8B/wHuAu8B/wHuAu8B/wMZAf8D9QH/HAADKQE+AxoB/wMaAf8DGgH/AxoB/wMa
Af8DGgH/AxoB/wM9AWgMAAP2Af8DGgH/AfEB7wHwAf8DGgH/AfEB7wHwAf8DGgH/AfEB7wHwAf8DGgH/
AfEB7wHwAf8DGgH/A/YB/xAABP8DIgH/Ay0B/wOpAf8C4QHiAf8D+AH/A/gB/wP4Af8D+AH/AuEB4gH/
A6kB/wMtAf8DIgH/CAAD9QH/AxkB/wHuAu8B/wHuAu8B/wHuAu8B/wMZAf8B7gLvAf8B7gLvAf8B7gLv
Af8DGQH/Ae4C7wH/Ae4C7wH/Ae4C7wH/AxkB/wP1Af8cAAMlATcDGgH/AfkC6gH/AfkC6gH/AfkC6gH/
AfkC6gH/AfkC6gH/AxoB/wM7AWMMAAP2Af8DGgH/AfEB7wHwAf8B8QHvAfAB/wHxAe8B8AH/AfEB7wHw
Af8B8QHvAfAB/wHxAe8B8AH/AfEB7wHwAf8DGgH/A/YB/xAAA/MB/wMtAf8D7QH/A/gB/wP4Af8D+AH/
A/gB/wP4Af8D+AH/A/gB/wP4Af8D7QH/AyIB/wgAA/UB/wMZAf8DGQH/AxkB/wMZAf8DGQH/AxkB/wMZ
Af8DGQH/AxkB/wMZAf8DGQH/AxkB/wMZAf8D9QH/HAADJQE3AxoB/wH5AuoB/wH5AuoB/wH5AuoB/wH5
AuoB/wH5AuoB/wMaAf8DLwFKDAAD9gH/AxoB/wHxAe8B8AH/AxoB/wMaAf8DGgH/AxoB/wMaAf8B8QHv
AfAB/wMaAf8D9gH/FAADLQH/A+0B/wP4Af8D+AH/A/gB/wP4Af8D+AH/A/gB/wP4Af8D+AH/A+0B/wMi
Af8IAAP1Af8DGQH/AxkB/wMZAf8DGQH/AxkB/wMZAf8DGQH/AxkB/wMZAf8DGQH/AxkB/wMZAf8DGQH/
A/UB/xwAAyUBNwMaAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8B+QLqAf8DGgH/AysBQgwAA/YB/wMa
Af8B8QHvAfAB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AfEB7wHwAf8DGgH/A/YB/xQAA7kB/wMtAf8DqQH/
AuEB4gH/A/gB/wP4Af8D+AH/A/gB/wLhAeIB/wOpAf8DLQH/A7kB/wgAA/UB/wMZAf8DGQH/AxkB/wMZ
Af8DGQH/AxkB/wMZAf8DGQH/AxkB/wMZAf8DGQH/AxkB/wMZAf8D9QH/HAADKgFAAxoB/wMaAf8DGgH/
AxoB/wMaAf8DGgH/AxoB/wMrAUIMAAP2Af8DGgH/AfEB7wHwAf8B8QHvAfAB/wHxAe8B8AH/AfEB7wHw
Af8B8QHvAfAB/wHxAe8B8AH/AfEB7wHwAf8DGgH/A/YB/xgAA+gB/wOKAf8DOgH/AyIB/wMiAf8DIgH/
AyIB/wM6Af8DigH/A+gB/wwAA/UB/wP1Af8D9QH/A/UB/wP1Af8D9QH/A/UB/wP1Af8D9QH/A/UB/wP1
Af8D9QH/A/UB/wP1Af8D9QH/HAADKwFCAxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wMzAVMMAAP3
Af8DhgH/AxoB/wMaAf8DGgH/AxoB/wMaAf8DGgH/AxoB/wOGAf8D9wH/pAADKwFCAy4BRwM+AWoDRwGB
A0oBiQM3AVsDNwFbAzcBWgM3AVoMAAP9Af8D9wH/A/YB/wP2Af8D9gH/A/YB/wP2Af8D9gH/A/YB/wP3
Af8D/QH/DAABQgFNAT4HAAE+AwABKAMAAUADAAFgAwABAQEAAQEGAAEDFgAD/wEABv8CAAH8AT8C/wHn
Af8CAAH8AT8B/AF4AfMB/wIAAfwBPwH+AXgB+wEnAgAB/AE/AT4BcAH5ASsCAAH8AT8BHgEBAfkBzwIA
AYABAQGMAQMB+QHPAgABgAEBAcgBMwH5Ac8CAAGAAQEB4QEDAfwBkwIAAYABAQHjAYcB/AH/AgAB/AE/
AQABBwH4AX8CAAH8AT8BAAEHAf4B/wIAAfwBPwECAQ8B/gH/AgAB/AE/AeMB/wH+AT8CAAH8AT8D/wE/
AgAB/wF/BP8CAAj/AgABgAF5BP8CAAGAAXEBxwH5AfABHwIAAYABYwHDAfEB4AEPAgABgAEHAcEBwwHg
AQcCAAGAAQ8B4AGHAcABAwIAAYABAQHwAQcBwAEDAgABgAEBAfgBBwHAAQMCAAGAAQEB/AEfAcABAwIA
AYABAQH4AR4BwAEDAgAB/AEBAfABDwHAAQMCAAH+AQEB4AGHAcABAwIAAf4BAQHhAeMB4AEHAgAB/gED
AeMB8wHwAQ8CAAH+AQcBvwP/AgAI/wMAAQMBAAEfBQABAQEAAR8HAAEHBwABBwcAAQcBgAEBBgABgAEB
BgABwAEDAgABgAEAAYABAAHAAQMCAAGAAQABgAEAAeABBwIAAYABAAGAAQAB8AEHAgABgAEAAYABAAHw
AQ8CAAGAAQABgAEAAfgBHwIAAcABAAGAAQAB+AEfAgABwAEAAfABAAH8AT8CAAHgAQEB8AEAAfwBPwIA
AfABAwHwAQAK/wEAAQEBAAEBAfABDwHwAR8BAAEBAQABAQHgAQcB4AEPAQABAQEAAQEBwAEDAeABBwEA
AQEBAAEBAYABAQHAAQMBAAEBAQABAQGAAQEBwAEDAQABAQEAAQEBAAEBAcABAwEAAQEBAAEBAQABAQHA
AQMBAAEBAQABAQEAAQEBwAEDAQABAQEAAQEBAAEBAcABAwEAAQEBAAEBAQABAQHAAQMBAAEBAQABAQGA
AQEB4AEHAQABAQEAAQEBgAEBAfABDwEAAQEBAAEBAcABAwL/AQABAQEAAQEB4AEHAv8BAAEBAQABAQHw
AR8O/wH8AT8BAAF/AccB+QHPAf8B/AE/AQABFwHDAfEB2wH/AfwBPwEAAUcBwQHDAfEB/wH8AT8BAAED
AeABhwHgAf8B/AE/AQABAwHwAQcB8AF/AYABAQIAAfgBBwH4AT8BgAEBAQABBwH8AR8B/AEfAYABAQIA
AfgBHgH+AQ8BgAEBAgAB8AEPAf8BHwH8AT8CAAHgAYcB/wGzAfwBPwIAAeEB4wH/AeMB/AE/AgAB4wHz
Af8B5wH8AT8CAAG/A/8B/AE/B/8BfwT/AQABfwHAAQcB8AEPAQABAQEAAX8BwAEHAcABAwEAAQEBAAEH
AcABBwGAAQMBAAEBAQABBwHAAQcBgAEDAQABAQEAAQcBwAEHAYABAwEAAQEBAAFHAcABBwGAAQMBAAEB
AQABRwHAAQcBgAEDAQABAQEAAUcBwAEHAYABAwEAAQEB/AEBAcABBwGAAQMBAAEBAfwBAQHAAQcBgAED
AQABAQH8AQEBwAEHAYABAwEAAQEB/AEBAcABBwHAAQMBAAEBAfwBAQHAAQcBwAEDAQABAQH8AQEBwAEH
AeABBwEAAQEB/AEBAcABBwT/AfwBAQHAAQcL
</value>
</data>
</root>

View File

@ -0,0 +1,314 @@
namespace BismNormalizer.TabularCompare.UI
{
partial class ConnectionsAlmt
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.cboSourceDatabase = new System.Windows.Forms.ComboBox();
this.cboSourceServer = new System.Windows.Forms.ComboBox();
this.label2 = new System.Windows.Forms.Label();
this.label1 = new System.Windows.Forms.Label();
this.pnlSourceDb = new System.Windows.Forms.Panel();
this.grpSource = new System.Windows.Forms.GroupBox();
this.btnOK = new System.Windows.Forms.Button();
this.btnCancel = new System.Windows.Forms.Button();
this.btnSwitch = new System.Windows.Forms.Button();
this.panel2 = new System.Windows.Forms.Panel();
this.panel1 = new System.Windows.Forms.Panel();
this.grpTarget = new System.Windows.Forms.GroupBox();
this.label3 = new System.Windows.Forms.Label();
this.label4 = new System.Windows.Forms.Label();
this.pnlTargetDb = new System.Windows.Forms.Panel();
this.cboTargetServer = new System.Windows.Forms.ComboBox();
this.cboTargetDatabase = new System.Windows.Forms.ComboBox();
this.pnlSourceDb.SuspendLayout();
this.grpSource.SuspendLayout();
this.panel2.SuspendLayout();
this.panel1.SuspendLayout();
this.grpTarget.SuspendLayout();
this.pnlTargetDb.SuspendLayout();
this.SuspendLayout();
//
// cboSourceDatabase
//
this.cboSourceDatabase.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.cboSourceDatabase.FormattingEnabled = true;
this.cboSourceDatabase.Location = new System.Drawing.Point(26, 87);
this.cboSourceDatabase.Margin = new System.Windows.Forms.Padding(7);
this.cboSourceDatabase.MaxDropDownItems = 11;
this.cboSourceDatabase.Name = "cboSourceDatabase";
this.cboSourceDatabase.Size = new System.Drawing.Size(858, 37);
this.cboSourceDatabase.TabIndex = 2;
this.cboSourceDatabase.Enter += new System.EventHandler(this.cboSourceDatabase_Enter);
//
// cboSourceServer
//
this.cboSourceServer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.cboSourceServer.FormattingEnabled = true;
this.cboSourceServer.Location = new System.Drawing.Point(26, 16);
this.cboSourceServer.Margin = new System.Windows.Forms.Padding(7);
this.cboSourceServer.MaxDropDownItems = 11;
this.cboSourceServer.Name = "cboSourceServer";
this.cboSourceServer.Size = new System.Drawing.Size(858, 37);
this.cboSourceServer.TabIndex = 1;
this.cboSourceServer.TextChanged += new System.EventHandler(this.cboSourceServer_TextChanged);
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(18, 154);
this.label2.Margin = new System.Windows.Forms.Padding(7, 0, 7, 0);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(94, 29);
this.label2.TabIndex = 2;
this.label2.Text = "Dataset";
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(14, 83);
this.label1.Margin = new System.Windows.Forms.Padding(7, 0, 7, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(134, 29);
this.label1.TabIndex = 1;
this.label1.Text = "Workspace";
//
// pnlSourceDb
//
this.pnlSourceDb.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.pnlSourceDb.Controls.Add(this.cboSourceServer);
this.pnlSourceDb.Controls.Add(this.cboSourceDatabase);
this.pnlSourceDb.Location = new System.Drawing.Point(147, 60);
this.pnlSourceDb.Margin = new System.Windows.Forms.Padding(7);
this.pnlSourceDb.Name = "pnlSourceDb";
this.pnlSourceDb.Size = new System.Drawing.Size(910, 147);
this.pnlSourceDb.TabIndex = 1;
//
// grpSource
//
this.grpSource.Controls.Add(this.label1);
this.grpSource.Controls.Add(this.label2);
this.grpSource.Controls.Add(this.pnlSourceDb);
this.grpSource.Dock = System.Windows.Forms.DockStyle.Top;
this.grpSource.Location = new System.Drawing.Point(0, 0);
this.grpSource.Margin = new System.Windows.Forms.Padding(7);
this.grpSource.Name = "grpSource";
this.grpSource.Padding = new System.Windows.Forms.Padding(7);
this.grpSource.Size = new System.Drawing.Size(1069, 263);
this.grpSource.TabIndex = 16;
this.grpSource.TabStop = false;
this.grpSource.Text = "Source";
//
// btnOK
//
this.btnOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK;
this.btnOK.Location = new System.Drawing.Point(696, 23);
this.btnOK.Margin = new System.Windows.Forms.Padding(7);
this.btnOK.Name = "btnOK";
this.btnOK.Size = new System.Drawing.Size(161, 51);
this.btnOK.TabIndex = 6;
this.btnOK.Text = "OK";
this.btnOK.UseVisualStyleBackColor = true;
this.btnOK.Click += new System.EventHandler(this.btnOK_Click);
//
// btnCancel
//
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.btnCancel.Location = new System.Drawing.Point(871, 23);
this.btnCancel.Margin = new System.Windows.Forms.Padding(7);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(166, 51);
this.btnCancel.TabIndex = 7;
this.btnCancel.Text = "Cancel";
this.btnCancel.UseVisualStyleBackColor = true;
//
// btnSwitch
//
this.btnSwitch.BackgroundImage = global::BismNormalizer.Resources.ButtonSwitch;
this.btnSwitch.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center;
this.btnSwitch.Location = new System.Drawing.Point(467, 6);
this.btnSwitch.Margin = new System.Windows.Forms.Padding(7);
this.btnSwitch.Name = "btnSwitch";
this.btnSwitch.Size = new System.Drawing.Size(130, 71);
this.btnSwitch.TabIndex = 3;
this.btnSwitch.UseVisualStyleBackColor = true;
this.btnSwitch.Click += new System.EventHandler(this.btnSwitch_Click);
//
// panel2
//
this.panel2.Controls.Add(this.btnCancel);
this.panel2.Controls.Add(this.btnOK);
this.panel2.Dock = System.Windows.Forms.DockStyle.Bottom;
this.panel2.Location = new System.Drawing.Point(0, 627);
this.panel2.Name = "panel2";
this.panel2.Size = new System.Drawing.Size(1069, 100);
this.panel2.TabIndex = 22;
//
// panel1
//
this.panel1.Controls.Add(this.btnSwitch);
this.panel1.Dock = System.Windows.Forms.DockStyle.Top;
this.panel1.Location = new System.Drawing.Point(0, 263);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(1069, 84);
this.panel1.TabIndex = 25;
//
// grpTarget
//
this.grpTarget.Controls.Add(this.label3);
this.grpTarget.Controls.Add(this.label4);
this.grpTarget.Controls.Add(this.pnlTargetDb);
this.grpTarget.Dock = System.Windows.Forms.DockStyle.Top;
this.grpTarget.Location = new System.Drawing.Point(0, 347);
this.grpTarget.Margin = new System.Windows.Forms.Padding(7);
this.grpTarget.Name = "grpTarget";
this.grpTarget.Padding = new System.Windows.Forms.Padding(7);
this.grpTarget.Size = new System.Drawing.Size(1069, 268);
this.grpTarget.TabIndex = 26;
this.grpTarget.TabStop = false;
this.grpTarget.Text = "Target";
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(15, 83);
this.label3.Margin = new System.Windows.Forms.Padding(7, 0, 7, 0);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(134, 29);
this.label3.TabIndex = 4;
this.label3.Text = "Workspace";
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(15, 154);
this.label4.Margin = new System.Windows.Forms.Padding(7, 0, 7, 0);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(94, 29);
this.label4.TabIndex = 5;
this.label4.Text = "Dataset";
//
// pnlTargetDb
//
this.pnlTargetDb.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.pnlTargetDb.Controls.Add(this.cboTargetServer);
this.pnlTargetDb.Controls.Add(this.cboTargetDatabase);
this.pnlTargetDb.Location = new System.Drawing.Point(144, 60);
this.pnlTargetDb.Margin = new System.Windows.Forms.Padding(7);
this.pnlTargetDb.Name = "pnlTargetDb";
this.pnlTargetDb.Size = new System.Drawing.Size(911, 147);
this.pnlTargetDb.TabIndex = 15;
//
// cboTargetServer
//
this.cboTargetServer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.cboTargetServer.FormattingEnabled = true;
this.cboTargetServer.Location = new System.Drawing.Point(26, 16);
this.cboTargetServer.Margin = new System.Windows.Forms.Padding(7);
this.cboTargetServer.MaxDropDownItems = 11;
this.cboTargetServer.Name = "cboTargetServer";
this.cboTargetServer.Size = new System.Drawing.Size(862, 37);
this.cboTargetServer.TabIndex = 4;
this.cboTargetServer.TextChanged += new System.EventHandler(this.cboTargetServer_TextChanged);
//
// cboTargetDatabase
//
this.cboTargetDatabase.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.cboTargetDatabase.FormattingEnabled = true;
this.cboTargetDatabase.Location = new System.Drawing.Point(26, 87);
this.cboTargetDatabase.Margin = new System.Windows.Forms.Padding(7);
this.cboTargetDatabase.MaxDropDownItems = 11;
this.cboTargetDatabase.Name = "cboTargetDatabase";
this.cboTargetDatabase.Size = new System.Drawing.Size(862, 37);
this.cboTargetDatabase.TabIndex = 5;
this.cboTargetDatabase.Enter += new System.EventHandler(this.cboTargetDatabase_Enter);
//
// Connections
//
this.AcceptButton = this.btnOK;
this.AutoScaleDimensions = new System.Drawing.SizeF(14F, 29F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.btnCancel;
this.ClientSize = new System.Drawing.Size(1069, 727);
this.Controls.Add(this.grpTarget);
this.Controls.Add(this.panel1);
this.Controls.Add(this.panel2);
this.Controls.Add(this.grpSource);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.Margin = new System.Windows.Forms.Padding(7);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "Connections";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.Text = "Connections";
this.Load += new System.EventHandler(this.Connections_Load);
this.pnlSourceDb.ResumeLayout(false);
this.grpSource.ResumeLayout(false);
this.grpSource.PerformLayout();
this.panel2.ResumeLayout(false);
this.panel1.ResumeLayout(false);
this.grpTarget.ResumeLayout(false);
this.grpTarget.PerformLayout();
this.pnlTargetDb.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.ComboBox cboSourceDatabase;
private System.Windows.Forms.ComboBox cboSourceServer;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Panel pnlSourceDb;
private System.Windows.Forms.GroupBox grpSource;
private System.Windows.Forms.Button btnOK;
private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.Button btnSwitch;
private System.Windows.Forms.Panel panel2;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.GroupBox grpTarget;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.Panel pnlTargetDb;
private System.Windows.Forms.ComboBox cboTargetServer;
private System.Windows.Forms.ComboBox cboTargetDatabase;
}
}

View File

@ -0,0 +1,243 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using Microsoft.AnalysisServices;
using System.Drawing;
namespace BismNormalizer.TabularCompare.UI
{
public partial class ConnectionsAlmt : Form
{
private ComparisonInfo _comparisonInfo;
private float _dpiScaleFactor;
private bool _sourceDatabaseBound = false;
private bool _targetDatabaseBound = false;
public ConnectionsAlmt()
{
InitializeComponent();
}
private void Connections_Load(object sender, EventArgs e)
{
this.Width = Convert.ToInt32(this.Width * 1.3);
if (_dpiScaleFactor > 1)
{
//DPI
float dpiScaleFactorFudged = _dpiScaleFactor * HighDPIUtils.PrimaryFudgeFactor;
float fudgeFactorWidth = 0.95f;
this.Scale(new SizeF(dpiScaleFactorFudged * (_dpiScaleFactor > 1.7 ? 1 : HighDPIUtils.SecondaryFudgeFactor), dpiScaleFactorFudged * HighDPIUtils.SecondaryFudgeFactor));
this.Width = Convert.ToInt32(this.Width * dpiScaleFactorFudged * fudgeFactorWidth);
foreach (Control control in HighDPIUtils.GetChildInControl(this)) //.OfType<Button>())
{
if (control is GroupBox || control is Button)
{
control.Font = new Font(control.Font.FontFamily,
control.Font.Size * dpiScaleFactorFudged * HighDPIUtils.SecondaryFudgeFactor,
control.Font.Style);
}
if (control is GroupBox || control.Name == "btnSwitch")
{
control.Width = Convert.ToInt32(control.Width * dpiScaleFactorFudged * fudgeFactorWidth);
}
if (control is ComboBox)
{
control.Width = Convert.ToInt32(control.Width * fudgeFactorWidth);
}
if (control is Panel)
{
control.Left = Convert.ToInt32(control.Left * dpiScaleFactorFudged);
}
}
this.btnSwitch.Left = grpSource.Right + Convert.ToInt32(12 * dpiScaleFactorFudged);
this.grpTarget.Left = btnSwitch.Right + Convert.ToInt32(12 * dpiScaleFactorFudged);
}
cboSourceServer.DataSource = ComparisonControl.ReverseArray<string>(Settings.Default.SourceServerAutoCompleteEntries.Substring(0, Settings.Default.SourceServerAutoCompleteEntries.Length - 1).Split("|".ToCharArray()));
cboTargetServer.DataSource = ComparisonControl.ReverseArray<string>(Settings.Default.TargetServerAutoCompleteEntries.Substring(0, Settings.Default.TargetServerAutoCompleteEntries.Length - 1).Split("|".ToCharArray()));
cboSourceDatabase.Text = Settings.Default.SourceCatalog;
cboTargetDatabase.Text = Settings.Default.TargetCatalog;
bool boundTargetDatabase = false;
BindSourceConnectionInfo();
BindTargetConnectionInfo(out boundTargetDatabase);
}
private bool BindSourceConnectionInfo()
{
bool returnVal = false;
if (_comparisonInfo?.ConnectionInfoSource != null)
{
if (!String.IsNullOrEmpty(_comparisonInfo.ConnectionInfoSource.ServerName) && !String.IsNullOrEmpty(_comparisonInfo.ConnectionInfoSource.DatabaseName))
{
cboSourceServer.Text = _comparisonInfo.ConnectionInfoSource.ServerName;
cboSourceDatabase.Text = _comparisonInfo.ConnectionInfoSource.DatabaseName;
returnVal = true;
}
}
return returnVal;
}
private bool BindTargetConnectionInfo(out bool boundTargetDatabase)
{
bool returnVal = false;
boundTargetDatabase = false;
if (_comparisonInfo?.ConnectionInfoTarget != null)
{
if (!String.IsNullOrEmpty(_comparisonInfo.ConnectionInfoTarget.ServerName) && !String.IsNullOrEmpty(_comparisonInfo.ConnectionInfoTarget.DatabaseName))
{
cboTargetServer.Text = _comparisonInfo.ConnectionInfoTarget.ServerName;
cboTargetDatabase.Text = _comparisonInfo.ConnectionInfoTarget.DatabaseName;
boundTargetDatabase = true;
returnVal = true;
}
}
return returnVal;
}
private static void IterateProject(SortedList projects, EnvDTE.Project project, string derivedProjectName = "")
{
if (project.ProjectItems != null) //if project is unloaded, its ProjectItems==null
{
derivedProjectName = AppendProjectName(project, derivedProjectName);
if (project.FileName.EndsWith(".smproj"))
{
projects.Add(derivedProjectName, project);
}
else if (project.Kind == "{66A26720-8FB5-11D2-AA7E-00C04F688DDE}")
{
foreach (EnvDTE.ProjectItem projectItem in project.ProjectItems)
{
if (projectItem.SubProject != null)
{
IterateProject(projects, projectItem.SubProject, derivedProjectName);
}
}
}
}
}
private static string AppendProjectName(EnvDTE.Project project, string derivedProjectName)
{
if (derivedProjectName != "")
{
derivedProjectName += "\\";
}
derivedProjectName += project.Name;
return derivedProjectName;
}
EnvDTE80.DTE2 _dte; //EnvDTE._DTE _dte;
public EnvDTE80.DTE2 Dte // EnvDTE._DTE DTE
{
get { return _dte; }
set { _dte = value; }
}
public ComparisonInfo ComparisonInfo
{
get { return _comparisonInfo; }
set { _comparisonInfo = value; }
}
public float DpiScaleFactor
{
get { return _dpiScaleFactor; }
set { _dpiScaleFactor = value; }
}
private void btnOK_Click(object sender, EventArgs e)
{
_comparisonInfo.ConnectionInfoSource.UseProject = false;
_comparisonInfo.ConnectionInfoSource.ServerName = cboSourceServer.Text;
_comparisonInfo.ConnectionInfoSource.DatabaseName = cboSourceDatabase.Text;
_comparisonInfo.ConnectionInfoSource.ProjectName = null;
_comparisonInfo.ConnectionInfoSource.ProjectFile = null;
_comparisonInfo.ConnectionInfoTarget.UseProject = false;
_comparisonInfo.ConnectionInfoTarget.ServerName = cboTargetServer.Text;
_comparisonInfo.ConnectionInfoTarget.DatabaseName = cboTargetDatabase.Text;
_comparisonInfo.ConnectionInfoTarget.ProjectName = null;
_comparisonInfo.ConnectionInfoTarget.ProjectFile = null;
}
private void btnSwitch_Click(object sender, EventArgs e)
{
ConnectionInfo infoSourceTemp = new ConnectionInfo();
infoSourceTemp.ServerName = cboSourceServer.Text;
infoSourceTemp.DatabaseName = cboSourceDatabase.Text;
cboSourceServer.Text = cboTargetServer.Text;
cboSourceDatabase.Text = cboTargetDatabase.Text;
cboTargetServer.Text = infoSourceTemp.ServerName;
cboTargetDatabase.Text = infoSourceTemp.DatabaseName;
}
private void cboSourceServer_TextChanged(object sender, EventArgs e)
{
_sourceDatabaseBound = false;
}
private void cboTargetServer_TextChanged(object sender, EventArgs e)
{
_targetDatabaseBound = false;
}
private void cboSourceDatabase_Enter(object sender, EventArgs e)
{
if (!_sourceDatabaseBound && cboSourceServer.Text != "")
{
BindDatabaseList(cboSourceServer.Text, cboSourceDatabase);
_sourceDatabaseBound = true;
}
}
private void cboTargetDatabase_Enter(object sender, EventArgs e)
{
if (!_targetDatabaseBound && cboTargetServer.Text != "")
{
BindDatabaseList(cboTargetServer.Text, cboTargetDatabase);
_targetDatabaseBound = true;
}
}
private void BindDatabaseList(string serverName, ComboBox cboCatalog)
{
try
{
// bind to databases from server
string currentDb = cboCatalog.Text;
// discover databases
Server server = new Server();
server.Connect($"Provider=MSOLAP;Data Source={serverName};");
List<string> databases = new List<string>();
foreach (Database database in server.Databases)
{
databases.Add(database.Name);
}
databases.Sort();
cboCatalog.DataSource = databases;
cboCatalog.Text = currentDb;
}
catch (Exception)
{ // if user entered duff server name, just ignore
cboCatalog.DataSource = null;
}
}
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -78,7 +78,7 @@ namespace BismNormalizer.TabularCompare.UI
}
catch (Exception exc)
{
MessageBox.Show(exc.Message, "BISM Normalizer", MessageBoxButtons.OK, MessageBoxIcon.Error);
MessageBox.Show(exc.Message, _comparisonInfo.AppName, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
@ -122,7 +122,7 @@ namespace BismNormalizer.TabularCompare.UI
btnStopProcessing.Enabled = false;
btnClose.Enabled = true;
btnClose.Select();
//btnClose.Select();
}
private delegate void SetErrorStatusDelegate(string errorMessage);
@ -169,7 +169,7 @@ namespace BismNormalizer.TabularCompare.UI
private void btnStopProcessing_Click(object sender, EventArgs e)
{
if (MessageBox.Show("Are you sure you want to attempt to stop processing?", "BISM Normalizer", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
if (MessageBox.Show("Are you sure you want to attempt to stop processing?", _comparisonInfo.AppName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
{
return;
}

View File

@ -35,14 +35,16 @@
this.chkMeasureDependencies = new System.Windows.Forms.CheckBox();
this.chkPerspectives = new System.Windows.Forms.CheckBox();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.chkRetainPolicyPartitions = new System.Windows.Forms.CheckBox();
this.chkRetainPartitions = new System.Windows.Forms.CheckBox();
this.chkMergeCultures = new System.Windows.Forms.CheckBox();
this.chkCultures = new System.Windows.Forms.CheckBox();
this.chkMergePerspectives = new System.Windows.Forms.CheckBox();
this.chkRetainPartitions = new System.Windows.Forms.CheckBox();
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.chkAffectedTables = new System.Windows.Forms.CheckBox();
this.cboProcessingOption = new System.Windows.Forms.ComboBox();
this.label1 = new System.Windows.Forms.Label();
this.chkRetainStorageMode = new System.Windows.Forms.CheckBox();
this.groupBox1.SuspendLayout();
this.groupBox2.SuspendLayout();
this.SuspendLayout();
@ -51,9 +53,10 @@
//
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.btnCancel.Location = new System.Drawing.Point(274, 393);
this.btnCancel.Location = new System.Drawing.Point(411, 673);
this.btnCancel.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(75, 23);
this.btnCancel.Size = new System.Drawing.Size(112, 35);
this.btnCancel.TabIndex = 21;
this.btnCancel.Text = "Cancel";
this.btnCancel.UseVisualStyleBackColor = true;
@ -62,9 +65,10 @@
//
this.btnOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK;
this.btnOK.Location = new System.Drawing.Point(193, 393);
this.btnOK.Location = new System.Drawing.Point(290, 673);
this.btnOK.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.btnOK.Name = "btnOK";
this.btnOK.Size = new System.Drawing.Size(75, 23);
this.btnOK.Size = new System.Drawing.Size(112, 35);
this.btnOK.TabIndex = 20;
this.btnOK.Text = "OK";
this.btnOK.UseVisualStyleBackColor = true;
@ -75,9 +79,10 @@
this.chkRoles.AutoSize = true;
this.chkRoles.Checked = true;
this.chkRoles.CheckState = System.Windows.Forms.CheckState.Checked;
this.chkRoles.Location = new System.Drawing.Point(13, 124);
this.chkRoles.Location = new System.Drawing.Point(20, 186);
this.chkRoles.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.chkRoles.Name = "chkRoles";
this.chkRoles.Size = new System.Drawing.Size(86, 17);
this.chkRoles.Size = new System.Drawing.Size(118, 24);
this.chkRoles.TabIndex = 5;
this.chkRoles.Text = "Include roles";
this.chkRoles.UseVisualStyleBackColor = true;
@ -85,9 +90,10 @@
// chkPartitions
//
this.chkPartitions.AutoSize = true;
this.chkPartitions.Location = new System.Drawing.Point(13, 152);
this.chkPartitions.Location = new System.Drawing.Point(20, 232);
this.chkPartitions.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.chkPartitions.Name = "chkPartitions";
this.chkPartitions.Size = new System.Drawing.Size(224, 17);
this.chkPartitions.Size = new System.Drawing.Size(327, 24);
this.chkPartitions.TabIndex = 6;
this.chkPartitions.Text = "Consider partitions when comparing tables";
this.chkPartitions.UseVisualStyleBackColor = true;
@ -99,9 +105,10 @@
this.chkMeasureDependencies.AutoSize = true;
this.chkMeasureDependencies.Checked = true;
this.chkMeasureDependencies.CheckState = System.Windows.Forms.CheckState.Checked;
this.chkMeasureDependencies.Location = new System.Drawing.Point(13, 208);
this.chkMeasureDependencies.Location = new System.Drawing.Point(20, 397);
this.chkMeasureDependencies.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.chkMeasureDependencies.Name = "chkMeasureDependencies";
this.chkMeasureDependencies.Size = new System.Drawing.Size(47, 17);
this.chkMeasureDependencies.Size = new System.Drawing.Size(61, 24);
this.chkMeasureDependencies.TabIndex = 7;
this.chkMeasureDependencies.Text = "XXX";
this.chkMeasureDependencies.UseVisualStyleBackColor = true;
@ -109,9 +116,10 @@
// chkPerspectives
//
this.chkPerspectives.AutoSize = true;
this.chkPerspectives.Location = new System.Drawing.Point(13, 25);
this.chkPerspectives.Location = new System.Drawing.Point(20, 38);
this.chkPerspectives.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.chkPerspectives.Name = "chkPerspectives";
this.chkPerspectives.Size = new System.Drawing.Size(124, 17);
this.chkPerspectives.Size = new System.Drawing.Size(173, 24);
this.chkPerspectives.TabIndex = 3;
this.chkPerspectives.Text = "Include perspectives";
this.chkPerspectives.UseVisualStyleBackColor = true;
@ -122,6 +130,8 @@
this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.groupBox1.Controls.Add(this.chkRetainStorageMode);
this.groupBox1.Controls.Add(this.chkRetainPolicyPartitions);
this.groupBox1.Controls.Add(this.chkRetainPartitions);
this.groupBox1.Controls.Add(this.chkMergeCultures);
this.groupBox1.Controls.Add(this.chkCultures);
@ -130,20 +140,47 @@
this.groupBox1.Controls.Add(this.chkMeasureDependencies);
this.groupBox1.Controls.Add(this.chkPartitions);
this.groupBox1.Controls.Add(this.chkRoles);
this.groupBox1.Location = new System.Drawing.Point(12, 12);
this.groupBox1.Location = new System.Drawing.Point(18, 18);
this.groupBox1.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(337, 256);
this.groupBox1.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.groupBox1.Size = new System.Drawing.Size(506, 471);
this.groupBox1.TabIndex = 22;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "Comparison Options";
//
// chkRetainPolicyPartitions
//
this.chkRetainPolicyPartitions.AutoSize = true;
this.chkRetainPolicyPartitions.Enabled = false;
this.chkRetainPolicyPartitions.Location = new System.Drawing.Point(52, 312);
this.chkRetainPolicyPartitions.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.chkRetainPolicyPartitions.Name = "chkRetainPolicyPartitions";
this.chkRetainPolicyPartitions.Size = new System.Drawing.Size(322, 24);
this.chkRetainPolicyPartitions.TabIndex = 11;
this.chkRetainPolicyPartitions.Text = "Retain only refresh-policy based partitions";
this.chkRetainPolicyPartitions.UseVisualStyleBackColor = true;
//
// chkRetainPartitions
//
this.chkRetainPartitions.AutoSize = true;
this.chkRetainPartitions.Location = new System.Drawing.Point(20, 280);
this.chkRetainPartitions.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.chkRetainPartitions.Name = "chkRetainPartitions";
this.chkRetainPartitions.Size = new System.Drawing.Size(270, 24);
this.chkRetainPartitions.TabIndex = 10;
this.chkRetainPartitions.Text = "For table updates, retain partitions";
this.chkRetainPartitions.UseVisualStyleBackColor = true;
this.chkRetainPartitions.CheckedChanged += new System.EventHandler(this.ChkRetainPartitions_CheckedChanged);
//
// chkMergeCultures
//
this.chkMergeCultures.AutoSize = true;
this.chkMergeCultures.Enabled = false;
this.chkMergeCultures.Location = new System.Drawing.Point(37, 98);
this.chkMergeCultures.Location = new System.Drawing.Point(52, 148);
this.chkMergeCultures.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.chkMergeCultures.Name = "chkMergeCultures";
this.chkMergeCultures.Size = new System.Drawing.Size(270, 17);
this.chkMergeCultures.Size = new System.Drawing.Size(398, 24);
this.chkMergeCultures.TabIndex = 9;
this.chkMergeCultures.Text = "For culture updates, merge translations (not replace)";
this.chkMergeCultures.UseVisualStyleBackColor = true;
@ -151,9 +188,10 @@
// chkCultures
//
this.chkCultures.AutoSize = true;
this.chkCultures.Location = new System.Drawing.Point(15, 75);
this.chkCultures.Location = new System.Drawing.Point(20, 115);
this.chkCultures.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.chkCultures.Name = "chkCultures";
this.chkCultures.Size = new System.Drawing.Size(101, 17);
this.chkCultures.Size = new System.Drawing.Size(140, 24);
this.chkCultures.TabIndex = 8;
this.chkCultures.Text = "Include cultures";
this.chkCultures.UseVisualStyleBackColor = true;
@ -163,23 +201,14 @@
//
this.chkMergePerspectives.AutoSize = true;
this.chkMergePerspectives.Enabled = false;
this.chkMergePerspectives.Location = new System.Drawing.Point(35, 48);
this.chkMergePerspectives.Location = new System.Drawing.Point(52, 71);
this.chkMergePerspectives.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.chkMergePerspectives.Name = "chkMergePerspectives";
this.chkMergePerspectives.Size = new System.Drawing.Size(287, 17);
this.chkMergePerspectives.Size = new System.Drawing.Size(420, 24);
this.chkMergePerspectives.TabIndex = 4;
this.chkMergePerspectives.Text = "For perspective updates, merge selections (not replace)";
this.chkMergePerspectives.UseVisualStyleBackColor = true;
//
// chkRetainPartitions
//
this.chkRetainPartitions.AutoSize = true;
this.chkRetainPartitions.Location = new System.Drawing.Point(13, 181);
this.chkRetainPartitions.Name = "chkRetainPartitions";
this.chkRetainPartitions.Size = new System.Drawing.Size(247, 17);
this.chkRetainPartitions.TabIndex = 10;
this.chkRetainPartitions.Text = "For table updates, retain partitions (not replace)";
this.chkRetainPartitions.UseVisualStyleBackColor = true;
//
// groupBox2
//
this.groupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
@ -187,9 +216,11 @@
this.groupBox2.Controls.Add(this.chkAffectedTables);
this.groupBox2.Controls.Add(this.cboProcessingOption);
this.groupBox2.Controls.Add(this.label1);
this.groupBox2.Location = new System.Drawing.Point(12, 274);
this.groupBox2.Location = new System.Drawing.Point(18, 499);
this.groupBox2.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(337, 101);
this.groupBox2.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.groupBox2.Size = new System.Drawing.Size(506, 155);
this.groupBox2.TabIndex = 23;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "Database Deployment";
@ -199,9 +230,10 @@
this.chkAffectedTables.AutoSize = true;
this.chkAffectedTables.Checked = true;
this.chkAffectedTables.CheckState = System.Windows.Forms.CheckState.Checked;
this.chkAffectedTables.Location = new System.Drawing.Point(13, 64);
this.chkAffectedTables.Location = new System.Drawing.Point(20, 98);
this.chkAffectedTables.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.chkAffectedTables.Name = "chkAffectedTables";
this.chkAffectedTables.Size = new System.Drawing.Size(159, 17);
this.chkAffectedTables.Size = new System.Drawing.Size(227, 24);
this.chkAffectedTables.TabIndex = 9;
this.chkAffectedTables.Text = "Process only affected tables";
this.chkAffectedTables.UseVisualStyleBackColor = true;
@ -215,38 +247,52 @@
"Default",
"Do Not Process",
"Full"});
this.cboProcessingOption.Location = new System.Drawing.Point(114, 25);
this.cboProcessingOption.Location = new System.Drawing.Point(171, 38);
this.cboProcessingOption.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.cboProcessingOption.Name = "cboProcessingOption";
this.cboProcessingOption.Size = new System.Drawing.Size(131, 21);
this.cboProcessingOption.Size = new System.Drawing.Size(194, 28);
this.cboProcessingOption.TabIndex = 8;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(12, 28);
this.label1.Location = new System.Drawing.Point(18, 43);
this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(96, 13);
this.label1.Size = new System.Drawing.Size(142, 20);
this.label1.TabIndex = 0;
this.label1.Text = "Processing Option:";
//
// chkRetainStorageMode
//
this.chkRetainStorageMode.AutoSize = true;
this.chkRetainStorageMode.Location = new System.Drawing.Point(20, 357);
this.chkRetainStorageMode.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.chkRetainStorageMode.Name = "chkRetainStorageMode";
this.chkRetainStorageMode.Size = new System.Drawing.Size(303, 24);
this.chkRetainStorageMode.TabIndex = 12;
this.chkRetainStorageMode.Text = "For table updates, retain storage mode";
this.chkRetainStorageMode.UseVisualStyleBackColor = true;
//
// Options
//
this.AcceptButton = this.btnOK;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.btnCancel;
this.ClientSize = new System.Drawing.Size(361, 428);
this.ClientSize = new System.Drawing.Size(542, 733);
this.Controls.Add(this.groupBox2);
this.Controls.Add(this.groupBox1);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.btnOK);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "Options";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.Text = "BISM Normalizer Options";
this.Text = "Options";
this.Load += new System.EventHandler(this.Options_Load);
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Options_KeyDown);
this.groupBox1.ResumeLayout(false);
@ -274,5 +320,7 @@
private System.Windows.Forms.CheckBox chkMergeCultures;
private System.Windows.Forms.CheckBox chkCultures;
private System.Windows.Forms.CheckBox chkRetainPartitions;
private System.Windows.Forms.CheckBox chkRetainPolicyPartitions;
private System.Windows.Forms.CheckBox chkRetainStorageMode;
}
}

View File

@ -51,6 +51,8 @@ namespace BismNormalizer.TabularCompare.UI
//chkActions.Checked = _comparisonInfo.OptionsInfo.OptionActions;
chkPartitions.Checked = _comparisonInfo.OptionsInfo.OptionPartitions;
chkRetainPartitions.Checked = _comparisonInfo.OptionsInfo.OptionRetainPartitions;
chkRetainPolicyPartitions.Checked = _comparisonInfo.OptionsInfo.OptionRetainPolicyPartitions;
chkRetainStorageMode.Checked = _comparisonInfo.OptionsInfo.OptionRetainStorageMode;
chkMeasureDependencies.Checked = _comparisonInfo.OptionsInfo.OptionMeasureDependencies;
string processingOption = _comparisonInfo.OptionsInfo.OptionProcessingOption.ToString();
cboProcessingOption.Text = processingOption == "DoNotProcess" ? "Do Not Process" : processingOption;
@ -69,6 +71,8 @@ namespace BismNormalizer.TabularCompare.UI
_comparisonInfo.OptionsInfo.OptionActions = false;
_comparisonInfo.OptionsInfo.OptionPartitions = chkPartitions.Checked;
_comparisonInfo.OptionsInfo.OptionRetainPartitions = chkRetainPartitions.Checked;
_comparisonInfo.OptionsInfo.OptionRetainPolicyPartitions = chkRetainPolicyPartitions.Checked;
_comparisonInfo.OptionsInfo.OptionRetainStorageMode = chkRetainStorageMode.Checked;
_comparisonInfo.OptionsInfo.OptionMeasureDependencies = chkMeasureDependencies.Checked;
_comparisonInfo.OptionsInfo.OptionProcessingOption = (ProcessingOption)Enum.Parse(typeof(ProcessingOption), cboProcessingOption.Text.Replace(" ", ""));
//_comparisonInfo.OptionsInfo.OptionTransaction = chkTransaction.Checked;
@ -88,15 +92,27 @@ namespace BismNormalizer.TabularCompare.UI
chkMergeCultures.Enabled = chkCultures.Checked;
}
private void ChkRetainPartitions_CheckedChanged(object sender, EventArgs e)
{
chkRetainPolicyPartitions.Enabled = chkRetainPartitions.Checked;
}
private void Options_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.Shift && e.KeyCode == Keys.D)
{
if (MessageBox.Show($"Are you sure you want to toggle 192 Device DPI from optimized for {(Settings.Default.OptionHighDpiLocal ? "local" : "Remote Desktop")} to {(Settings.Default.OptionHighDpiLocal ? "Remote Desktop" : "local")}?", "BISM Normalizer", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
if (MessageBox.Show($"Are you sure you want to toggle 192 Device DPI from optimized for {(Settings.Default.OptionHighDpiLocal ? "local" : "Remote Desktop")} to {(Settings.Default.OptionHighDpiLocal ? "Remote Desktop" : "local")}?", "ALM Toolkit", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
Settings.Default.OptionHighDpiLocal = !Settings.Default.OptionHighDpiLocal;
}
}
else if (e.Control && e.Shift && e.KeyCode == Keys.C)
{
if (MessageBox.Show($"Are you sure you want to {(Settings.Default.OptionCompositeModelsOverride ? "*disallow*" : "*allow*")} composite model comparisons on Analysis Services?", "ALM Toolkit", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
Settings.Default.OptionCompositeModelsOverride = !Settings.Default.OptionCompositeModelsOverride;
}
}
}
public ComparisonInfo ComparisonInfo
@ -110,5 +126,6 @@ namespace BismNormalizer.TabularCompare.UI
get { return _dpiScaleFactor; }
set { _dpiScaleFactor = value; }
}
}
}

View File

@ -221,6 +221,10 @@ namespace BismNormalizer.TabularCompare.UI
switch (comparisonObject.ComparisonObjectType)
{
// Tabular objecs
case ComparisonObjectType.Model:
node.ImageIndex = 25;
node.Cells[0].Value = treeIndentLevel1 + "Model";
break;
case ComparisonObjectType.DataSource:
node.ImageIndex = 0;
node.Cells[0].Value = treeIndentLevel1 + "Data Source";
@ -241,6 +245,10 @@ namespace BismNormalizer.TabularCompare.UI
node.ImageIndex = 4;
node.Cells[0].Value = treeIndentLevel2 + "KPI";
break;
case ComparisonObjectType.CalculationItem:
node.ImageIndex = 24;
node.Cells[0].Value = treeIndentLevel2 + "Calculation Item";
break;
case ComparisonObjectType.Expression:
node.ImageIndex = 22;
node.Cells[0].Value = treeIndentLevel1 + "Expression";
@ -261,6 +269,10 @@ namespace BismNormalizer.TabularCompare.UI
node.ImageIndex = 16;
node.Cells[0].Value = treeIndentLevel1 + "Action";
break;
//case ComparisonObjectType.RefreshPolicy:
// node.ImageIndex = 26;
// node.Cells[0].Value = treeIndentLevel1 + "Refresh Policy";
// break;
default:
break;

View File

@ -120,6 +120,10 @@ namespace BismNormalizer.TabularCompare.UI
TreeGridNode particularTypeNode = null;
switch (validationMessageType)
{
case ValidationMessageType.Model:
particularTypeNode = FindOrCreateTypeNode(topLevelNodeForHandle, "Model");
particularTypeNode.ImageIndex = 25;
break;
case ValidationMessageType.DataSource:
particularTypeNode = FindOrCreateTypeNode(topLevelNodeForHandle, "Data Sources");
particularTypeNode.ImageIndex = 0;
@ -140,6 +144,14 @@ namespace BismNormalizer.TabularCompare.UI
particularTypeNode = FindOrCreateTypeNode(topLevelNodeForHandle, "KPIs");
particularTypeNode.ImageIndex = 4;
break;
case ValidationMessageType.CalculationItem:
particularTypeNode = FindOrCreateTypeNode(topLevelNodeForHandle, "Calculation Items");
particularTypeNode.ImageIndex = 24;
break;
case ValidationMessageType.CalculationGroup:
particularTypeNode = FindOrCreateTypeNode(topLevelNodeForHandle, "Calculation Groups");
particularTypeNode.ImageIndex = 23;
break;
case ValidationMessageType.Expression:
particularTypeNode = FindOrCreateTypeNode(topLevelNodeForHandle, "Expression");
particularTypeNode.ImageIndex = 22;
@ -160,10 +172,18 @@ namespace BismNormalizer.TabularCompare.UI
particularTypeNode = FindOrCreateTypeNode(topLevelNodeForHandle, "Actions");
particularTypeNode.ImageIndex = 16;
break;
//case ValidationMessageType.RefreshPolicy:
// particularTypeNode = FindOrCreateTypeNode(topLevelNodeForHandle, "Refresh Policy");
// particularTypeNode.ImageIndex = 26;
// break;
case ValidationMessageType.MeasureCalculationDependency:
particularTypeNode = FindOrCreateTypeNode(topLevelNodeForHandle, "Measure Calculation Dependencies");
particularTypeNode.ImageIndex = 3;
break;
case ValidationMessageType.AggregationDependency:
particularTypeNode = FindOrCreateTypeNode(topLevelNodeForHandle, "Aggregation Dependencies");
particularTypeNode.ImageIndex = 2;
break;
default:
//Something is wrong, better get out of here.
return;

View File

@ -143,7 +143,8 @@ namespace BismNormalizer.TabularCompare.UI
_informationalMessageButton.Left = Convert.ToInt32(92 * _hpiScaleFactor) + pixelsPerDigit * warningCount.Length;
//where "informational messages" button ends
_fillerPanel.Left = Convert.ToInt32(268 * _hpiScaleFactor) + (pixelsPerDigit * warningCount.Length) + (pixelsPerDigit * informationalMessageCount.Length);
//5/15/2018: was ToInt32(268 * _hpiScaleFactor)
_fillerPanel.Left = Convert.ToInt32(250 * _hpiScaleFactor) + (pixelsPerDigit * warningCount.Length) + (pixelsPerDigit * informationalMessageCount.Length);
}
}
}

View File

@ -73,6 +73,15 @@
<setting name="OptionHighDpiLocal" serializeAs="String">
<value>True</value>
</setting>
<setting name="OptionCompositeModelsOverride" serializeAs="String">
<value>False</value>
</setting>
<setting name="OptionRetainPolicyPartitions" serializeAs="String">
<value>False</value>
</setting>
<setting name="OptionRetainStorageMode" serializeAs="String">
<value>False</value>
</setting>
</BismNormalizer.Settings>
</userSettings>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" /></startup>
@ -95,5 +104,9 @@
<bindingRedirect oldVersion="0.0.0.0-14.0.0.0" newVersion="14.0.0.0" />
</dependentAssembly>
</assemblyBinding>
<System.Windows.Forms.ApplicationConfigurationSection>
<add key="DpiAwareness" value="PerMonitorV2" />
<add key="EnableWindowsFormsHighDpiAutoResizing" value="false" />
</System.Windows.Forms.ApplicationConfigurationSection>
</runtime>
</configuration>

View File

@ -2,7 +2,7 @@
<packages>
<package id="EnvDTE" version="8.0.2" targetFramework="net472" />
<package id="EnvDTE80" version="8.0.3" targetFramework="net472" />
<package id="Microsoft.AnalysisServices.retail.amd64" version="16.3.0" targetFramework="net461" />
<package id="Microsoft.AnalysisServices.retail.amd64" version="18.0.5" targetFramework="net472" />
<package id="Microsoft.ApplicationInsights" version="2.8.1" targetFramework="net461" />
<package id="Microsoft.ApplicationInsights.Agent.Intercept" version="2.4.0" targetFramework="net461" />
<package id="Microsoft.ApplicationInsights.DependencyCollector" version="2.8.1" targetFramework="net461" />

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Id="BismNormalizer.ea2aeb43-64a6-4dee-8816-099fb44513fa" Version="4.0.1.10" Language="en-US" Publisher="BISM Normalizer" />
<Identity Id="BismNormalizer.ea2aeb43-64a6-4dee-8816-099fb44513fa" Version="4.0.1.12" Language="en-US" Publisher="BISM Normalizer" />
<DisplayName>BISM Normalizer</DisplayName>
<Description xml:space="preserve">BISM Normalizer manages Analysis Services tabular models</Description>
<MoreInfo>http://bism-normalizer.com/</MoreInfo>