diff --git a/src/Umbraco.Core/Properties/AssemblyInfo.cs b/src/Umbraco.Core/Properties/AssemblyInfo.cs
index d1ddadde37..dbc1ab6c93 100644
--- a/src/Umbraco.Core/Properties/AssemblyInfo.cs
+++ b/src/Umbraco.Core/Properties/AssemblyInfo.cs
@@ -32,6 +32,7 @@ using System.Security.Permissions;
[assembly: InternalsVisibleTo("umbraco.editorControls")]
[assembly: InternalsVisibleTo("Umbraco.Tests")]
+[assembly: InternalsVisibleTo("Umbraco.Tests.Benchmarks")]
[assembly: InternalsVisibleTo("Umbraco.Core")]
[assembly: InternalsVisibleTo("Umbraco.Web")]
[assembly: InternalsVisibleTo("Umbraco.Web.UI")]
diff --git a/src/Umbraco.Tests.Benchmarks/App.config b/src/Umbraco.Tests.Benchmarks/App.config
new file mode 100644
index 0000000000..a988966f23
--- /dev/null
+++ b/src/Umbraco.Tests.Benchmarks/App.config
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Umbraco.Tests.Benchmarks/BulkInsertBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/BulkInsertBenchmarks.cs
new file mode 100644
index 0000000000..5212a4f524
--- /dev/null
+++ b/src/Umbraco.Tests.Benchmarks/BulkInsertBenchmarks.cs
@@ -0,0 +1,210 @@
+using System;
+using System.Collections.Generic;
+using System.Data.SqlServerCe;
+using System.Diagnostics;
+using System.IO;
+using System.Threading;
+using System.Xml;
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Columns;
+using BenchmarkDotNet.Configs;
+using BenchmarkDotNet.Diagnosers;
+using BenchmarkDotNet.Diagnostics.Windows;
+using BenchmarkDotNet.Jobs;
+using BenchmarkDotNet.Loggers;
+using BenchmarkDotNet.Reports;
+using BenchmarkDotNet.Running;
+using BenchmarkDotNet.Validators;
+using Umbraco.Core;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models.Rdbms;
+using Umbraco.Core.Persistence;
+using Umbraco.Core.Persistence.Migrations.Initial;
+using Umbraco.Core.Persistence.SqlSyntax;
+using Umbraco.Tests.TestHelpers;
+using ILogger = Umbraco.Core.Logging.ILogger;
+
+namespace Umbraco.Tests.Benchmarks
+{
+ [Config(typeof(Config))]
+ public class BulkInsertBenchmarks
+ {
+ private class Config : ManualConfig
+ {
+ public Config()
+ {
+ Add(new MemoryDiagnoser());
+ //Add(ExecutionValidator.FailOnError);
+
+ //The 'quick and dirty' settings, so it runs a little quicker
+ // see benchmarkdotnet FAQ
+ Add(Job.Default
+ .WithLaunchCount(1) // benchmark process will be launched only once
+ .WithIterationTime(100) // 100ms per iteration
+ .WithWarmupCount(3) // 3 warmup iteration
+ .WithTargetCount(3)); // 3 target iteration
+ }
+ }
+
+ private static byte[] _initDbBytes = null;
+
+ [Setup]
+ public void Setup()
+ {
+ var logger = new DebugDiagnosticsLogger();
+ var path = TestHelper.CurrentAssemblyDirectory;
+
+ _sqlCeSyntax = new SqlCeSyntaxProvider();
+ _sqlServerSyntax = new SqlServerSyntaxProvider();
+
+ SetupSqlCe(path, logger);
+ SetupSqlServer(logger);
+
+
+ }
+
+ private void SetupSqlServer(ILogger logger)
+ {
+ //create the db
+ _dbSqlServer = new UmbracoDatabase(
+ "server=.\\SQLExpress;database=YOURDB;user id=YOURUSER;password=YOURPASS",
+ Constants.DatabaseProviders.SqlServer,
+ logger);
+
+ //drop the table
+ _dbSqlServer.Execute("DROP TABLE [umbracoServer]");
+
+ //re-create it
+ _dbSqlServer.Execute(@"CREATE TABLE [umbracoServer](
+ [id] [int] IDENTITY(1,1) NOT NULL,
+ [address] [nvarchar](500) NOT NULL,
+ [computerName] [nvarchar](255) NOT NULL,
+ [registeredDate] [datetime] NOT NULL CONSTRAINT [DF_umbracoServer_registeredDate] DEFAULT (getdate()),
+ [lastNotifiedDate] [datetime] NOT NULL,
+ [isActive] [bit] NOT NULL,
+ [isMaster] [bit] NOT NULL,
+ CONSTRAINT [PK_umbracoServer] PRIMARY KEY CLUSTERED
+(
+ [id] ASC
+)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
+)");
+ }
+
+ private void SetupSqlCe(string path, ILogger logger)
+ {
+ var dbName = string.Concat("Umb", Guid.NewGuid(), ".sdf");
+ AppDomain.CurrentDomain.SetData("DataDirectory", path);
+ var sqlCeConnectionString = $"Datasource=|DataDirectory|\\{dbName};Flush Interval=1;";
+
+ _dbFile = Path.Combine(path, dbName);
+
+ //only create the db one time
+ if (_initDbBytes == null)
+ {
+ using (var engine = new SqlCeEngine(sqlCeConnectionString))
+ {
+ engine.CreateDatabase();
+ }
+
+ //use the db to create the initial schema so we can reuse in each bench
+ using (_dbSqlCe = new UmbracoDatabase(
+ sqlCeConnectionString,
+ Constants.DatabaseProviders.SqlCe,
+ logger))
+ {
+ var creation = new DatabaseSchemaCreation(_dbSqlCe, logger, _sqlCeSyntax);
+ creation.InitializeDatabaseSchema();
+ }
+ _initDbBytes = File.ReadAllBytes(_dbFile);
+ }
+ else
+ {
+ File.WriteAllBytes(_dbFile, _initDbBytes);
+ }
+
+ //create the db
+ _dbSqlCe = new UmbracoDatabase(
+ sqlCeConnectionString,
+ Constants.DatabaseProviders.SqlCe,
+ logger);
+ }
+
+ private List GetData()
+ {
+ var data = new List();
+ for (var i = 0; i < 1000; i++)
+ {
+ data.Add(new ServerRegistrationDto
+ {
+ ServerAddress = "address" + Guid.NewGuid(),
+ ServerIdentity = "computer" + Guid.NewGuid(),
+ DateRegistered = DateTime.Now,
+ IsActive = true,
+ DateAccessed = DateTime.Now
+ });
+ }
+ return data;
+ }
+
+ [Cleanup]
+ public void Cleanup()
+ {
+ _dbSqlCe.Dispose();
+ _dbSqlServer.Dispose();
+ File.Delete(_dbFile);
+ }
+
+ private string _dbFile;
+ private UmbracoDatabase _dbSqlCe;
+ private UmbracoDatabase _dbSqlServer;
+ private ISqlSyntaxProvider _sqlCeSyntax;
+ private ISqlSyntaxProvider _sqlServerSyntax;
+
+ ///
+ /// Tests updating the existing XML way
+ ///
+ [Benchmark(Baseline = true)]
+ public void SqlCeOneByOne()
+ {
+ using (var tr = _dbSqlCe.GetTransaction())
+ {
+ _dbSqlCe.BulkInsertRecords(GetData(), tr, _sqlCeSyntax, useNativeSqlPlatformBulkInsert: false);
+ tr.Complete();
+ }
+ }
+
+ ///
+ /// Tests updating with only the object graph
+ ///
+ [Benchmark]
+ public void SqlCeTableDirect()
+ {
+ using (var tr = _dbSqlCe.GetTransaction())
+ {
+ _dbSqlCe.BulkInsertRecords(GetData(), tr, _sqlCeSyntax, useNativeSqlPlatformBulkInsert: true);
+ tr.Complete();
+ }
+ }
+
+ [Benchmark]
+ public void SqlServerBulkInsertStatements()
+ {
+ using (var tr = _dbSqlServer.GetTransaction())
+ {
+ _dbSqlServer.BulkInsertRecords(GetData(), tr, _sqlServerSyntax, useNativeSqlPlatformBulkInsert: false);
+ tr.Complete();
+ }
+ }
+
+ [Benchmark]
+ public void SqlServerBulkCopy()
+ {
+ using (var tr = _dbSqlServer.GetTransaction())
+ {
+ _dbSqlServer.BulkInsertRecords(GetData(), tr, _sqlServerSyntax, useNativeSqlPlatformBulkInsert: true);
+ tr.Complete();
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Tests.Benchmarks/Program.cs b/src/Umbraco.Tests.Benchmarks/Program.cs
new file mode 100644
index 0000000000..37c1ccd853
--- /dev/null
+++ b/src/Umbraco.Tests.Benchmarks/Program.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using BenchmarkDotNet.Running;
+
+namespace Umbraco.Tests.Benchmarks
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ var summary = BenchmarkRunner.Run();
+
+ Console.ReadLine();
+ }
+ }
+}
diff --git a/src/Umbraco.Tests.Benchmarks/Properties/AssemblyInfo.cs b/src/Umbraco.Tests.Benchmarks/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..fb00b8d4e9
--- /dev/null
+++ b/src/Umbraco.Tests.Benchmarks/Properties/AssemblyInfo.cs
@@ -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("Umbraco.Tests.Benchmarks")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Umbraco.Tests.Benchmarks")]
+[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("86deb346-089f-4106-89c8-d852b9cf2a33")]
+
+// 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")]
diff --git a/src/Umbraco.Tests.Benchmarks/TraceEvent.ReleaseNotes.txt b/src/Umbraco.Tests.Benchmarks/TraceEvent.ReleaseNotes.txt
new file mode 100644
index 0000000000..21fcb5d0ca
--- /dev/null
+++ b/src/Umbraco.Tests.Benchmarks/TraceEvent.ReleaseNotes.txt
@@ -0,0 +1,61 @@
+Version 1.0.0.3 - Initial release to NuGet, pre-release.
+
+ TraceEvent has been available from the site http://bcl.codeplex.com/wikipage?title=TraceEvent for some time now
+ this NuGet Version of the library supersedes that one. WHile the 'core' part of the library is unchanged,
+ we did change lesser used features, and change the namespace and DLL name, which will cause break. We anticipate
+ it will take an hour or so to 'port' to this version from the old one. Below are specific details on what
+ has changed to help in this port.
+
+ * The DLL has been renamed from TraceEvent.dll to Microsoft.Diagnostics.Tracing.TraceEvent.dll
+ * The name spaces for all classes have been changed. The easiest way to port is to simply place
+ the following using clauses at the top of any file that uses TraceEvent classes
+ using Microsoft.Diagnostics.Symbols;
+ using Microsoft.Diagnostics.Tracing;
+ using Microsoft.Diagnostics.Tracing.Etlx;
+ using Microsoft.Diagnostics.Tracing.Parsers.Clr;
+ using Microsoft.Diagnostics.Tracing.Parsers.Kernel;
+ using Microsoft.Diagnostics.Tracing.Session;
+ using Microsoft.Diagnostics.Tracing.Stacks;
+ * Any method with the name RelMSec in it has been changed to be RelativeMSec. The easiest port is to
+ simply globally rename RelMSec to RelativeMSec
+ * Any property in the Trace* classes that has the form Max*Index has been renamed to Count.
+ * A number of methods have been declared obsolete, these are mostly renames and the warning will tell you
+ how to update them.
+ * The following classes have been rename
+ SymPath -> SymbolPath
+ SymPathElement -> SymbolPathElement
+ SymbolReaderFlags -> SymbolReaderOptions
+ * TraceEventSession is now StopOnDispose (it will stop the session when TraceEventSesssion dies), by default
+ If you were relying on the kernel session living past the process that started it, you must now set
+ the StopOnDispose explicitly
+ * There used to be XmlAttrib extensions methods on StringBuilder for use in manifest generated TraceEventParsers
+ These have been moved to protected members of TraceEvent. The result is that in stead of writing
+ sb.XmlAttrib(...) you write XmlAttrib(sb, ...)
+ * References to Pdb in names have been replaced with 'Symbol' to conform to naming guidelines.
+
+ ***********************************************************************************************
+Version 1.0.0.4 - Initial stable release
+
+ Mostly this was insuring that the library was cleaned up in preparation
+ for release the TraceParserGen tool
+
+ Improved the docs, removed old code, fixed some naming convention stuff
+
+ * Additional changes from the PreRelease copy to the first Stable release
+
+ * The arguments to AddCallbackForProviderEvent were reversed!!!! (now provider than event)
+ * The arguments to Observe(string, string)!!!! (now provider than event)
+ * Event names for these APIs must include a / between the Task and Opcode names
+
+ * Many Events in KernelTraceEventParser were harmonized to be consistent with other conventions
+ * Events of the form PageFault* were typically renamed to Memory*
+ * The 'End' suffix was renamed to 'Stop' (its official name)
+ * PerfInfoSampleProf -> PerfInfoSample
+ * PerfInfoSampleProf -> PerfInfoSample
+ * ReadyThread -> DispatcherReadyThread
+ * StackWalkTraceData -> StackWalkStackTraceData
+ * FileIo -> FileIO
+ * DiskIo -> DiskIO
+
+ * Many Events in SymbolTraceEventParser were harmonized to be consistent with other conventions
+ * names with Symbol -> ImageID
diff --git a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj
new file mode 100644
index 0000000000..66033ba08e
--- /dev/null
+++ b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj
@@ -0,0 +1,149 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {86DEB346-089F-4106-89C8-D852B9CF2A33}
+ Exe
+ Properties
+ Umbraco.Tests.Benchmarks
+ Umbraco.Tests.Benchmarks
+ v4.5
+ 512
+
+
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+ Always
+
+
+
+ ..\packages\BenchmarkDotNet.0.9.9\lib\net45\BenchmarkDotNet.dll
+ True
+
+
+ ..\packages\BenchmarkDotNet.Core.0.9.9\lib\net45\BenchmarkDotNet.Core.dll
+ True
+
+
+ ..\packages\BenchmarkDotNet.Diagnostics.Windows.0.9.9\lib\net45\BenchmarkDotNet.Diagnostics.Windows.dll
+ True
+
+
+ ..\packages\BenchmarkDotNet.Toolchains.Roslyn.0.9.9\lib\net45\BenchmarkDotNet.Toolchains.Roslyn.dll
+ True
+
+
+ ..\packages\Microsoft.CodeAnalysis.Common.1.3.2\lib\net45\Microsoft.CodeAnalysis.dll
+ True
+
+
+ ..\packages\Microsoft.CodeAnalysis.CSharp.1.3.2\lib\net45\Microsoft.CodeAnalysis.CSharp.dll
+ True
+
+
+ ..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.1.0.41\lib\net40\Microsoft.Diagnostics.Tracing.TraceEvent.dll
+ True
+
+
+
+ ..\packages\System.Collections.Immutable.1.1.37\lib\dotnet\System.Collections.Immutable.dll
+ True
+
+
+
+ ..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.dll
+ True
+
+
+ ..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.Entity.dll
+ True
+
+
+
+ ..\packages\System.Reflection.Metadata.1.2.0\lib\portable-net45+win8\System.Reflection.Metadata.dll
+ True
+
+
+ ..\packages\System.Threading.Tasks.Extensions.4.0.0\lib\portable-net45+win8+wp8+wpa81\System.Threading.Tasks.Extensions.dll
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {31785bc3-256c-4613-b2f5-a1b0bdded8c1}
+ Umbraco.Core
+
+
+ {5d3b8245-ada6-453f-a008-50ed04bfe770}
+ Umbraco.Tests
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
+
+
+
+ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\amd64\*.* "$(TargetDir)amd64\" /Y /F /E /I /C /D
+xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\" /Y /F /E /I /C /D
+
+
+
\ No newline at end of file
diff --git a/src/Umbraco.Tests.Benchmarks/packages.config b/src/Umbraco.Tests.Benchmarks/packages.config
new file mode 100644
index 0000000000..c4d2ba1df2
--- /dev/null
+++ b/src/Umbraco.Tests.Benchmarks/packages.config
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/umbraco.sln b/src/umbraco.sln
index f9267c0412..8648f6ed5c 100644
--- a/src/umbraco.sln
+++ b/src/umbraco.sln
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
-VisualStudioVersion = 14.0.25123.0
+VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Web.UI", "Umbraco.Web.UI\Umbraco.Web.UI.csproj", "{4C4C194C-B5E4-4991-8F87-4373E24CC19F}"
EndProject
@@ -114,6 +114,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{5B03EF4E
..\build\NuSpecs\build\UmbracoCms.targets = ..\build\NuSpecs\build\UmbracoCms.targets
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Tests.Benchmarks", "Umbraco.Tests.Benchmarks\Umbraco.Tests.Benchmarks.csproj", "{86DEB346-089F-4106-89C8-D852B9CF2A33}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -178,6 +180,10 @@ Global
{07FBC26B-2927-4A22-8D96-D644C667FECC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{07FBC26B-2927-4A22-8D96-D644C667FECC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{07FBC26B-2927-4A22-8D96-D644C667FECC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {86DEB346-089F-4106-89C8-D852B9CF2A33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {86DEB346-089F-4106-89C8-D852B9CF2A33}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {86DEB346-089F-4106-89C8-D852B9CF2A33}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {86DEB346-089F-4106-89C8-D852B9CF2A33}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -187,5 +193,6 @@ Global
{5D3B8245-ADA6-453F-A008-50ED04BFE770} = {B5BD12C1-A454-435E-8A46-FF4A364C0382}
{E3F9F378-AFE1-40A5-90BD-82833375DBFE} = {227C3B55-80E5-4E7E-A802-BE16C5128B9D}
{5B03EF4E-E0AC-4905-861B-8C3EC1A0D458} = {227C3B55-80E5-4E7E-A802-BE16C5128B9D}
+ {86DEB346-089F-4106-89C8-D852B9CF2A33} = {B5BD12C1-A454-435E-8A46-FF4A364C0382}
EndGlobalSection
EndGlobal