mirror of
https://github.com/Microsoft/sql-server-samples.git
synced 2025-12-08 14:58:54 +00:00
Initial Commit
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
|
||||
</startup>
|
||||
<connectionStrings>
|
||||
<!--<add name="Db" connectionString="Server=tcp:peskount-hktest1.database.windows.net,1433;Database=PowerConsumption;User ID=demovm001@peskount-hktest1;Password=LetAzureIn0_;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"/>-->
|
||||
<add name="Db" connectionString="Server=.;Database=PowerConsumption;Integrated Security=True"/>
|
||||
</connectionStrings>
|
||||
<appSettings>
|
||||
<add key="insertSPName" value="InsertMeterMeasurement"/> <!--Stored Procedure Name-->
|
||||
<add key="numberOfTasks" value="50"/> <!--Number of concurrent async tasks that the Data Generator will use-->
|
||||
<add key="numberOfMeters" value="1000000"/> <!--Number of unique meter Ids-->
|
||||
<add key="batchSize" value="1000"/> <!--Row Batch Size that every task produces-->
|
||||
<add key="commandDelay" value="1500"/> <!--Delay between sql commands. You can set this to 0 for max high volume workload-->
|
||||
<add key="commandTimeout" value="600"/> <!--SQL Command Timeout-->
|
||||
<add key="enableShock" value="1"/> <!--Flag that turns on/off the data shock. This should be set to 0 for max high volume workload-->
|
||||
<add key="shockFrequency" value="35000"/> <!--The Frequency that the code sets the commandDelay to 0-->
|
||||
<add key="shockDuration" value="4000"/> <!--The Duration that the code keeps the commandDelay to 0-->
|
||||
<add key="rpsFrequency" value="2000"/> <!--How frequently the Data Generator Rows Per Second(RPS) is polled-->
|
||||
<add key="logFileName" value="log.txt"/> <!--Log File Path-->
|
||||
<add key="powerBIDesktopPath" value="C:\Program Files\Microsoft Power BI Desktop\bin\PBIDesktop.exe"/><!--PowerBIDesktop.exe path-->
|
||||
</appSettings>
|
||||
</configuration>
|
||||
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{BB747094-2CF6-46D2-8E6C-07E27CF0BCAA}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>ConsoleClient</RootNamespace>
|
||||
<AssemblyName>ConsoleClient</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\App.config">
|
||||
<Link>App.config</Link>
|
||||
</None>
|
||||
<None Include="..\PowerDashboard.pbix">
|
||||
<Link>Reports\PowerDashboard.pbix</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DataGenerator\DataGenerator.csproj">
|
||||
<Project>{d871b062-06a7-49e3-8bcd-8465b772fc52}</Project>
|
||||
<Name>DataGenerator</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -0,0 +1,246 @@
|
||||
/*----------------------------------------------------------------------------------
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
|
||||
EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
----------------------------------------------------------------------------------
|
||||
The example companies, organizations, products, domain names,
|
||||
e-mail addresses, logos, people, places, and events depicted
|
||||
herein are fictitious. No association with any real company,
|
||||
organization, product, domain name, email address, logo, person,
|
||||
places, or events is intended or should be inferred.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using DataGenerator;
|
||||
using System.Configuration;
|
||||
using System.Timers;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/*----------------------------------------------------------------------------------
|
||||
High Level Scenario:
|
||||
This code sample demonstrates how a SQL Server 2016 (or higher) memory optimized database could be used to ingest a very high input data rate
|
||||
and ultimately help improve the performance of applications with this scenario. The code simulates an IoT Smart Grid scenario where multiple
|
||||
IoT power meters are constantly sending electricity usage measurements to the database.
|
||||
|
||||
Details:
|
||||
This code sample simulates an IoT Smart Grid scenario where multiple IoT power meters are sending electricity usage measurements to a SQL Server memory optimized database.
|
||||
The Data Generator, that can be started either from the Console or the Windows Form client, produces a data generated spike to simulate a
|
||||
shock absorber scenario: https://blogs.technet.microsoft.com/dataplatforminsider/2013/09/19/in-memory-oltp-common-design-pattern-high-data-input-rateshock-absorber/.
|
||||
Every async task in the Data Generator produces a batch of records with random values in order to simulate the data of an IoT power meter.
|
||||
It then calls a natively compiled stored procedure, that accepts an memory optimized table valued parameter (TVP), to insert the data into an memory optimized SQL Server table.
|
||||
In addition to the in-memory features, the sample is leveraging System-Versioned Temporal Tables: https://msdn.microsoft.com/en-us/library/dn935015.aspx for building version history,
|
||||
Clustered Columnstore Index: https://msdn.microsoft.com/en-us/library/dn817827.aspx) for enabling real time operational analytics, and
|
||||
Power BI: https://powerbi.microsoft.com/en-us/desktop/ for data visualization.
|
||||
*/
|
||||
namespace ConsoleClient
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static SqlDataGenerator dataGenerator;
|
||||
static string connection;
|
||||
static string spName;
|
||||
static string logFileName;
|
||||
static string powerBIDesktopPath;
|
||||
static int tasks;
|
||||
static int meters;
|
||||
static int batchSize;
|
||||
static int delay;
|
||||
static int commandTimeout;
|
||||
static int shockFrequency;
|
||||
static int shockDuration;
|
||||
static int rpsFrequency;
|
||||
static int enableShock;
|
||||
static Timer mainTimer = new Timer();
|
||||
static Timer rpsTimer = new Timer();
|
||||
static Timer shockTimer = new Timer();
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Init();
|
||||
dataGenerator = new SqlDataGenerator(connection, spName, commandTimeout, meters, tasks, delay, batchSize, ExceptionCallback);
|
||||
|
||||
mainTimer.Elapsed += mainTimer_Tick;
|
||||
rpsTimer.Elapsed += rpsTimer_Tick;
|
||||
shockTimer.Elapsed += shockTimer_Tick;
|
||||
|
||||
string commandString = string.Empty;
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.WriteLine("***********************************************************");
|
||||
Console.WriteLine("* Data Generator *");
|
||||
Console.WriteLine("* *");
|
||||
Console.WriteLine("* Type commands to get started *");
|
||||
Console.WriteLine("* *");
|
||||
Console.WriteLine("***********************************************************");
|
||||
Console.WriteLine("");
|
||||
|
||||
// main command cycle
|
||||
while (!commandString.Equals("Exit"))
|
||||
{
|
||||
Console.ResetColor();
|
||||
Console.WriteLine("Enter command (start | stop | help | report | exit) >");
|
||||
commandString = Console.ReadLine();
|
||||
|
||||
switch (commandString.ToUpper())
|
||||
{
|
||||
case "START":
|
||||
Start();
|
||||
break;
|
||||
case "STOP":
|
||||
Stop();
|
||||
break;
|
||||
case "HELP":
|
||||
Help();
|
||||
break;
|
||||
case "REPORT":
|
||||
Report();
|
||||
break;
|
||||
case "EXIT":
|
||||
Console.WriteLine("Bye!");
|
||||
return;
|
||||
default:
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine("Invalid command.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
static void ExceptionCallback(int taskId, Exception exception)
|
||||
{
|
||||
HandleException(exception, taskId);
|
||||
}
|
||||
static void HandleException(Exception exception, int? taskId = null)
|
||||
{
|
||||
string ex = taskId?.ToString() + " - " + exception.Message + (exception.InnerException != null ? "\n\nInner Exception\n" + exception.InnerException : "");
|
||||
|
||||
Console.WriteLine(ex);
|
||||
using (StreamWriter w = File.AppendText(logFileName)) { w.WriteLine("\r\n{0}: {1}", DateTime.Now, ex); }
|
||||
}
|
||||
static async void Start()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!dataGenerator.IsRunning)
|
||||
{
|
||||
if(enableShock == 1) mainTimer.Start();
|
||||
rpsTimer.Start();
|
||||
|
||||
await dataGenerator.RunAsync();
|
||||
}
|
||||
}
|
||||
catch (Exception exception) { HandleException(exception); }
|
||||
}
|
||||
|
||||
static async void Stop()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (dataGenerator.IsRunning)
|
||||
{
|
||||
if (enableShock == 1) mainTimer.Stop();
|
||||
rpsTimer.Stop();
|
||||
if (enableShock == 1) shockTimer.Stop();
|
||||
|
||||
await dataGenerator.StopAsync();
|
||||
dataGenerator.RpsReset();
|
||||
}
|
||||
}
|
||||
catch (Exception exception) { HandleException(exception); }
|
||||
}
|
||||
|
||||
static void Report()
|
||||
{
|
||||
ProcessStartInfo psi = new ProcessStartInfo();
|
||||
psi.FileName = powerBIDesktopPath;
|
||||
psi.Arguments = @"Reports\PowerDashboard.pbix";
|
||||
Process.Start(psi);
|
||||
}
|
||||
static void Help()
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
Console.WriteLine("");
|
||||
Console.WriteLine("START - Starts the DataGenerator");
|
||||
Console.WriteLine("STOP - Stops the DataGenerator");
|
||||
Console.WriteLine("HELP - Displays this page");
|
||||
Console.WriteLine("REPORT - Launches the Power BI Report");
|
||||
Console.WriteLine("EXIT - Closes this program");
|
||||
Console.WriteLine("");
|
||||
}
|
||||
static void Init()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Read Config Settings
|
||||
connection = ConfigurationManager.ConnectionStrings["Db"].ConnectionString;
|
||||
spName = ConfigurationManager.AppSettings["insertSPName"];
|
||||
logFileName = ConfigurationManager.AppSettings["logFileName"];
|
||||
powerBIDesktopPath = ConfigurationManager.AppSettings["powerBIDesktopPath"];
|
||||
tasks = int.Parse(ConfigurationManager.AppSettings["numberOfTasks"]);
|
||||
meters = int.Parse(ConfigurationManager.AppSettings["numberOfMeters"]);
|
||||
batchSize = int.Parse(ConfigurationManager.AppSettings["batchSize"]);
|
||||
delay = int.Parse(ConfigurationManager.AppSettings["commandDelay"]);
|
||||
commandTimeout = int.Parse(ConfigurationManager.AppSettings["commandTimeout"]);
|
||||
shockFrequency = int.Parse(ConfigurationManager.AppSettings["shockFrequency"]);
|
||||
shockDuration = int.Parse(ConfigurationManager.AppSettings["shockDuration"]);
|
||||
enableShock = int.Parse(ConfigurationManager.AppSettings["enableShock"]);
|
||||
|
||||
rpsFrequency = int.Parse(ConfigurationManager.AppSettings["rpsFrequency"]);
|
||||
|
||||
// Initialize Timers
|
||||
mainTimer.Interval = shockFrequency;
|
||||
shockTimer.Interval = shockDuration;
|
||||
rpsTimer.Interval = rpsFrequency;
|
||||
|
||||
if (batchSize <= 0) throw new SqlDataGeneratorException("The Batch Size cannot be less or equal to zero.");
|
||||
|
||||
if (tasks <= 0) throw new SqlDataGeneratorException("Number Of Tasks cannot be less or equal to zero.");
|
||||
|
||||
if (delay < 0) throw new SqlDataGeneratorException("Delay cannot be less than zero");
|
||||
|
||||
if (meters <= 0) throw new SqlDataGeneratorException("Number Of Meters cannot be less than zero");
|
||||
|
||||
if (meters < batchSize * tasks) throw new SqlDataGeneratorException("Number Of Meters cannot be less than (Tasks * BatchSize).");
|
||||
}
|
||||
catch (Exception exception) { HandleException(exception); }
|
||||
}
|
||||
static void mainTimer_Tick(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
if (dataGenerator.IsRunning)
|
||||
{
|
||||
dataGenerator.Delay = 0;
|
||||
shockTimer.Start();
|
||||
}
|
||||
}
|
||||
static void rpsTimer_Tick(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
double rps = dataGenerator.Rps;
|
||||
if (dataGenerator.IsRunning)
|
||||
{
|
||||
if (dataGenerator.RunningTasks == 0) return;
|
||||
|
||||
if (rps > 0)
|
||||
{
|
||||
Console.SetCursorPosition(0, Console.CursorTop);
|
||||
Console.Write(string.Format("Rows Per Second (RPS):{0:#,#} ", rps).ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exception) { HandleException(exception); }
|
||||
}
|
||||
static void shockTimer_Tick(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
Random rand = new Random();
|
||||
dataGenerator.Delay = rand.Next(1500, 3000);
|
||||
shockTimer.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("ConsoleClient")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("ConsoleClient")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2016")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("bb747094-2cf6-46d2-8e6c-07e27cf0bcaa")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// 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("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DataGenerator
|
||||
{
|
||||
internal struct CancellableTask
|
||||
{
|
||||
public CancellableTask(int id, Task task, CancellationTokenSource cancellationTokenSource)
|
||||
{
|
||||
this.Id = id;
|
||||
this.Task = task;
|
||||
this.CancellationTokenSource = cancellationTokenSource;
|
||||
}
|
||||
|
||||
public int Id { get; }
|
||||
|
||||
public Task Task { get; }
|
||||
|
||||
public CancellationTokenSource CancellationTokenSource { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{D871B062-06A7-49E3-8BCD-8465B772FC52}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>DataGenerator</RootNamespace>
|
||||
<AssemblyName>DataGenerator</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="CancellableTask.cs" />
|
||||
<Compile Include="SqlDataGeneratorException.cs" />
|
||||
<Compile Include="SqlDataGenerator.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("DataGenerator")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("DataGenerator")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2016")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("d871b062-06a7-49e3-8bcd-8465b772fc52")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// 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("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -0,0 +1,340 @@
|
||||
//----------------------------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
|
||||
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
|
||||
// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//----------------------------------------------------------------------------------
|
||||
// The example companies, organizations, products, domain names,
|
||||
// e-mail addresses, logos, people, places, and events depicted
|
||||
// herein are fictitious. No association with any real company,
|
||||
// organization, product, domain name, email address, logo, person,
|
||||
// places, or events is intended or should be inferred.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Data.SqlClient;
|
||||
using System.Data.Sql;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Concurrent;
|
||||
using Microsoft.SqlServer.Server;
|
||||
|
||||
namespace DataGenerator
|
||||
{
|
||||
/// <summary>SqlDataGenerator is a class used for creating SQL Server sample data by using multiple Asychronous Tasks.</summary>
|
||||
public class SqlDataGenerator
|
||||
{
|
||||
private string[] postalCodes = {"98001","98002","98003","98004","98005","98006","98007","98008","98009","98010","98011","98012","98013","98014","98015","98019","98020","98021","98022","98023",
|
||||
"98024","98025","98026","98027","98028","98029","98030","98031","98032","98033","98034","98035","98036","98037","98038","98039","98040","98041","98042","98043",
|
||||
"98050","98051","98052","98053","98054","98055","98056","98057","98058","98059","98061","98062","98063","98064","98065","98068","98070","98071","98072","98073",
|
||||
"98074","98075","98077","98082","98083","98087","98089","98092","98093","98101","98102","98103","98104","98105","98106","98107","98108","98109","98110","98111"};
|
||||
|
||||
private Action<int, Exception> onException;
|
||||
private ConcurrentDictionary<int, CancellableTask> tasks;
|
||||
|
||||
private string sqlConnectionString;
|
||||
private string sqlInsertMeterMeasurementSPName;
|
||||
private int sqlCommandTimeout;
|
||||
private int batchSize;
|
||||
private int initialNumberOfTasks;
|
||||
private int numberOfMetersPerTask;
|
||||
private int numberOfBatchesPerTask;
|
||||
private int delay;
|
||||
private int numberOfMeters;
|
||||
|
||||
private Stopwatch timer;
|
||||
private int numberOfRowsInserted = 0;
|
||||
|
||||
protected ThreadLocal<Random> randomValue;
|
||||
private bool running = false;
|
||||
|
||||
/// <summary>Wait Time in milliseconds between executing SqlCommands.</summary>
|
||||
/// <returns>Integer</returns>
|
||||
public int Delay
|
||||
{
|
||||
get { return delay; }
|
||||
set
|
||||
{
|
||||
Validate(this.batchSize, this.initialNumberOfTasks, value, this.numberOfMeters);
|
||||
delay = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The row count of the sample data batch that every task generates.</summary>
|
||||
/// <returns>Integer</returns>
|
||||
public int BatchSize
|
||||
{
|
||||
get { return batchSize; }
|
||||
set
|
||||
{
|
||||
Validate(value, this.initialNumberOfTasks, this.delay, this.numberOfMeters);
|
||||
batchSize = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Rows (inserted or updated) per second.</summary>
|
||||
/// <returns>Double</returns>
|
||||
public double Rps => (double)this.numberOfRowsInserted / this.timer.Elapsed.TotalSeconds;
|
||||
|
||||
/// <summary>The number of current active tasks.</summary>
|
||||
/// <returns>Integer</returns>
|
||||
public int RunningTasks => this.tasks.Count();
|
||||
|
||||
/// <summary>Running Status</summary>
|
||||
/// <returns>Bool</returns>
|
||||
public bool IsRunning => this.running;
|
||||
|
||||
/// <summary>Creates a new instance of the SqlDataGenerator Class.</summary>
|
||||
/// <param name="sqlConnectionString">The sqlserver connectionString. Example: "Data Source=.;Initial Catalog=DbName;Integrated Security=True"</param>
|
||||
/// <param name="sqlInsertSPName">The Insert Meter Measurement sqlserver stored procedure. Example: "InsertMeterMeasurement". Note that the sql stored procedure needs to accept exactly two parameters: @Batch AS (Your User Defined Table Type) and @BatchSize INT</param>
|
||||
/// <param name="sqlCommandTimeout">The sqlserver command timeout. Example: 600</param>
|
||||
/// <param name="numberOfMeters">The total number of Meters. Example: 1000</param>
|
||||
/// <param name="initialNumberOfTasks">The number of concurrent tasks. Example: 5. Note that every task 1.Creates and opens a new sql connection 2.Creates a batch of BatchSize sample data and 3.Executes the sql stored procedure passed in sqlStoredProcedureName endless times until stopped by the user.</param>
|
||||
/// <param name="delayInMilliseconds">Delay in Millisecods betweeen Sql Commands. Example. 100</param>
|
||||
/// <param name="batchSize">The row count of the batch size to be used by every task. Example: 200</param>
|
||||
/// <param name="batchDataTypes">The pipe seperated column types of the batch table. Example. identity:1:1|string|datetime|double|int|guid</param>
|
||||
/// <param name="onException">Exception call back method with TaskId(int) and exception(Exception). Example: ExceptionCallback</param>
|
||||
public SqlDataGenerator(
|
||||
string sqlConnectionString,
|
||||
string sqlInsertSPName,
|
||||
int sqlCommandTimeout,
|
||||
int numberOfMeters,
|
||||
int initialNumberOfTasks,
|
||||
int delayInMilliseconds,
|
||||
int batchSize,
|
||||
Action<int, Exception> onException)
|
||||
{
|
||||
|
||||
this.sqlConnectionString = sqlConnectionString;
|
||||
this.sqlInsertMeterMeasurementSPName = sqlInsertSPName;
|
||||
this.sqlCommandTimeout = sqlCommandTimeout;
|
||||
this.numberOfMeters = numberOfMeters;
|
||||
this.onException = onException;
|
||||
this.tasks = new ConcurrentDictionary<int, CancellableTask>();
|
||||
this.randomValue = new ThreadLocal<Random>(() => new Random(Guid.NewGuid().GetHashCode()));
|
||||
this.initialNumberOfTasks = initialNumberOfTasks;
|
||||
this.delay = delayInMilliseconds;
|
||||
this.batchSize = batchSize;
|
||||
|
||||
Validate(this.batchSize, this.initialNumberOfTasks, this.delay, this.numberOfMeters);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>Creates and Starts all the tasks asynchronously. Note that every task 1.Creates and opens a new sql connection 2.Creates a batch of BatchSize sample data and 3.Executes the sql stored procedure passed in sqlStoredProcedureName endless times until stopped by the user.</summary>
|
||||
/// <returns>Task</returns>
|
||||
public async Task RunAsync()
|
||||
{
|
||||
if (this.running)
|
||||
{
|
||||
return;
|
||||
}
|
||||
timer = Stopwatch.StartNew();
|
||||
await this.RunAsync(this.initialNumberOfTasks);
|
||||
}
|
||||
|
||||
/// <summary>Stops all tasks asynchronously.</summary>
|
||||
/// <returns>Task</returns>
|
||||
public async Task StopAsync()
|
||||
{
|
||||
await this.StopAsync(this.RunningTasks);
|
||||
}
|
||||
|
||||
/// <summary>Restarts the Rows/Second Counter. This is called internally every time the input is changed.</summary>
|
||||
/// <returns>void</returns>
|
||||
public void RpsReset()
|
||||
{
|
||||
if (running)
|
||||
{
|
||||
this.timer.Restart();
|
||||
this.numberOfRowsInserted = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Updates the number of tasks that the DataGenerator is using.</summary>
|
||||
/// <returns>Task</returns>
|
||||
/// <remarks></remarks>
|
||||
/// <param name="numberOfTasks">The number of Tasks to start/stop depending of the number of tasks currently running.</param>
|
||||
public async Task UpdateTasksAsync(int numberOfTasks)
|
||||
{
|
||||
int diff = numberOfTasks - this.RunningTasks;
|
||||
|
||||
if (!running || diff == 0)
|
||||
{
|
||||
this.initialNumberOfTasks = numberOfTasks;
|
||||
return;
|
||||
}
|
||||
|
||||
if (diff < 0)
|
||||
{
|
||||
await this.StopAsync(-diff);
|
||||
}
|
||||
else
|
||||
{
|
||||
await this.RunAsync(diff);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>InsertMeterMeasurementAsync(int taskId, CancellationToken token)</summary>
|
||||
/// <returns>Task</returns>
|
||||
/// <remarks>Every Task creates a new sql connection, creates a new sqlcommand, create a batch of random numbers, and executes indefinetely until stopped by the user.</remarks>
|
||||
/// <param name="taskId">The taskId</param>
|
||||
/// <param name="token">The task's CancellationToken</param>
|
||||
private async Task InsertMeterMeasurementAsync(int taskId, CancellationToken token)
|
||||
{
|
||||
int batchId = 0;
|
||||
int size = this.BatchSize;
|
||||
|
||||
using (SqlConnection connection = new SqlConnection(this.sqlConnectionString))
|
||||
{
|
||||
await connection.OpenAsync(token);
|
||||
|
||||
using (SqlCommand command = new SqlCommand())
|
||||
{
|
||||
command.Connection = connection;
|
||||
command.CommandType = CommandType.StoredProcedure;
|
||||
command.CommandTimeout = this.sqlCommandTimeout;
|
||||
command.CommandText = this.sqlInsertMeterMeasurementSPName;
|
||||
command.Parameters.Add("@Batch", SqlDbType.Structured);
|
||||
command.Parameters.Add("@BatchSize", SqlDbType.Int).Value = this.BatchSize;
|
||||
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
DataTable dataTable = CreateBatch(taskId, batchId++);
|
||||
command.Parameters[0].Value = dataTable;
|
||||
|
||||
await command.ExecuteNonQueryAsync(token);
|
||||
Interlocked.Add(ref this.numberOfRowsInserted, size);
|
||||
await Task.Delay(this.Delay, token);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>CreateMeterMeasurementBatch(int taskId)</summary>
|
||||
/// <returns>DataTable</returns>
|
||||
/// <param name="taskId">Task Id</param>
|
||||
private DataTable CreateBatch(int taskId, int batchId)
|
||||
{
|
||||
DataTable table = new DataTable();
|
||||
table.Columns.Add("RowID", typeof(int));
|
||||
table.Columns.Add("MeterID", typeof(int));
|
||||
table.Columns.Add("MeasurementInkWh", typeof(double));
|
||||
table.Columns.Add("PostalCode", typeof(string));
|
||||
table.Columns.Add("MeasurementDate", typeof(DateTime));
|
||||
|
||||
batchId = (batchId % this.numberOfBatchesPerTask);
|
||||
for (int i = 1; i <= this.batchSize; i++)
|
||||
{
|
||||
int randomPostalCode = randomValue.Value.Next(0, this.postalCodes.Length);
|
||||
int meterId = taskId * this.numberOfMetersPerTask + batchId * this.BatchSize + i;
|
||||
double value = randomValue.Value.NextDouble();
|
||||
DateTime date = DateTime.Now;
|
||||
string postalCode = this.postalCodes[randomPostalCode].ToString();
|
||||
|
||||
// Data Shock - Produces 10X values
|
||||
value = this.Delay == 0 ? value * 10 : value;
|
||||
|
||||
table.Rows.Add(i, meterId, value, postalCode, date);
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
/// <summary>StopAsync(int numberOfTasksToStop)</summary>
|
||||
/// <returns>Task</returns>
|
||||
/// <param name="numberOfTasksToStop">The number of Tasks to stop.</param>
|
||||
private async Task StopAsync(int numberOfTasksToStop)
|
||||
{
|
||||
// TODO: Lock
|
||||
if (numberOfTasksToStop >= this.RunningTasks) { this.running = false; }
|
||||
|
||||
numberOfTasksToStop = Math.Min(numberOfTasksToStop, this.RunningTasks);
|
||||
List<CancellableTask> cancellableTasksToKill = this.tasks.Take(numberOfTasksToStop).Select(kv => kv.Value).ToList();
|
||||
|
||||
foreach (CancellableTask cancellableTask in cancellableTasksToKill)
|
||||
{
|
||||
cancellableTask.CancellationTokenSource.Cancel();
|
||||
}
|
||||
|
||||
await Task.WhenAll(cancellableTasksToKill.Select(c => c.Task));
|
||||
}
|
||||
|
||||
/// <summary>RunAsync(int numberOfTasks)</summary>
|
||||
/// <returns>Task</returns>
|
||||
/// <param name="numberOfTasks">The number of Tasks to start/stop depending of the number of tasks currently running.</param>
|
||||
private async Task RunAsync(int numberOfTasks)
|
||||
{
|
||||
for (int i = 0; i < numberOfTasks; i++)
|
||||
{
|
||||
CancellationTokenSource tokenSource = new CancellationTokenSource();
|
||||
int taskId = i;
|
||||
Task task = Task.Factory.StartNew(
|
||||
async () => await this.InsertMeterMeasurementAsync(taskId, tokenSource.Token).ContinueWith(t => CleanupTask(taskId, t)),
|
||||
tokenSource.Token,
|
||||
TaskCreationOptions.LongRunning,
|
||||
TaskScheduler.Default).Unwrap();
|
||||
|
||||
tasks.TryAdd(taskId, new CancellableTask(taskId, task, tokenSource));
|
||||
}
|
||||
|
||||
this.running = true;
|
||||
|
||||
await Task.WhenAll(this.tasks.Values.Select(t => t.Task));
|
||||
}
|
||||
|
||||
/// <summary>CleanupTask(int taskId, Task task)</summary>
|
||||
/// <returns>void</returns>
|
||||
/// <remarks></remarks>
|
||||
/// <param name="taskId">The taskId</param>
|
||||
/// <param name="task">The actual Task</param>
|
||||
private void CleanupTask(int taskId, Task task)
|
||||
{
|
||||
CancellableTask cancellableTask;
|
||||
bool succeeded = this.tasks.TryRemove(taskId, out cancellableTask);
|
||||
|
||||
if (task.IsFaulted && !cancellableTask.CancellationTokenSource.IsCancellationRequested)
|
||||
{
|
||||
this.onException(taskId, task.Exception?.InnerException);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Validate(int batchSize, int tasks, int delay)</summary>
|
||||
/// <param name="batchSize">The Batch Size</param>
|
||||
/// <param name="tasks">The number Of Tasks</param>
|
||||
/// <param name="delay">Teh Delay</param>
|
||||
private void Validate(int batchSize, int tasks, int delay, int numberOfMeters)
|
||||
{
|
||||
// Validate
|
||||
if (batchSize <= 0)
|
||||
{
|
||||
throw new SqlDataGeneratorException("The Batch Size cannot be less or equal to zero.");
|
||||
}
|
||||
if (tasks <= 0)
|
||||
{
|
||||
throw new SqlDataGeneratorException("Number Of Tasks cannot be less or equal to zero.");
|
||||
}
|
||||
if (delay < 0)
|
||||
{
|
||||
throw new SqlDataGeneratorException("Delay cannot be less than zero");
|
||||
}
|
||||
if (numberOfMeters <= 0)
|
||||
{
|
||||
throw new SqlDataGeneratorException("Number Of Meters cannot be less than zero");
|
||||
}
|
||||
if (numberOfMeters < batchSize * tasks)
|
||||
{
|
||||
throw new SqlDataGeneratorException("Number Of Meters cannot be less than (Tasks * BatchSize).");
|
||||
}
|
||||
// Reset Rps
|
||||
RpsReset();
|
||||
|
||||
// Set Number Of Meters Per Tasks
|
||||
this.numberOfMetersPerTask = this.numberOfMeters / this.initialNumberOfTasks;
|
||||
this.numberOfBatchesPerTask = this.numberOfMetersPerTask / this.BatchSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DataGenerator
|
||||
{
|
||||
public class SqlDataGeneratorException : Exception
|
||||
{
|
||||
public SqlDataGeneratorException()
|
||||
:base()
|
||||
{
|
||||
}
|
||||
|
||||
public SqlDataGeneratorException(string message)
|
||||
:base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public SqlDataGeneratorException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,148 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<Name>Db</Name>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectVersion>4.1</ProjectVersion>
|
||||
<ProjectGuid>{074bb065-0cc1-438a-a963-ea3fd781ba2a}</ProjectGuid>
|
||||
<DSP>Microsoft.Data.Tools.Schema.Sql.Sql130DatabaseSchemaProvider</DSP>
|
||||
<OutputType>Database</OutputType>
|
||||
<RootPath>
|
||||
</RootPath>
|
||||
<RootNamespace>Db</RootNamespace>
|
||||
<AssemblyName>Db</AssemblyName>
|
||||
<ModelCollation>1033,CI</ModelCollation>
|
||||
<DefaultFileStructure>BySchemaAndSchemaType</DefaultFileStructure>
|
||||
<DeployToDatabase>True</DeployToDatabase>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<TargetLanguage>CS</TargetLanguage>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<SqlServerVerification>False</SqlServerVerification>
|
||||
<IncludeCompositeObjects>True</IncludeCompositeObjects>
|
||||
<TargetDatabaseSet>True</TargetDatabaseSet>
|
||||
<DefaultCollation>SQL_Latin1_General_CP1_CI_AS</DefaultCollation>
|
||||
<AnsiNulls>False</AnsiNulls>
|
||||
<QuotedIdentifier>False</QuotedIdentifier>
|
||||
<QueryStoreCaptureMode>All</QueryStoreCaptureMode>
|
||||
<QueryStoreDesiredState>Off</QueryStoreDesiredState>
|
||||
<QueryStoreFlushInterval>900</QueryStoreFlushInterval>
|
||||
<QueryStoreStatsInterval>60</QueryStoreStatsInterval>
|
||||
<QueryStoreMaxPlansPerQuery>200</QueryStoreMaxPlansPerQuery>
|
||||
<QueryStoreStaleQueryThreshold>367</QueryStoreStaleQueryThreshold>
|
||||
<QueryStoreMaxStorageSize>100</QueryStoreMaxStorageSize>
|
||||
<DbScopedConfigLegacyCardinalityEstimation>Off</DbScopedConfigLegacyCardinalityEstimation>
|
||||
<DbScopedConfigMaxDOP>0</DbScopedConfigMaxDOP>
|
||||
<DbScopedConfigParameterSniffing>On</DbScopedConfigParameterSniffing>
|
||||
<DbScopedConfigOptimizerHotfixes>Off</DbScopedConfigOptimizerHotfixes>
|
||||
<DelayedDurability>DISABLED</DelayedDurability>
|
||||
<AutoCreateStatisticsIncremental>False</AutoCreateStatisticsIncremental>
|
||||
<MemoryOptimizedElevateToSnapshot>False</MemoryOptimizedElevateToSnapshot>
|
||||
<Containment>None</Containment>
|
||||
<IsNestedTriggersOn>True</IsNestedTriggersOn>
|
||||
<IsTransformNoiseWordsOn>False</IsTransformNoiseWordsOn>
|
||||
<TwoDigitYearCutoff>2049</TwoDigitYearCutoff>
|
||||
<NonTransactedFileStreamAccess>OFF</NonTransactedFileStreamAccess>
|
||||
<TargetRecoveryTimePeriod>60</TargetRecoveryTimePeriod>
|
||||
<TargetRecoveryTimeUnit>SECONDS</TargetRecoveryTimeUnit>
|
||||
<IsChangeTrackingOn>False</IsChangeTrackingOn>
|
||||
<IsChangeTrackingAutoCleanupOn>True</IsChangeTrackingAutoCleanupOn>
|
||||
<ChangeTrackingRetentionPeriod>2</ChangeTrackingRetentionPeriod>
|
||||
<ChangeTrackingRetentionUnit>Days</ChangeTrackingRetentionUnit>
|
||||
<IsEncryptionOn>False</IsEncryptionOn>
|
||||
<IsBrokerPriorityHonored>False</IsBrokerPriorityHonored>
|
||||
<Trustworthy>False</Trustworthy>
|
||||
<AutoUpdateStatisticsAsynchronously>False</AutoUpdateStatisticsAsynchronously>
|
||||
<PageVerify>CHECKSUM</PageVerify>
|
||||
<ServiceBrokerOption>DisableBroker</ServiceBrokerOption>
|
||||
<DateCorrelationOptimizationOn>False</DateCorrelationOptimizationOn>
|
||||
<Parameterization>SIMPLE</Parameterization>
|
||||
<AllowSnapshotIsolation>False</AllowSnapshotIsolation>
|
||||
<ReadCommittedSnapshot>False</ReadCommittedSnapshot>
|
||||
<VardecimalStorageFormatOn>True</VardecimalStorageFormatOn>
|
||||
<SupplementalLoggingOn>False</SupplementalLoggingOn>
|
||||
<CompatibilityMode>130</CompatibilityMode>
|
||||
<AnsiNullDefault>False</AnsiNullDefault>
|
||||
<AnsiPadding>False</AnsiPadding>
|
||||
<AnsiWarnings>False</AnsiWarnings>
|
||||
<ArithAbort>False</ArithAbort>
|
||||
<ConcatNullYieldsNull>False</ConcatNullYieldsNull>
|
||||
<NumericRoundAbort>False</NumericRoundAbort>
|
||||
<RecursiveTriggersEnabled>False</RecursiveTriggersEnabled>
|
||||
<DatabaseChaining>False</DatabaseChaining>
|
||||
<DatabaseState>ONLINE</DatabaseState>
|
||||
<CloseCursorOnCommitEnabled>False</CloseCursorOnCommitEnabled>
|
||||
<DefaultCursor>GLOBAL</DefaultCursor>
|
||||
<AutoClose>False</AutoClose>
|
||||
<AutoCreateStatistics>True</AutoCreateStatistics>
|
||||
<AutoShrink>False</AutoShrink>
|
||||
<AutoUpdateStatistics>True</AutoUpdateStatistics>
|
||||
<TornPageDetection>False</TornPageDetection>
|
||||
<DatabaseAccess>MULTI_USER</DatabaseAccess>
|
||||
<Recovery>FULL</Recovery>
|
||||
<EnableFullTextSearch>True</EnableFullTextSearch>
|
||||
<DefaultFilegroup>PRIMARY</DefaultFilegroup>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<BuildScriptName>$(MSBuildProjectName).sql</BuildScriptName>
|
||||
<TreatWarningsAsErrors>False</TreatWarningsAsErrors>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<DefineDebug>false</DefineDebug>
|
||||
<DefineTrace>true</DefineTrace>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<BuildScriptName>$(MSBuildProjectName).sql</BuildScriptName>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<DefineDebug>true</DefineDebug>
|
||||
<DefineTrace>true</DefineTrace>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">11.0</VisualStudioVersion>
|
||||
<!-- Default to the v11.0 targets path if the targets file for the current VS version is not found -->
|
||||
<SSDTExists Condition="Exists('$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\SSDT\Microsoft.Data.Tools.Schema.SqlTasks.targets')">True</SSDTExists>
|
||||
<VisualStudioVersion Condition="'$(SSDTExists)' == ''">11.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<Import Condition="'$(SQLDBExtensionsRefPath)' != ''" Project="$(SQLDBExtensionsRefPath)\Microsoft.Data.Tools.Schema.SqlTasks.targets" />
|
||||
<Import Condition="'$(SQLDBExtensionsRefPath)' == ''" Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\SSDT\Microsoft.Data.Tools.Schema.SqlTasks.targets" />
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties" />
|
||||
<Folder Include="dbo\" />
|
||||
<Folder Include="dbo\Tables\" />
|
||||
<Folder Include="dbo\Views\" />
|
||||
<Folder Include="Storage\" />
|
||||
<Folder Include="dbo\Stored Procedures\" />
|
||||
<Folder Include="dbo\User Defined Types\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Build Include="dbo\Tables\MeterMeasurement.sql">
|
||||
<AnsiNulls>On</AnsiNulls>
|
||||
<QuotedIdentifier>On</QuotedIdentifier>
|
||||
</Build>
|
||||
<Build Include="dbo\Tables\MeterMeasurementHistory.sql">
|
||||
<AnsiNulls>On</AnsiNulls>
|
||||
<QuotedIdentifier>On</QuotedIdentifier>
|
||||
</Build>
|
||||
<Build Include="dbo\Views\vwMeterMeasurement.sql">
|
||||
<AnsiNulls>On</AnsiNulls>
|
||||
<QuotedIdentifier>On</QuotedIdentifier>
|
||||
</Build>
|
||||
<Build Include="Storage\mod.sql" />
|
||||
<Build Include="dbo\Stored Procedures\InsertMeterMeasurement.sql">
|
||||
<AnsiNulls>On</AnsiNulls>
|
||||
<QuotedIdentifier>On</QuotedIdentifier>
|
||||
</Build>
|
||||
<Build Include="dbo\User Defined Types\udtMeterMeasurement.sql" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,3 @@
|
||||
--ALTER DATABASE [$(DatabaseName)]
|
||||
-- ADD FILEGROUP [mod] CONTAINS MEMORY_OPTIMIZED_DATA;
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
|
||||
|
||||
CREATE PROCEDURE [dbo].[InsertMeterMeasurement]
|
||||
@Batch AS dbo.udtMeterMeasurement READONLY,
|
||||
@BatchSize INT
|
||||
|
||||
WITH NATIVE_COMPILATION, SCHEMABINDING
|
||||
AS
|
||||
BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL=SNAPSHOT, LANGUAGE=N'English')
|
||||
DECLARE @i INT = 1
|
||||
DECLARE @MeterID INT
|
||||
DECLARE @MeasurementInkWh DECIMAL(9, 4)
|
||||
DECLARE @PostalCode NVARCHAR(10)
|
||||
DECLARE @MeasurementDate DATETIME2(7)
|
||||
|
||||
WHILE (@i <= @BatchSize)
|
||||
BEGIN
|
||||
|
||||
SELECT @MeterID = MeterID,
|
||||
@MeasurementInkWh = MeasurementInkWh,
|
||||
@MeasurementDate = MeasurementDate,
|
||||
@PostalCode = PostalCode
|
||||
FROM @Batch
|
||||
WHERE RowID = @i
|
||||
|
||||
UPDATE dbo.MeterMeasurement
|
||||
SET MeasurementInkWh += @MeasurementInkWh,
|
||||
MeasurementDate = @MeasurementDate,
|
||||
PostalCode = @PostalCode
|
||||
WHERE MeterID = @MeterID
|
||||
|
||||
IF(@@ROWCOUNT = 0)
|
||||
BEGIN
|
||||
INSERT INTO dbo.MeterMeasurement (MeterID, MeasurementInkWh, PostalCode, MeasurementDate)
|
||||
VALUES (@MeterID, @MeasurementInkWh, @PostalCode, @MeasurementDate);
|
||||
END
|
||||
|
||||
SET @i += 1
|
||||
END
|
||||
END
|
||||
@@ -0,0 +1,12 @@
|
||||
CREATE TABLE [dbo].[MeterMeasurement] (
|
||||
[MeterID] INT NOT NULL,
|
||||
[MeasurementInkWh] DECIMAL (9, 4) NOT NULL,
|
||||
[PostalCode] NVARCHAR (10) NOT NULL,
|
||||
[MeasurementDate] DATETIME2 (7) NOT NULL,
|
||||
[SysStartTime] DATETIME2 (7) GENERATED ALWAYS AS ROW START NOT NULL,
|
||||
[SysEndTime] DATETIME2 (7) GENERATED ALWAYS AS ROW END NOT NULL,
|
||||
PRIMARY KEY NONCLUSTERED ([MeterID] ASC),
|
||||
PERIOD FOR SYSTEM_TIME ([SysStartTime], [SysEndTime])
|
||||
)
|
||||
WITH (MEMORY_OPTIMIZED = ON, SYSTEM_VERSIONING = ON (HISTORY_TABLE=[dbo].[MeterMeasurementHistory], DATA_CONSISTENCY_CHECK=ON));
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
CREATE TABLE [dbo].[MeterMeasurementHistory] (
|
||||
[MeterID] INT NOT NULL,
|
||||
[MeasurementInkWh] DECIMAL (9, 4) NOT NULL,
|
||||
[PostalCode] NVARCHAR (10) NOT NULL,
|
||||
[MeasurementDate] DATETIME2 (7) NOT NULL,
|
||||
[SysStartTime] DATETIME2 (7) NOT NULL,
|
||||
[SysEndTime] DATETIME2 (7) NOT NULL
|
||||
);
|
||||
|
||||
|
||||
GO
|
||||
CREATE CLUSTERED COLUMNSTORE INDEX [ix_MeterMeasurementHistory]
|
||||
ON [dbo].[MeterMeasurementHistory];
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
CREATE TYPE [dbo].[udtMeterMeasurement] AS TABLE (
|
||||
[RowID] INT NOT NULL,
|
||||
[MeterID] INT NOT NULL,
|
||||
[MeasurementInkWh] DECIMAL (9, 4) NOT NULL,
|
||||
[PostalCode] NVARCHAR (10) NOT NULL,
|
||||
[MeasurementDate] DATETIME2 (7) NOT NULL,
|
||||
INDEX [IX_RowID] NONCLUSTERED HASH ([RowID]) WITH (BUCKET_COUNT = 1024))
|
||||
WITH (MEMORY_OPTIMIZED = ON);
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
CREATE VIEW vwMeterMeasurement
|
||||
AS
|
||||
SELECT PostalCode,
|
||||
DATETIMEFROMPARTS(
|
||||
YEAR(MeasurementDate),
|
||||
MONTH(MeasurementDate),
|
||||
DAY(MeasurementDate),
|
||||
DATEPART(HOUR,MeasurementDate),
|
||||
DATEPART(MINUTE,MeasurementDate),
|
||||
DATEPART(ss,MeasurementDate)/1,
|
||||
0
|
||||
) AS MeasurementDate,
|
||||
count(*) AS MeterCount,
|
||||
AVG(MeasurementInkWh) AS AvgMeasurementInkWh
|
||||
FROM [dbo].[MeterMeasurement] FOR SYSTEM_TIME ALL WITH (NOLOCK)
|
||||
GROUP BY
|
||||
PostalCode,
|
||||
DATETIMEFROMPARTS(
|
||||
YEAR(MeasurementDate),
|
||||
MONTH(MeasurementDate),
|
||||
DAY(MeasurementDate),
|
||||
DATEPART(HOUR,MeasurementDate),
|
||||
DATEPART(MINUTE,MeasurementDate),
|
||||
DATEPART(ss,MeasurementDate)/1,0)
|
||||
@@ -0,0 +1,48 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25123.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataGenerator", "DataGenerator\DataGenerator.csproj", "{D871B062-06A7-49E3-8BCD-8465B772FC52}"
|
||||
EndProject
|
||||
Project("{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}") = "Db", "Db\Db.sqlproj", "{074BB065-0CC1-438A-A963-EA3FD781BA2A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinFormsClient", "WinFormsClient\WinFormsClient.csproj", "{6C0E1820-A10B-47DA-B806-939CBCD0DD39}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleClient", "ConsoleClient\ConsoleClient.csproj", "{BB747094-2CF6-46D2-8E6C-07E27CF0BCAA}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2D761B8A-EE55-4825-B19D-F3B04860D9D7}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
App.config = App.config
|
||||
PowerDashboard.pbix = PowerDashboard.pbix
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{D871B062-06A7-49E3-8BCD-8465B772FC52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D871B062-06A7-49E3-8BCD-8465B772FC52}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D871B062-06A7-49E3-8BCD-8465B772FC52}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D871B062-06A7-49E3-8BCD-8465B772FC52}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{074BB065-0CC1-438A-A963-EA3FD781BA2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{074BB065-0CC1-438A-A963-EA3FD781BA2A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{074BB065-0CC1-438A-A963-EA3FD781BA2A}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{074BB065-0CC1-438A-A963-EA3FD781BA2A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{074BB065-0CC1-438A-A963-EA3FD781BA2A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{074BB065-0CC1-438A-A963-EA3FD781BA2A}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{6C0E1820-A10B-47DA-B806-939CBCD0DD39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6C0E1820-A10B-47DA-B806-939CBCD0DD39}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6C0E1820-A10B-47DA-B806-939CBCD0DD39}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6C0E1820-A10B-47DA-B806-939CBCD0DD39}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BB747094-2CF6-46D2-8E6C-07E27CF0BCAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BB747094-2CF6-46D2-8E6C-07E27CF0BCAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BB747094-2CF6-46D2-8E6C-07E27CF0BCAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BB747094-2CF6-46D2-8E6C-07E27CF0BCAA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
Binary file not shown.
@@ -0,0 +1,123 @@
|
||||
# IoT Smart Grid
|
||||
This code sample demonstrates how a SQL Server 2016 (or higher) memory optimized database could be used to ingest a very high input data rate and ultimately help improve the performance of applications with this scenario. The code simulates an IoT Smart Grid scenario where multiple IoT power meters are constantly sending electricity usage measurements to the database.
|
||||
|
||||
### Contents
|
||||
|
||||
[About this sample](#about-this-sample)<br/>
|
||||
[Before you begin](#before-you-begin)<br/>
|
||||
[Run this sample](#run-this-sample)<br/>
|
||||
[Sample details](#sample-details)<br/>
|
||||
[Disclaimers](#disclaimers)<br/>
|
||||
[Related links](#related-links)<br/>
|
||||
|
||||
<a name=about-this-sample></a>
|
||||
|
||||
## About this sample
|
||||
|
||||
1. **Applies to:** SQL Server 2016 (or higher) Enterprise / Developer / Evaluation Edition, Azure SQL Database
|
||||
2. **Key features:** Memory Optimized Tables and Table valued Parameters (TVPs), Natively Compiled Stored Procedures, System-Versioned Temporal Tables, Clustered Columnstore Index, Power BI Report
|
||||
3. **Workload:** Data Ingestion for IoT
|
||||
4. **Programming Language:** .NET C#, T-SQL
|
||||
5. **Authors:** Perry Skountrianos [perrysk-msft]
|
||||
|
||||
<a name=before-you-begin></a>
|
||||
|
||||
## Before you begin
|
||||
|
||||
To run this sample, you need the following prerequisites.
|
||||
|
||||
**Software prerequisites:**
|
||||
|
||||
1. SQL Server 2016 (or higher) or an Azure SQL Database
|
||||
2. Visual Studio 2015 (or higher) with the latest SSDT installed
|
||||
3. [Power BI Desktop Installed] (https://powerbi.microsoft.com/en-us/desktop/)
|
||||
|
||||
**Azure prerequisites:**
|
||||
|
||||
1. Permission to create an Azure SQL Database
|
||||
|
||||
<a name=run-this-sample></a>
|
||||
|
||||
## Run this sample
|
||||
1. Clone this repository using Git for Windows (http://www.git-scm.com/), or download the zip file.
|
||||
|
||||
2. From Visual Studio, open the **IoT-Smart-Grid.sln** file from the root directory.
|
||||
|
||||
3. The sample includes two clients for generating the workload: **ConsoleClient** and **WinFormsClient**. Right click on either of these projects and select "Set as StartUp Project".
|
||||
|
||||
4. In Visual Studio Build menu, select **Build Solution** (or Press F6).
|
||||
|
||||
5. Modify the **App.config Settings** (located in the **Solution Items** solution folder)
|
||||
|
||||
By accepting the default values for the following settings you should be able to see a data generated spike every 35 seconds with a 4 second duration. If you want to produce a continuous high data volume workload you should set the **commandDelay to 0** and adjust the **numberOfTasks** and **batchSize** according to the hardware specifications of your environment.
|
||||
|
||||
- **Db**: SQL Server connectionString. Currently it is configured to connect to the local default SQL Server Instance using Integrated Security.
|
||||
- **insertSPName**: The name of the Natively Compiled Stored Procedure that inserted the sample data. (Default Value: InsertMeterMeasurement)
|
||||
- **numberOfTasks**: The number of Asynchronous Tasks the Data Generator uses. (Default Value: 50)
|
||||
- **numberOfMeters**: The number of IoT Power Meters to be used. (Default Value: 1000000)
|
||||
- **batchSize**: The sample data batch size.(Default Value: 1000)
|
||||
- **commandDelay**: The delay between sql calls. Note that during a data generated spike the app changes this to 0. (Default Value: 1500ms)
|
||||
- **enableShock**: Flag that turns on/off the data shock. This should be set to 0 for max high volume workload
|
||||
- **commandTimeout**: SQL Command Timeout(Default Value: 600)
|
||||
- **shockFrequency**: How often to generate a data spike. (Default Value: 35000ms)
|
||||
- **shockDuration**: The duration of the data spike. (Default Value: 4000ms)
|
||||
- **rpsFrequency**: The polling frequency for Rows per Second. (Default Value: 2000ms)
|
||||
- **logFileName**: Log File Name. (Default Value: log.txt)
|
||||
- **powerBIDesktopPath**: The local path to the PBIDesktop.exe. (Default Value: C:\Program Files\Microsoft Power BI Desktop\bin\PBIDesktop.exe)
|
||||
|
||||
6. Publish the Database
|
||||
- Right click on the **Db** SQL Server Database Project and Select **Publish**.
|
||||
- Click Edit... to choose your connection string.
|
||||
- We recommend choosing **PowerConsumption** as the Database name as this is the default db name the sample is configured to run under.
|
||||
- Click Publish.
|
||||
- Note: For publishing to Azure SQL you need to
|
||||
1. Change the DB project target platform to **Microsoft Azure SQL Database V12** and
|
||||
2. Comment the T-SQL in the **mod.sql** file in the Db/Storage folder before publishing.
|
||||
|
||||
7. Build the app and run it. Do not use the debugger, as that will slow down the app.
|
||||
|
||||
8. Start the workload
|
||||
- From the **ConsoleClient**: Type 'START'
|
||||
- From the **WinFormsClient**: Press the **Start** button.
|
||||
|
||||
9. Launch the Power BI Report
|
||||
- From the **ConsoleClient**: Type 'REPORT' on the command prompt window.
|
||||
- From the **WinFormsClient**: Click the **Power BI Report** link.
|
||||
|
||||
10. In the Power BI Desktop menu click on **Edit Queries** and then **Source** to make sure that both the Server and the Database Name match the values from **Step 6**. Click OK to apply changes.
|
||||
|
||||
<a name=sample-details></a>
|
||||
|
||||
## Sample details
|
||||
|
||||
General Description
|
||||
|
||||
This code sample simulates an IoT Smart Grid scenario where multiple IoT power meters are sending electricity usage measurements to a SQL Server memory optimized database. The Data Generator, that can be started either from the Console or the Windows Form client, produces a data generated spike to simulate a [shock absorber scenario] (https://blogs.technet.microsoft.com/dataplatforminsider/2013/09/19/in-memory-oltp-common-design-pattern-high-data-input-rateshock-absorber/). Every async task in the Data Generator produces a batch of records with random values in order to simulate the data of an IoT power meter. It then calls a natively compiled stored procedure, that accepts an memory optimized table valued parameter (TVP), to insert the data into an memory optimized SQL Server table. In addition to the in-memory features, the sample is leveraging [System-Versioned Temporal Tables](https://msdn.microsoft.com/en-us/library/dn935015.aspx) for building version history, [Clustered Columnstore Index](https://msdn.microsoft.com/en-us/library/dn817827.aspx) for enabling real time operational analytics, and [Power BI](https://powerbi.microsoft.com/en-us/desktop/) for data visualization.
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
Visual Studio Projects:
|
||||
|
||||
1. **ConsoleAppClient**: Console Data Generator client. Uses START | STOP | HELP | REPORT | EXIT commands.
|
||||
2. **Data Generator**: Data Generator client library. Uses multiple async tasks to produce a test data workload.
|
||||
3. **Db**: SQL Server Database project
|
||||
4. **WFClient**: Windows Forms Data Generator client.
|
||||
|
||||
<a name=disclaimers></a>
|
||||
|
||||
## Disclaimers
|
||||
The code included in this sample is not intended to be a set of best practices on how to build scalable enterprise grade applications. This is beyond the scope of this quick start sample.
|
||||
|
||||
<a name=related-links></a>
|
||||
|
||||
## Related Links
|
||||
<!-- Links to more articles. Remember to delete "en-us" from the link path. -->
|
||||
|
||||
For more information, see these articles:
|
||||
- [In-Memory OLTP (In-Memory Optimization)] (https://msdn.microsoft.com/en-us/library/dn133186.aspx)
|
||||
- [OLTP and database management] (https://www.microsoft.com/en-us/server-cloud/solutions/oltp-database-management.aspx)
|
||||
- [SQL Server 2016 Temporal Tables] (https://msdn.microsoft.com/en-us/library/dn935015.aspx)
|
||||
- [In-Memory OLTP Common Design Pattern – High Data Input Rate/Shock Absorber] (https://blogs.technet.microsoft.com/dataplatforminsider/2013/09/19/in-memory-oltp-common-design-pattern-high-data-input-rateshock-absorber/)
|
||||
- [Power BI Download] (https://powerbi.microsoft.com/en-us/desktop/)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 969 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
+314
@@ -0,0 +1,314 @@
|
||||
namespace Client
|
||||
{
|
||||
partial class FrmMain
|
||||
{
|
||||
/// <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.components = new System.ComponentModel.Container();
|
||||
System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea();
|
||||
System.Windows.Forms.DataVisualization.Charting.Legend legend1 = new System.Windows.Forms.DataVisualization.Charting.Legend();
|
||||
System.Windows.Forms.DataVisualization.Charting.Series series1 = new System.Windows.Forms.DataVisualization.Charting.Series();
|
||||
System.Windows.Forms.DataVisualization.Charting.DataPoint dataPoint1 = new System.Windows.Forms.DataVisualization.Charting.DataPoint(0D, 0D);
|
||||
this.bottomToolStrip = new System.Windows.Forms.ToolStrip();
|
||||
this.lblTasksTitle = new System.Windows.Forms.ToolStripLabel();
|
||||
this.lblTasksValue = new System.Windows.Forms.ToolStripLabel();
|
||||
this.tss_1 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.lblBatchSizeTitle = new System.Windows.Forms.ToolStripLabel();
|
||||
this.lblBatchSizeValue = new System.Windows.Forms.ToolStripLabel();
|
||||
this.tss_2 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.lblFrequencyTitle = new System.Windows.Forms.ToolStripLabel();
|
||||
this.lblFrequencyValue = new System.Windows.Forms.ToolStripLabel();
|
||||
this.tss_3 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.lblMetersTitle = new System.Windows.Forms.ToolStripLabel();
|
||||
this.lblMetersValue = new System.Windows.Forms.ToolStripLabel();
|
||||
this.tss_5 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.lblRpsTitle = new System.Windows.Forms.ToolStripLabel();
|
||||
this.lblRpsValue = new System.Windows.Forms.ToolStripLabel();
|
||||
this.Start = new System.Windows.Forms.Button();
|
||||
this.Stop = new System.Windows.Forms.Button();
|
||||
this.RpsChart = new System.Windows.Forms.DataVisualization.Charting.Chart();
|
||||
this.rpsTimer = new System.Windows.Forms.Timer(this.components);
|
||||
this.mainTimer = new System.Windows.Forms.Timer(this.components);
|
||||
this.shockTimer = new System.Windows.Forms.Timer(this.components);
|
||||
this.powerBIReport = new System.Windows.Forms.LinkLabel();
|
||||
this.bottomToolStrip.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.RpsChart)).BeginInit();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// bottomToolStrip
|
||||
//
|
||||
this.bottomToolStrip.BackColor = System.Drawing.Color.White;
|
||||
this.bottomToolStrip.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||
this.bottomToolStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.lblTasksTitle,
|
||||
this.lblTasksValue,
|
||||
this.tss_1,
|
||||
this.lblBatchSizeTitle,
|
||||
this.lblBatchSizeValue,
|
||||
this.tss_2,
|
||||
this.lblFrequencyTitle,
|
||||
this.lblFrequencyValue,
|
||||
this.tss_3,
|
||||
this.lblMetersTitle,
|
||||
this.lblMetersValue,
|
||||
this.tss_5,
|
||||
this.lblRpsTitle,
|
||||
this.lblRpsValue});
|
||||
this.bottomToolStrip.Location = new System.Drawing.Point(0, 303);
|
||||
this.bottomToolStrip.Name = "bottomToolStrip";
|
||||
this.bottomToolStrip.Size = new System.Drawing.Size(847, 25);
|
||||
this.bottomToolStrip.TabIndex = 0;
|
||||
this.bottomToolStrip.Text = "toolStrip1";
|
||||
//
|
||||
// lblTasksTitle
|
||||
//
|
||||
this.lblTasksTitle.ForeColor = System.Drawing.Color.Gray;
|
||||
this.lblTasksTitle.Name = "lblTasksTitle";
|
||||
this.lblTasksTitle.Size = new System.Drawing.Size(38, 22);
|
||||
this.lblTasksTitle.Text = "Tasks:";
|
||||
//
|
||||
// lblTasksValue
|
||||
//
|
||||
this.lblTasksValue.Name = "lblTasksValue";
|
||||
this.lblTasksValue.Size = new System.Drawing.Size(13, 22);
|
||||
this.lblTasksValue.Text = "0";
|
||||
//
|
||||
// tss_1
|
||||
//
|
||||
this.tss_1.Name = "tss_1";
|
||||
this.tss_1.Size = new System.Drawing.Size(6, 25);
|
||||
//
|
||||
// lblBatchSizeTitle
|
||||
//
|
||||
this.lblBatchSizeTitle.ForeColor = System.Drawing.Color.Gray;
|
||||
this.lblBatchSizeTitle.Name = "lblBatchSizeTitle";
|
||||
this.lblBatchSizeTitle.Size = new System.Drawing.Size(63, 22);
|
||||
this.lblBatchSizeTitle.Text = "Batch Size:";
|
||||
//
|
||||
// lblBatchSizeValue
|
||||
//
|
||||
this.lblBatchSizeValue.Name = "lblBatchSizeValue";
|
||||
this.lblBatchSizeValue.Size = new System.Drawing.Size(13, 22);
|
||||
this.lblBatchSizeValue.Text = "0";
|
||||
//
|
||||
// tss_2
|
||||
//
|
||||
this.tss_2.Name = "tss_2";
|
||||
this.tss_2.Size = new System.Drawing.Size(6, 25);
|
||||
//
|
||||
// lblFrequencyTitle
|
||||
//
|
||||
this.lblFrequencyTitle.ForeColor = System.Drawing.Color.Gray;
|
||||
this.lblFrequencyTitle.Name = "lblFrequencyTitle";
|
||||
this.lblFrequencyTitle.Size = new System.Drawing.Size(179, 22);
|
||||
this.lblFrequencyTitle.Text = "Shock Frequency/Duration (sec):";
|
||||
//
|
||||
// lblFrequencyValue
|
||||
//
|
||||
this.lblFrequencyValue.Name = "lblFrequencyValue";
|
||||
this.lblFrequencyValue.Size = new System.Drawing.Size(30, 22);
|
||||
this.lblFrequencyValue.Text = "0 / 0";
|
||||
//
|
||||
// tss_3
|
||||
//
|
||||
this.tss_3.Name = "tss_3";
|
||||
this.tss_3.Size = new System.Drawing.Size(6, 25);
|
||||
//
|
||||
// lblMetersTitle
|
||||
//
|
||||
this.lblMetersTitle.ForeColor = System.Drawing.Color.Gray;
|
||||
this.lblMetersTitle.Name = "lblMetersTitle";
|
||||
this.lblMetersTitle.Size = new System.Drawing.Size(46, 22);
|
||||
this.lblMetersTitle.Text = "Meters:";
|
||||
//
|
||||
// lblMetersValue
|
||||
//
|
||||
this.lblMetersValue.Name = "lblMetersValue";
|
||||
this.lblMetersValue.Size = new System.Drawing.Size(13, 22);
|
||||
this.lblMetersValue.Text = "0";
|
||||
//
|
||||
// tss_5
|
||||
//
|
||||
this.tss_5.Name = "tss_5";
|
||||
this.tss_5.Size = new System.Drawing.Size(6, 25);
|
||||
//
|
||||
// lblRpsTitle
|
||||
//
|
||||
this.lblRpsTitle.ForeColor = System.Drawing.Color.Gray;
|
||||
this.lblRpsTitle.Name = "lblRpsTitle";
|
||||
this.lblRpsTitle.Size = new System.Drawing.Size(29, 22);
|
||||
this.lblRpsTitle.Text = "Rps:";
|
||||
//
|
||||
// lblRpsValue
|
||||
//
|
||||
this.lblRpsValue.ForeColor = System.Drawing.Color.Red;
|
||||
this.lblRpsValue.Name = "lblRpsValue";
|
||||
this.lblRpsValue.Size = new System.Drawing.Size(13, 22);
|
||||
this.lblRpsValue.Text = "0";
|
||||
//
|
||||
// Start
|
||||
//
|
||||
this.Start.FlatAppearance.BorderColor = System.Drawing.Color.Silver;
|
||||
this.Start.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.Start.Location = new System.Drawing.Point(720, 268);
|
||||
this.Start.Name = "Start";
|
||||
this.Start.Size = new System.Drawing.Size(105, 40);
|
||||
this.Start.TabIndex = 2;
|
||||
this.Start.Text = "Start";
|
||||
this.Start.UseVisualStyleBackColor = true;
|
||||
this.Start.Click += new System.EventHandler(this.Start_Click);
|
||||
//
|
||||
// Stop
|
||||
//
|
||||
this.Stop.Enabled = false;
|
||||
this.Stop.FlatAppearance.BorderColor = System.Drawing.Color.Silver;
|
||||
this.Stop.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.Stop.Location = new System.Drawing.Point(609, 268);
|
||||
this.Stop.Name = "Stop";
|
||||
this.Stop.Size = new System.Drawing.Size(105, 40);
|
||||
this.Stop.TabIndex = 3;
|
||||
this.Stop.Text = "Stop";
|
||||
this.Stop.UseVisualStyleBackColor = true;
|
||||
this.Stop.Click += new System.EventHandler(this.Stop_Click);
|
||||
//
|
||||
// RpsChart
|
||||
//
|
||||
this.RpsChart.BackColor = System.Drawing.Color.Transparent;
|
||||
this.RpsChart.BorderlineColor = System.Drawing.Color.Black;
|
||||
chartArea1.AxisX.IntervalType = System.Windows.Forms.DataVisualization.Charting.DateTimeIntervalType.Seconds;
|
||||
chartArea1.AxisX.LabelAutoFitMaxFontSize = 8;
|
||||
chartArea1.AxisX.LineColor = System.Drawing.Color.DarkGray;
|
||||
chartArea1.AxisX.MajorGrid.Enabled = false;
|
||||
chartArea1.AxisX.MajorGrid.Interval = 0D;
|
||||
chartArea1.AxisX.MajorGrid.IntervalOffset = 0D;
|
||||
chartArea1.AxisX.MajorGrid.IntervalType = System.Windows.Forms.DataVisualization.Charting.DateTimeIntervalType.Auto;
|
||||
chartArea1.AxisX.MajorTickMark.Enabled = false;
|
||||
chartArea1.AxisX.Maximum = 100D;
|
||||
chartArea1.AxisX.Minimum = 0D;
|
||||
chartArea1.AxisY.LabelAutoFitMaxFontSize = 8;
|
||||
chartArea1.AxisY.LineColor = System.Drawing.Color.DarkGray;
|
||||
chartArea1.AxisY.MajorGrid.Enabled = false;
|
||||
chartArea1.AxisY.Minimum = 0D;
|
||||
chartArea1.BackColor = System.Drawing.Color.Transparent;
|
||||
chartArea1.Name = "Chart";
|
||||
this.RpsChart.ChartAreas.Add(chartArea1);
|
||||
legend1.BackColor = System.Drawing.Color.Transparent;
|
||||
legend1.Enabled = false;
|
||||
legend1.ForeColor = System.Drawing.Color.Maroon;
|
||||
legend1.Name = "Legend1";
|
||||
this.RpsChart.Legends.Add(legend1);
|
||||
this.RpsChart.Location = new System.Drawing.Point(0, 0);
|
||||
this.RpsChart.Name = "RpsChart";
|
||||
this.RpsChart.Palette = System.Windows.Forms.DataVisualization.Charting.ChartColorPalette.None;
|
||||
series1.ChartArea = "Chart";
|
||||
series1.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.FastLine;
|
||||
series1.Color = System.Drawing.Color.Red;
|
||||
series1.Font = new System.Drawing.Font("Microsoft Sans Serif", 6F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
series1.Legend = "Legend1";
|
||||
series1.LegendText = "sadsaDS";
|
||||
series1.MarkerBorderWidth = 3;
|
||||
series1.Name = "RPS";
|
||||
series1.Points.Add(dataPoint1);
|
||||
this.RpsChart.Series.Add(series1);
|
||||
this.RpsChart.Size = new System.Drawing.Size(847, 262);
|
||||
this.RpsChart.TabIndex = 102;
|
||||
this.RpsChart.Text = "Rows / Sec";
|
||||
//
|
||||
// rpsTimer
|
||||
//
|
||||
this.rpsTimer.Interval = 300;
|
||||
this.rpsTimer.Tick += new System.EventHandler(this.rpsTimer_Tick);
|
||||
//
|
||||
// mainTimer
|
||||
//
|
||||
this.mainTimer.Tick += new System.EventHandler(this.mainTimer_Tick);
|
||||
//
|
||||
// shockTimer
|
||||
//
|
||||
this.shockTimer.Interval = 5000;
|
||||
this.shockTimer.Tick += new System.EventHandler(this.shockTimer_Tick);
|
||||
//
|
||||
// powerBIReport
|
||||
//
|
||||
this.powerBIReport.AutoSize = true;
|
||||
this.powerBIReport.Location = new System.Drawing.Point(47, 260);
|
||||
this.powerBIReport.Name = "powerBIReport";
|
||||
this.powerBIReport.Size = new System.Drawing.Size(85, 13);
|
||||
this.powerBIReport.TabIndex = 103;
|
||||
this.powerBIReport.TabStop = true;
|
||||
this.powerBIReport.Text = "Power BI Report";
|
||||
this.powerBIReport.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.powerBIReport_LinkClicked);
|
||||
//
|
||||
// FrmMain
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.BackColor = System.Drawing.Color.White;
|
||||
this.ClientSize = new System.Drawing.Size(847, 328);
|
||||
this.Controls.Add(this.powerBIReport);
|
||||
this.Controls.Add(this.RpsChart);
|
||||
this.Controls.Add(this.Stop);
|
||||
this.Controls.Add(this.Start);
|
||||
this.Controls.Add(this.bottomToolStrip);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||
this.Name = "FrmMain";
|
||||
this.Text = "Data Generator Client";
|
||||
this.bottomToolStrip.ResumeLayout(false);
|
||||
this.bottomToolStrip.PerformLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.RpsChart)).EndInit();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.ToolStrip bottomToolStrip;
|
||||
private System.Windows.Forms.ToolStripLabel lblTasksTitle;
|
||||
private System.Windows.Forms.ToolStripLabel lblTasksValue;
|
||||
private System.Windows.Forms.ToolStripSeparator tss_1;
|
||||
private System.Windows.Forms.ToolStripLabel lblBatchSizeTitle;
|
||||
private System.Windows.Forms.ToolStripLabel lblBatchSizeValue;
|
||||
private System.Windows.Forms.ToolStripSeparator tss_2;
|
||||
private System.Windows.Forms.ToolStripLabel lblFrequencyTitle;
|
||||
private System.Windows.Forms.ToolStripLabel lblFrequencyValue;
|
||||
private System.Windows.Forms.Button Start;
|
||||
private System.Windows.Forms.Button Stop;
|
||||
private System.Windows.Forms.DataVisualization.Charting.Chart RpsChart;
|
||||
private System.Windows.Forms.ToolStripLabel lblRpsTitle;
|
||||
private System.Windows.Forms.ToolStripLabel lblRpsValue;
|
||||
private System.Windows.Forms.Timer rpsTimer;
|
||||
private System.Windows.Forms.Timer mainTimer;
|
||||
private System.Windows.Forms.ToolStripLabel lblMetersTitle;
|
||||
private System.Windows.Forms.ToolStripLabel lblMetersValue;
|
||||
private System.Windows.Forms.ToolStripSeparator tss_5;
|
||||
private System.Windows.Forms.Timer shockTimer;
|
||||
private System.Windows.Forms.ToolStripSeparator tss_3;
|
||||
private System.Windows.Forms.LinkLabel powerBIReport;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
/*----------------------------------------------------------------------------------
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
|
||||
EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
----------------------------------------------------------------------------------
|
||||
The example companies, organizations, products, domain names,
|
||||
e-mail addresses, logos, people, places, and events depicted
|
||||
herein are fictitious. No association with any real company,
|
||||
organization, product, domain name, email address, logo, person,
|
||||
places, or events is intended or should be inferred.
|
||||
|
||||
*/
|
||||
|
||||
using DataGenerator;
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Forms.DataVisualization.Charting;
|
||||
|
||||
/*----------------------------------------------------------------------------------
|
||||
High Level Scenario:
|
||||
This code sample demonstrates how a SQL Server 2016 (or higher) memory optimized database could be used to ingest a very high input data rate
|
||||
and ultimately help improve the performance of applications with this scenario. The code simulates an IoT Smart Grid scenario where multiple
|
||||
IoT power meters are constantly sending electricity usage measurements to the database.
|
||||
|
||||
Details:
|
||||
This code sample simulates an IoT Smart Grid scenario where multiple IoT power meters are sending electricity usage measurements to a SQL Server memory optimized database.
|
||||
The Data Generator, that can be started either from the Console or the Windows Form client, produces a data generated spike to simulate a
|
||||
shock absorber scenario: https://blogs.technet.microsoft.com/dataplatforminsider/2013/09/19/in-memory-oltp-common-design-pattern-high-data-input-rateshock-absorber/.
|
||||
Every async task in the Data Generator produces a batch of records with random values in order to simulate the data of an IoT power meter.
|
||||
It then calls a natively compiled stored procedure, that accepts an memory optimized table valued parameter (TVP), to insert the data into an memory optimized SQL Server table.
|
||||
In addition to the in-memory features, the sample is leveraging System-Versioned Temporal Tables: https://msdn.microsoft.com/en-us/library/dn935015.aspx for building version history,
|
||||
Clustered Columnstore Index: https://msdn.microsoft.com/en-us/library/dn817827.aspx) for enabling real time operational analytics, and
|
||||
Power BI: https://powerbi.microsoft.com/en-us/desktop/ for data visualization.
|
||||
*/
|
||||
namespace Client
|
||||
{
|
||||
public partial class FrmMain : Form
|
||||
{
|
||||
private SqlDataGenerator dataGenerator;
|
||||
private string connection;
|
||||
private string spName;
|
||||
private string logFileName;
|
||||
private string powerBIDesktopPath;
|
||||
private int tasks;
|
||||
private int meters;
|
||||
private int batchSize;
|
||||
private int delay;
|
||||
private int commandTimeout;
|
||||
private int shockFrequency;
|
||||
private int shockDuration;
|
||||
private int rpsFrequency;
|
||||
private int rpsChartTime = 0;
|
||||
private int enableShock;
|
||||
|
||||
public FrmMain()
|
||||
{
|
||||
InitializeComponent();
|
||||
Init();
|
||||
|
||||
this.dataGenerator = new SqlDataGenerator(this.connection, this.spName, this.commandTimeout, this.meters, this.tasks, this.delay, this.batchSize, this.ExceptionCallback);
|
||||
}
|
||||
|
||||
private void ExceptionCallback(int taskId, Exception exception)
|
||||
{
|
||||
HandleException(exception, taskId);
|
||||
}
|
||||
|
||||
private void HandleException(Exception exception, int? taskId = null)
|
||||
{
|
||||
string ex = taskId?.ToString() + " - " + exception.Message + (exception.InnerException != null ? "\n\nInner Exception\n" + exception.InnerException : "");
|
||||
|
||||
MessageBox.Show(ex, "Invalid Input Parameter", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
|
||||
using (StreamWriter w = File.AppendText(logFileName)) { w.WriteLine("\r\n{0}: {1}", DateTime.Now, ex); }
|
||||
}
|
||||
|
||||
private async void Start_Click(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (enableShock == 1) this.mainTimer.Start();
|
||||
this.rpsTimer.Start();
|
||||
this.Stop.Enabled = true;
|
||||
this.Stop.Update();
|
||||
this.Start.Enabled = false;
|
||||
this.Start.Update();
|
||||
|
||||
await this.dataGenerator.RunAsync();
|
||||
}
|
||||
catch (Exception exception) { HandleException(exception); }
|
||||
}
|
||||
|
||||
private async void Stop_Click(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.UpdateChart(-1);
|
||||
if (enableShock == 1) this.mainTimer.Stop();
|
||||
this.rpsTimer.Stop();
|
||||
if (enableShock == 1) this.shockTimer.Stop();
|
||||
this.lblRpsValue.Text = "0";
|
||||
this.lblTasksValue.Text = "0";
|
||||
this.Stop.Enabled = false;
|
||||
this.Stop.Update();
|
||||
this.Start.Enabled = true;
|
||||
this.Start.Update();
|
||||
|
||||
await this.dataGenerator.StopAsync();
|
||||
this.dataGenerator.RpsReset();
|
||||
}
|
||||
catch (Exception exception) { HandleException(exception); }
|
||||
}
|
||||
|
||||
private void UpdateChart(double rps)
|
||||
{
|
||||
if (rps >= 0)
|
||||
{
|
||||
rpsChartTime++;
|
||||
|
||||
if (rpsChartTime > this.RpsChart.ChartAreas[0].AxisX.Maximum)
|
||||
{
|
||||
this.RpsChart.ChartAreas[0].AxisX.Maximum += 100;
|
||||
}
|
||||
this.RpsChart.Series[0].Points.Add(new DataPoint(rpsChartTime, rps));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.RpsChart.Series[0].Points.Clear();
|
||||
rpsChartTime = 0;
|
||||
}
|
||||
this.RpsChart.Update();
|
||||
}
|
||||
|
||||
private void Init()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Read Config Settings
|
||||
this.connection = ConfigurationManager.ConnectionStrings["Db"].ConnectionString;
|
||||
this.spName = ConfigurationManager.AppSettings["insertSPName"];
|
||||
this.logFileName = ConfigurationManager.AppSettings["logFileName"];
|
||||
this.powerBIDesktopPath = ConfigurationManager.AppSettings["powerBIDesktopPath"];
|
||||
this.tasks = int.Parse(ConfigurationManager.AppSettings["numberOfTasks"]);
|
||||
this.meters = int.Parse(ConfigurationManager.AppSettings["numberOfMeters"]);
|
||||
this.batchSize = int.Parse(ConfigurationManager.AppSettings["batchSize"]);
|
||||
this.delay = int.Parse(ConfigurationManager.AppSettings["commandDelay"]);
|
||||
this.commandTimeout = int.Parse(ConfigurationManager.AppSettings["commandTimeout"]);
|
||||
this.shockFrequency = int.Parse(ConfigurationManager.AppSettings["shockFrequency"]);
|
||||
this.shockDuration = int.Parse(ConfigurationManager.AppSettings["shockDuration"]);
|
||||
this.enableShock = int.Parse(ConfigurationManager.AppSettings["enableShock"]);
|
||||
|
||||
this.rpsFrequency = int.Parse(ConfigurationManager.AppSettings["rpsFrequency"]);
|
||||
|
||||
// Initialize Timers
|
||||
this.mainTimer.Interval = shockFrequency;
|
||||
this.shockTimer.Interval = shockDuration;
|
||||
this.rpsTimer.Interval = this.rpsFrequency;
|
||||
|
||||
// Initialize Labels
|
||||
this.lblTasksValue.Text = string.Format("{0:#,#}", this.tasks).ToString();
|
||||
this.lblFrequencyValue.Text = (this.shockFrequency/1000).ToString() + "/" + (this.shockDuration / 1000).ToString();
|
||||
this.lblBatchSizeValue.Text = string.Format("{0:#,#}", this.batchSize).ToString();
|
||||
this.lblMetersValue.Text = string.Format("{0:#,#}", this.meters).ToString();
|
||||
|
||||
if (batchSize <= 0) throw new SqlDataGeneratorException("The Batch Size cannot be less or equal to zero.");
|
||||
|
||||
if (tasks <= 0) throw new SqlDataGeneratorException("Number Of Tasks cannot be less or equal to zero.");
|
||||
|
||||
if (delay < 0) throw new SqlDataGeneratorException("Delay cannot be less than zero");
|
||||
|
||||
if (meters <= 0) throw new SqlDataGeneratorException("Number Of Meters cannot be less than zero");
|
||||
|
||||
if (meters < batchSize * tasks) throw new SqlDataGeneratorException("Number Of Meters cannot be less than (Tasks * BatchSize).");
|
||||
}
|
||||
catch (Exception exception) { HandleException(exception); }
|
||||
}
|
||||
|
||||
private void rpsTimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.lblTasksValue.Text = this.dataGenerator.RunningTasks.ToString();
|
||||
|
||||
double rps = this.dataGenerator.Rps;
|
||||
if (dataGenerator.IsRunning)
|
||||
{
|
||||
if (this.dataGenerator.RunningTasks == 0) return;
|
||||
|
||||
if (rps > 0)
|
||||
{
|
||||
this.lblRpsValue.Text = string.Format("{0:#,#}", rps).ToString();
|
||||
UpdateChart(rps);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception exception) { HandleException(exception); }
|
||||
}
|
||||
|
||||
private void mainTimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
if (this.dataGenerator.IsRunning)
|
||||
{
|
||||
this.dataGenerator.Delay = 0;
|
||||
this.shockTimer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
private void shockTimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
Random rand = new Random();
|
||||
this.dataGenerator.Delay = rand.Next(1500,3000);
|
||||
this.shockTimer.Stop();
|
||||
}
|
||||
|
||||
private void powerBIReport_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
|
||||
{
|
||||
ProcessStartInfo psi = new ProcessStartInfo();
|
||||
psi.FileName = this.powerBIDesktopPath;
|
||||
psi.Arguments = @"Reports\PowerDashboard.pbix";
|
||||
Process.Start(psi);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
<?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>
|
||||
<metadata name="bottomToolStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<metadata name="rpsTimer.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>159, 17</value>
|
||||
</metadata>
|
||||
<metadata name="mainTimer.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>258, 17</value>
|
||||
</metadata>
|
||||
<metadata name="shockTimer.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>367, 17</value>
|
||||
</metadata>
|
||||
</root>
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Client
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new FrmMain());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Client")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Client")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2016")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("6c0e1820-a10b-47da-b806-939cbcd0dd39")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// 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("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Client.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Client.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
<?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.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: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" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
</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" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
</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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Client.Properties
|
||||
{
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
|
||||
{
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default
|
||||
{
|
||||
get
|
||||
{
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
|
||||
<Profiles>
|
||||
<Profile Name="(Default)" />
|
||||
</Profiles>
|
||||
<Settings />
|
||||
</SettingsFile>
|
||||
@@ -0,0 +1,103 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{6C0E1820-A10B-47DA-B806-939CBCD0DD39}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Client</RootNamespace>
|
||||
<AssemblyName>Client</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Windows.Forms.DataVisualization" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Deployment" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="FrmMain.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="FrmMain.Designer.cs">
|
||||
<DependentUpon>FrmMain.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<EmbeddedResource Include="FrmMain.resx">
|
||||
<DependentUpon>FrmMain.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
<DesignTime>True</DesignTime>
|
||||
</Compile>
|
||||
<None Include="..\App.config">
|
||||
<Link>App.config</Link>
|
||||
</None>
|
||||
<None Include="..\PowerDashboard.pbix">
|
||||
<Link>Reports\PowerDashboard.pbix</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DataGenerator\DataGenerator.csproj">
|
||||
<Project>{d871b062-06a7-49e3-8bcd-8465b772fc52}</Project>
|
||||
<Name>DataGenerator</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
Reference in New Issue
Block a user