Merge branch '6.1.4-cmsContentXml_Optimizations' into 6.1.4
This commit is contained in:
@@ -296,16 +296,13 @@ namespace Umbraco.Core
|
||||
if (databaseSettings != null && string.IsNullOrWhiteSpace(databaseSettings.ConnectionString) == false && string.IsNullOrWhiteSpace(databaseSettings.ProviderName) == false)
|
||||
{
|
||||
var providerName = "System.Data.SqlClient";
|
||||
string connString = null;
|
||||
if (!string.IsNullOrEmpty(ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName].ProviderName))
|
||||
{
|
||||
providerName = ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName].ProviderName;
|
||||
|
||||
_connectionString =
|
||||
ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName].ConnectionString;
|
||||
|
||||
connString = ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName].ConnectionString;
|
||||
}
|
||||
|
||||
Initialize(providerName);
|
||||
Initialize(providerName, connString);
|
||||
}
|
||||
else if (ConfigurationManager.AppSettings.ContainsKey(GlobalSettings.UmbracoConnectionName) && string.IsNullOrEmpty(ConfigurationManager.AppSettings[GlobalSettings.UmbracoConnectionName]) == false)
|
||||
{
|
||||
@@ -369,6 +366,12 @@ namespace Umbraco.Core
|
||||
}
|
||||
}
|
||||
|
||||
internal void Initialize(string providerName, string connectionString)
|
||||
{
|
||||
_connectionString = connectionString;
|
||||
Initialize(providerName);
|
||||
}
|
||||
|
||||
internal DatabaseSchemaResult ValidateDatabaseSchema()
|
||||
{
|
||||
if (_configured == false || (string.IsNullOrEmpty(_connectionString) || string.IsNullOrEmpty(ProviderName)))
|
||||
|
||||
@@ -14,9 +14,9 @@ namespace Umbraco.Core.Persistence
|
||||
/// </remarks>
|
||||
internal class DefaultDatabaseFactory : DisposableObject, IDatabaseFactory
|
||||
{
|
||||
private readonly string _connectionStringName;
|
||||
private readonly string _connectionString;
|
||||
private readonly string _providerName;
|
||||
private readonly string _connectionStringName;
|
||||
public string ConnectionString { get; private set; }
|
||||
public string ProviderName { get; private set; }
|
||||
|
||||
//very important to have ThreadStatic:
|
||||
// see: http://issues.umbraco.org/issue/U4-2172
|
||||
@@ -52,8 +52,8 @@ namespace Umbraco.Core.Persistence
|
||||
{
|
||||
Mandate.ParameterNotNullOrEmpty(connectionString, "connectionString");
|
||||
Mandate.ParameterNotNullOrEmpty(providerName, "providerName");
|
||||
_connectionString = connectionString;
|
||||
_providerName = providerName;
|
||||
ConnectionString = connectionString;
|
||||
ProviderName = providerName;
|
||||
}
|
||||
|
||||
public UmbracoDatabase CreateDatabase()
|
||||
@@ -68,8 +68,8 @@ namespace Umbraco.Core.Persistence
|
||||
//double check
|
||||
if (_nonHttpInstance == null)
|
||||
{
|
||||
_nonHttpInstance = string.IsNullOrEmpty(_connectionString) == false && string.IsNullOrEmpty(_providerName) == false
|
||||
? new UmbracoDatabase(_connectionString, _providerName)
|
||||
_nonHttpInstance = string.IsNullOrEmpty(ConnectionString) == false && string.IsNullOrEmpty(ProviderName) == false
|
||||
? new UmbracoDatabase(ConnectionString, ProviderName)
|
||||
: new UmbracoDatabase(_connectionStringName);
|
||||
}
|
||||
}
|
||||
@@ -81,8 +81,8 @@ namespace Umbraco.Core.Persistence
|
||||
if (HttpContext.Current.Items.Contains(typeof(DefaultDatabaseFactory)) == false)
|
||||
{
|
||||
HttpContext.Current.Items.Add(typeof (DefaultDatabaseFactory),
|
||||
string.IsNullOrEmpty(_connectionString) == false && string.IsNullOrEmpty(_providerName) == false
|
||||
? new UmbracoDatabase(_connectionString, _providerName)
|
||||
string.IsNullOrEmpty(ConnectionString) == false && string.IsNullOrEmpty(ProviderName) == false
|
||||
? new UmbracoDatabase(ConnectionString, ProviderName)
|
||||
: new UmbracoDatabase(_connectionStringName));
|
||||
}
|
||||
return (UmbracoDatabase)HttpContext.Current.Items[typeof(DefaultDatabaseFactory)];
|
||||
|
||||
@@ -6,6 +6,7 @@ using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.Migrations.Initial;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence
|
||||
@@ -32,76 +33,142 @@ namespace Umbraco.Core.Persistence
|
||||
|
||||
public static void BulkInsertRecords<T>(this Database db, IEnumerable<T> collection)
|
||||
{
|
||||
//don't do anything if there are no records.
|
||||
if (collection.Any() == false)
|
||||
return;
|
||||
|
||||
using (var tr = db.GetTransaction())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (SqlSyntaxContext.SqlSyntaxProvider is SqlCeSyntaxProvider)
|
||||
{
|
||||
//SqlCe doesn't support bulk insert statements!
|
||||
|
||||
foreach (var poco in collection)
|
||||
{
|
||||
db.Insert(poco);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
string sql;
|
||||
using (var cmd = db.GenerateBulkInsertCommand(collection, db.Connection, out sql))
|
||||
{
|
||||
cmd.CommandText = sql;
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
tr.Complete();
|
||||
}
|
||||
catch
|
||||
{
|
||||
tr.Dispose();
|
||||
throw;
|
||||
}
|
||||
db.BulkInsertRecords(collection, tr);
|
||||
}
|
||||
}
|
||||
|
||||
internal static IDbCommand GenerateBulkInsertCommand<T>(this Database db, IEnumerable<T> collection, IDbConnection connection, out string sql)
|
||||
public static void BulkInsertRecords<T>(this Database db, IEnumerable<T> collection, Transaction tr)
|
||||
{
|
||||
//don't do anything if there are no records.
|
||||
if (collection.Any() == false)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (SqlSyntaxContext.SqlSyntaxProvider is SqlCeSyntaxProvider)
|
||||
{
|
||||
//SqlCe doesn't support bulk insert statements!
|
||||
|
||||
foreach (var poco in collection)
|
||||
{
|
||||
db.Insert(poco);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string[] sqlStatements;
|
||||
var cmds = db.GenerateBulkInsertCommand(collection, db.Connection, out sqlStatements);
|
||||
for (var i = 0; i < sqlStatements.Length; i++)
|
||||
{
|
||||
using (var cmd = cmds[i])
|
||||
{
|
||||
cmd.CommandText = sqlStatements[i];
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tr.Complete();
|
||||
}
|
||||
catch
|
||||
{
|
||||
tr.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a bulk insert command
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="db"></param>
|
||||
/// <param name="collection"></param>
|
||||
/// <param name="connection"></param>
|
||||
/// <param name="sql"></param>
|
||||
/// <returns>Sql commands with populated command parameters required to execute the sql statement</returns>
|
||||
/// <remarks>
|
||||
/// The limits for number of parameters are 2100 (in sql server, I think there's many more allowed in mysql). So
|
||||
/// we need to detect that many params and split somehow.
|
||||
/// For some reason the 2100 limit is not actually allowed even though the exception from sql server mentions 2100 as a max, perhaps it is 2099
|
||||
/// that is max. I've reduced it to 2000 anyways.
|
||||
/// </remarks>
|
||||
internal static IDbCommand[] GenerateBulkInsertCommand<T>(
|
||||
this Database db,
|
||||
IEnumerable<T> collection,
|
||||
IDbConnection connection,
|
||||
out string[] sql)
|
||||
{
|
||||
//A filter used below a few times to get all columns except result cols and not the primary key if it is auto-incremental
|
||||
Func<Database.PocoData, KeyValuePair<string, Database.PocoColumn>, bool> includeColumn = (data, column) =>
|
||||
{
|
||||
if (column.Value.ResultColumn) return false;
|
||||
if (data.TableInfo.AutoIncrement && column.Key == data.TableInfo.PrimaryKey) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
var pd = Database.PocoData.ForType(typeof(T));
|
||||
var tableName = db.EscapeTableName(pd.TableInfo.TableName);
|
||||
|
||||
//get all columns but not the primary key if it is auto-incremental
|
||||
var cols = string.Join(", ", (
|
||||
from c in pd.Columns
|
||||
where
|
||||
//don't return ResultColumns
|
||||
!c.Value.ResultColumn
|
||||
//if the table is auto-incremental, don't return the primary key
|
||||
&& (pd.TableInfo.AutoIncrement && c.Key != pd.TableInfo.PrimaryKey)
|
||||
select tableName + "." + db.EscapeSqlIdentifier(c.Key))
|
||||
.ToArray());
|
||||
//get all columns to include and format for sql
|
||||
var cols = string.Join(", ",
|
||||
pd.Columns
|
||||
.Where(c => includeColumn(pd, c))
|
||||
.Select(c => tableName + "." + db.EscapeSqlIdentifier(c.Key)).ToArray());
|
||||
|
||||
var cmd = db.CreateCommand(connection, "");
|
||||
var itemArray = collection.ToArray();
|
||||
|
||||
var pocoValues = new List<string>();
|
||||
var index = 0;
|
||||
foreach (var poco in collection)
|
||||
//calculate number of parameters per item
|
||||
var paramsPerItem = pd.Columns.Count(i => includeColumn(pd, i));
|
||||
|
||||
//Example calc:
|
||||
// Given: we have 4168 items in the itemArray, each item contains 8 command parameters (values to be inserterted)
|
||||
// 2100 / 8 = 262.5
|
||||
// Math.Floor(2100 / 8) = 262 items per trans
|
||||
// 4168 / 262 = 15.908... = there will be 16 trans in total
|
||||
|
||||
//all items will be included if we have disabled db parameters
|
||||
var itemsPerTrans = Math.Floor(2000.00 / paramsPerItem);
|
||||
//there will only be one transaction if we have disabled db parameters
|
||||
var numTrans = Math.Ceiling(itemArray.Length / itemsPerTrans);
|
||||
|
||||
var sqlQueries = new List<string>();
|
||||
var commands = new List<IDbCommand>();
|
||||
|
||||
for (var tIndex = 0; tIndex < numTrans; tIndex++)
|
||||
{
|
||||
var values = new List<string>();
|
||||
foreach (var i in pd.Columns)
|
||||
var itemsForTrans = itemArray
|
||||
.Skip(tIndex * (int)itemsPerTrans)
|
||||
.Take((int)itemsPerTrans);
|
||||
|
||||
var cmd = db.CreateCommand(connection, "");
|
||||
var pocoValues = new List<string>();
|
||||
var index = 0;
|
||||
foreach (var poco in itemsForTrans)
|
||||
{
|
||||
if (pd.TableInfo.AutoIncrement && i.Key == pd.TableInfo.PrimaryKey)
|
||||
var values = new List<string>();
|
||||
//get all columns except result cols and not the primary key if it is auto-incremental
|
||||
foreach (var i in pd.Columns.Where(x => includeColumn(pd, x)))
|
||||
{
|
||||
continue;
|
||||
db.AddParam(cmd, i.Value.GetValue(poco), "@");
|
||||
values.Add(string.Format("{0}{1}", "@", index++));
|
||||
}
|
||||
values.Add(string.Format("{0}{1}", "@", index++));
|
||||
db.AddParam(cmd, i.Value.GetValue(poco), "@");
|
||||
pocoValues.Add("(" + string.Join(",", values.ToArray()) + ")");
|
||||
}
|
||||
pocoValues.Add("(" + string.Join(",", values.ToArray()) + ")");
|
||||
|
||||
var sqlResult = string.Format("INSERT INTO {0} ({1}) VALUES {2}", tableName, cols, string.Join(", ", pocoValues));
|
||||
sqlQueries.Add(sqlResult);
|
||||
commands.Add(cmd);
|
||||
}
|
||||
sql = string.Format("INSERT INTO {0} ({1}) VALUES {2}", tableName, cols, string.Join(", ", pocoValues));
|
||||
return cmd;
|
||||
|
||||
sql = sqlQueries.ToArray();
|
||||
|
||||
return commands.ToArray();
|
||||
}
|
||||
|
||||
public static void CreateTable(this Database db, bool overwrite, Type modelType)
|
||||
|
||||
@@ -435,38 +435,7 @@ namespace Umbraco.Core.Persistence.Querying
|
||||
|
||||
public virtual string GetQuotedValue(object value, Type fieldType)
|
||||
{
|
||||
if (value == null) return "NULL";
|
||||
|
||||
if (!fieldType.UnderlyingSystemType.IsValueType && fieldType != typeof(string))
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
string.Format("Property of type: {0} is not supported", fieldType.FullName));
|
||||
}
|
||||
|
||||
if (fieldType == typeof(int))
|
||||
return ((int)value).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (fieldType == typeof(float))
|
||||
return ((float)value).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (fieldType == typeof(double))
|
||||
return ((double)value).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (fieldType == typeof(decimal))
|
||||
return ((decimal)value).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (fieldType == typeof (DateTime))
|
||||
{
|
||||
return "'" + EscapeParam(((DateTime)value).ToIsoString()) + "'";
|
||||
}
|
||||
|
||||
|
||||
if (fieldType == typeof(bool))
|
||||
return ((bool)value) ? Convert.ToString(1, CultureInfo.InvariantCulture) : Convert.ToString(0, CultureInfo.InvariantCulture);
|
||||
|
||||
return ShouldQuoteValue(fieldType)
|
||||
? "'" + EscapeParam(value) + "'"
|
||||
: value.ToString();
|
||||
return QueryHelper.GetQuotedValue(value, fieldType, EscapeParam, ShouldQuoteValue);
|
||||
}
|
||||
|
||||
public virtual string EscapeParam(object paramValue)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
@@ -440,43 +439,7 @@ namespace Umbraco.Core.Persistence.Querying
|
||||
|
||||
public virtual string GetQuotedValue(object value, Type fieldType)
|
||||
{
|
||||
if (value == null) return "NULL";
|
||||
|
||||
if (!fieldType.UnderlyingSystemType.IsValueType && fieldType != typeof(string))
|
||||
{
|
||||
//if (TypeSerializer.CanCreateFromString(fieldType))
|
||||
//{
|
||||
// return "'" + EscapeParam(TypeSerializer.SerializeToString(value)) + "'";
|
||||
//}
|
||||
|
||||
throw new NotSupportedException(
|
||||
string.Format("Property of type: {0} is not supported", fieldType.FullName));
|
||||
}
|
||||
|
||||
if (fieldType == typeof(int))
|
||||
return ((int)value).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (fieldType == typeof(float))
|
||||
return ((float)value).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (fieldType == typeof(double))
|
||||
return ((double)value).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (fieldType == typeof(decimal))
|
||||
return ((decimal)value).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (fieldType == typeof (DateTime))
|
||||
{
|
||||
return "'" + EscapeParam(((DateTime)value).ToIsoString()) + "'";
|
||||
}
|
||||
|
||||
|
||||
if (fieldType == typeof(bool))
|
||||
return ((bool)value) ? Convert.ToString(1, CultureInfo.InvariantCulture) : Convert.ToString(0, CultureInfo.InvariantCulture);
|
||||
|
||||
return ShouldQuoteValue(fieldType)
|
||||
? "'" + EscapeParam(value) + "'"
|
||||
: value.ToString();
|
||||
return QueryHelper.GetQuotedValue(value, fieldType, EscapeParam, ShouldQuoteValue);
|
||||
}
|
||||
|
||||
public virtual string EscapeParam(object paramValue)
|
||||
|
||||
70
src/Umbraco.Core/Persistence/Querying/QueryHelper.cs
Normal file
70
src/Umbraco.Core/Persistence/Querying/QueryHelper.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Querying
|
||||
{
|
||||
/// <summary>
|
||||
/// Logic that is shared with the expression helpers
|
||||
/// </summary>
|
||||
internal class QueryHelper
|
||||
{
|
||||
public static string GetQuotedValue(object value, Type fieldType, Func<object, string> escapeCallback = null, Func<Type, bool> shouldQuoteCallback = null)
|
||||
{
|
||||
if (value == null) return "NULL";
|
||||
|
||||
if (escapeCallback == null)
|
||||
{
|
||||
escapeCallback = EscapeParam;
|
||||
}
|
||||
if (shouldQuoteCallback == null)
|
||||
{
|
||||
shouldQuoteCallback = ShouldQuoteValue;
|
||||
}
|
||||
|
||||
if (!fieldType.UnderlyingSystemType.IsValueType && fieldType != typeof(string))
|
||||
{
|
||||
//if (TypeSerializer.CanCreateFromString(fieldType))
|
||||
//{
|
||||
// return "'" + EscapeParam(TypeSerializer.SerializeToString(value)) + "'";
|
||||
//}
|
||||
|
||||
throw new NotSupportedException(
|
||||
string.Format("Property of type: {0} is not supported", fieldType.FullName));
|
||||
}
|
||||
|
||||
if (fieldType == typeof(int))
|
||||
return ((int)value).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (fieldType == typeof(float))
|
||||
return ((float)value).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (fieldType == typeof(double))
|
||||
return ((double)value).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (fieldType == typeof(decimal))
|
||||
return ((decimal)value).ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
if (fieldType == typeof(DateTime))
|
||||
{
|
||||
return "'" + EscapeParam(((DateTime)value).ToIsoString()) + "'";
|
||||
}
|
||||
|
||||
if (fieldType == typeof(bool))
|
||||
return ((bool)value) ? Convert.ToString(1, CultureInfo.InvariantCulture) : Convert.ToString(0, CultureInfo.InvariantCulture);
|
||||
|
||||
return ShouldQuoteValue(fieldType)
|
||||
? "'" + EscapeParam(value) + "'"
|
||||
: value.ToString();
|
||||
}
|
||||
|
||||
public static string EscapeParam(object paramValue)
|
||||
{
|
||||
return paramValue.ToString().Replace("'", "''");
|
||||
}
|
||||
|
||||
public static bool ShouldQuoteValue(Type fieldType)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -449,7 +449,19 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
foreach (var dto in dtos)
|
||||
{
|
||||
yield return CreateContentFromDto(dto, dto.VersionId);
|
||||
//Check in the cache first. If it exists there AND it is published
|
||||
// then we can use that entity. Otherwise if it is not published (which can be the case
|
||||
// because we only store the 'latest' entries in the cache which might not be the published
|
||||
// version)
|
||||
var fromCache = TryGetFromCache(dto.NodeId);
|
||||
if (fromCache.Success && fromCache.Result.Published)
|
||||
{
|
||||
yield return fromCache.Result;
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return CreateContentFromDto(dto, dto.VersionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
IContent GetByLanguage(int id, string language);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all published Content byh the specified query
|
||||
/// Gets all published Content by the specified query
|
||||
/// </summary>
|
||||
/// <param name="query">Query to execute against published versions</param>
|
||||
/// <returns>An enumerable list of <see cref="IContent"/></returns>
|
||||
|
||||
@@ -85,11 +85,10 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <returns></returns>
|
||||
public TEntity Get(TId id)
|
||||
{
|
||||
Guid key = id is int ? ConvertIdToGuid(id) : ConvertStringIdToGuid(id.ToString());
|
||||
var rEntity = _cache.GetById(typeof(TEntity), key);
|
||||
if (rEntity != null)
|
||||
var fromCache = TryGetFromCache(id);
|
||||
if (fromCache.Success)
|
||||
{
|
||||
return (TEntity)rEntity;
|
||||
return fromCache.Result;
|
||||
}
|
||||
|
||||
var entity = PerformGet(id);
|
||||
@@ -112,6 +111,17 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
return entity;
|
||||
}
|
||||
|
||||
protected Attempt<TEntity> TryGetFromCache(TId id)
|
||||
{
|
||||
Guid key = id is int ? ConvertIdToGuid(id) : ConvertStringIdToGuid(id.ToString());
|
||||
var rEntity = _cache.GetById(typeof(TEntity), key);
|
||||
if (rEntity != null)
|
||||
{
|
||||
return new Attempt<TEntity>(true, (TEntity) rEntity);
|
||||
}
|
||||
return Attempt<TEntity>.False;
|
||||
}
|
||||
|
||||
protected abstract IEnumerable<TEntity> PerformGetAll(params TId[] ids);
|
||||
/// <summary>
|
||||
/// Gets all entities of type TEntity or a list according to the passed in Ids
|
||||
@@ -173,14 +183,12 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <returns></returns>
|
||||
public bool Exists(TId id)
|
||||
{
|
||||
Guid key = id is int ? ConvertIdToGuid(id) : ConvertStringIdToGuid(id.ToString());
|
||||
var rEntity = _cache.GetById(typeof(TEntity), key);
|
||||
if (rEntity != null)
|
||||
var fromCache = TryGetFromCache(id);
|
||||
if (fromCache.Success)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return PerformExists(id);
|
||||
return PerformExists(id);
|
||||
}
|
||||
|
||||
protected abstract int PerformCount(IQuery<TEntity> query);
|
||||
|
||||
@@ -13,6 +13,7 @@ using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Caching;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
using Umbraco.Core.Publishing;
|
||||
|
||||
@@ -250,6 +251,17 @@ namespace Umbraco.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
internal IEnumerable<IContent> GetPublishedContentOfContentType(int id)
|
||||
{
|
||||
using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork()))
|
||||
{
|
||||
var query = Query<IContent>.Builder.Where(x => x.ContentTypeId == id);
|
||||
var contents = repository.GetByPublishedVersion(query);
|
||||
|
||||
return contents;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of <see cref="IContent"/> objects by Level
|
||||
/// </summary>
|
||||
@@ -431,6 +443,19 @@ namespace Umbraco.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all published content items
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal IEnumerable<IContent> GetAllPublished()
|
||||
{
|
||||
using (var repository = _repositoryFactory.CreateContentRepository(_uowProvider.GetUnitOfWork()))
|
||||
{
|
||||
var query = Query<IContent>.Builder.Where(x => x.Trashed == false);
|
||||
return repository.GetByPublishedVersion(query);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of <see cref="IContent"/> objects, which has an expiration date less than or equal to today.
|
||||
/// </summary>
|
||||
@@ -1353,46 +1378,45 @@ namespace Umbraco.Core.Services
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
using (var repository = _repositoryFactory.CreateContentRepository(uow))
|
||||
{
|
||||
if (contentTypeIds.Any() == false)
|
||||
{
|
||||
//Remove all Document records from the cmsContentXml table (DO NOT REMOVE Media/Members!)
|
||||
uow.Database.Execute(@"DELETE FROM cmsContentXml WHERE nodeId IN
|
||||
(SELECT DISTINCT cmsContentXml.nodeId FROM cmsContentXml
|
||||
INNER JOIN cmsDocument ON cmsContentXml.nodeId = cmsDocument.nodeId)");
|
||||
|
||||
//get all content items that are published
|
||||
// Consider creating a Path query instead of recursive method:
|
||||
// var query = Query<IContent>.Builder.Where(x => x.Path.StartsWith("-1"));
|
||||
var rootContent = GetRootContent();
|
||||
foreach (var content in rootContent.Where(content => content.Published))
|
||||
{
|
||||
list.Add(content);
|
||||
list.AddRange(GetPublishedDescendants(content));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var id in contentTypeIds)
|
||||
{
|
||||
//first we'll clear out the data from the cmsContentXml table for this type
|
||||
uow.Database.Execute(@"delete from cmsContentXml where nodeId in
|
||||
(select cmsDocument.nodeId from cmsDocument
|
||||
inner join cmsContent on cmsDocument.nodeId = cmsContent.nodeId
|
||||
where published = 1 and contentType = @contentTypeId)", new {contentTypeId = id});
|
||||
//First we're going to get the data that needs to be inserted before clearing anything, this
|
||||
//ensures that we don't accidentally leave the content xml table empty if something happens
|
||||
//during the lookup process.
|
||||
|
||||
//now get all published content objects of this type and add to the list
|
||||
list.AddRange(GetContentOfContentType(id).Where(content => content.Published));
|
||||
}
|
||||
}
|
||||
list.AddRange(contentTypeIds.Any() == false
|
||||
? GetAllPublished()
|
||||
: contentTypeIds.SelectMany(GetPublishedContentOfContentType));
|
||||
|
||||
var xmlItems = new List<ContentXmlDto>();
|
||||
foreach (var c in list)
|
||||
{
|
||||
//generate the xml
|
||||
var xml = c.ToXml();
|
||||
//create the dto to insert
|
||||
var poco = new ContentXmlDto { NodeId = c.Id, Xml = xml.ToString(SaveOptions.None) };
|
||||
//insert it into the database
|
||||
uow.Database.Insert(poco);
|
||||
xmlItems.Add(new ContentXmlDto {NodeId = c.Id, Xml = xml.ToString(SaveOptions.None)});
|
||||
}
|
||||
|
||||
//Ok, now we need to remove the data and re-insert it, we'll do this all in one transaction too.
|
||||
using (var tr = uow.Database.GetTransaction())
|
||||
{
|
||||
if (contentTypeIds.Any() == false)
|
||||
{
|
||||
//Remove all Document records from the cmsContentXml table (DO NOT REMOVE Media/Members!)
|
||||
uow.Database.Execute(@"DELETE FROM cmsContentXml WHERE nodeId IN
|
||||
(SELECT DISTINCT cmsContentXml.nodeId FROM cmsContentXml
|
||||
INNER JOIN cmsDocument ON cmsContentXml.nodeId = cmsDocument.nodeId)");
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var id in contentTypeIds)
|
||||
{
|
||||
//first we'll clear out the data from the cmsContentXml table for this type
|
||||
uow.Database.Execute(@"delete from cmsContentXml where nodeId in
|
||||
(select cmsDocument.nodeId from cmsDocument
|
||||
inner join cmsContent on cmsDocument.nodeId = cmsContent.nodeId
|
||||
where published = 1 and contentType = @contentTypeId)", new { contentTypeId = id });
|
||||
}
|
||||
}
|
||||
|
||||
//bulk insert it into the database
|
||||
uow.Database.BulkInsertRecords(xmlItems, tr);
|
||||
}
|
||||
}
|
||||
Audit.Add(AuditTypes.Publish, "RebuildXmlStructures completed, the xml has been regenerated in the database", 0, -1);
|
||||
|
||||
@@ -11,10 +11,11 @@ using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
using Umbraco.Core.Publishing;
|
||||
|
||||
namespace Umbraco.Core.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// Represents the Media Service, which is an easy access to operations involving <see cref="IMedia"/>
|
||||
/// </summary>
|
||||
public class MediaService : IMediaService
|
||||
@@ -875,15 +876,16 @@ namespace Umbraco.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
var xmlItems = new List<ContentXmlDto>();
|
||||
foreach (var c in list)
|
||||
{
|
||||
//generate the xml
|
||||
var xml = c.ToXml();
|
||||
//create the dto to insert
|
||||
var poco = new ContentXmlDto { NodeId = c.Id, Xml = xml.ToString(SaveOptions.None) };
|
||||
//insert it into the database
|
||||
uow.Database.Insert(poco);
|
||||
xmlItems.Add(new ContentXmlDto { NodeId = c.Id, Xml = xml.ToString(SaveOptions.None) });
|
||||
}
|
||||
//bulk insert it into the database
|
||||
uow.Database.BulkInsertRecords(xmlItems);
|
||||
}
|
||||
Audit.Add(AuditTypes.Publish, "RebuildXmlStructures completed, the xml has been regenerated in the database", 0, -1);
|
||||
}
|
||||
|
||||
@@ -462,6 +462,7 @@
|
||||
<Compile Include="Persistence\Querying\IQuery.cs" />
|
||||
<Compile Include="Persistence\Querying\ModelToSqlExpressionHelper.cs" />
|
||||
<Compile Include="Persistence\Querying\Query.cs" />
|
||||
<Compile Include="Persistence\Querying\QueryHelper.cs" />
|
||||
<Compile Include="Persistence\Querying\SqlTranslator.cs" />
|
||||
<Compile Include="Persistence\Relators\DictionaryLanguageTextRelator.cs" />
|
||||
<Compile Include="Persistence\Relators\UserSectionRelator.cs" />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
@@ -73,13 +74,47 @@ namespace Umbraco.Tests.Persistence
|
||||
db.OpenSharedConnection();
|
||||
|
||||
// Act
|
||||
string sql;
|
||||
string[] sql;
|
||||
db.GenerateBulkInsertCommand(servers, db.Connection, out sql);
|
||||
db.CloseSharedConnection();
|
||||
|
||||
// Assert
|
||||
Assert.That(sql,
|
||||
Assert.That(sql[0],
|
||||
Is.EqualTo("INSERT INTO [umbracoServer] ([umbracoServer].[address], [umbracoServer].[computerName], [umbracoServer].[registeredDate], [umbracoServer].[lastNotifiedDate], [umbracoServer].[isActive]) VALUES (@0,@1,@2,@3,@4), (@5,@6,@7,@8,@9)"));
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void Generate_Bulk_Import_Sql_Exceeding_Max_Params()
|
||||
{
|
||||
// Arrange
|
||||
var db = DatabaseContext.Database;
|
||||
|
||||
var servers = new List<ServerRegistrationDto>();
|
||||
for (var i = 0; i < 1500; i++)
|
||||
{
|
||||
servers.Add(new ServerRegistrationDto
|
||||
{
|
||||
Address = "address" + i,
|
||||
ComputerName = "computer" + i,
|
||||
DateRegistered = DateTime.Now,
|
||||
IsActive = true,
|
||||
LastNotified = DateTime.Now
|
||||
});
|
||||
}
|
||||
db.OpenSharedConnection();
|
||||
|
||||
// Act
|
||||
string[] sql;
|
||||
db.GenerateBulkInsertCommand(servers, db.Connection, out sql);
|
||||
db.CloseSharedConnection();
|
||||
|
||||
// Assert
|
||||
Assert.That(sql.Length, Is.EqualTo(4));
|
||||
foreach (var s in sql)
|
||||
{
|
||||
Assert.LessOrEqual(Regex.Matches(s, "@\\d+").Count, 2000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -306,12 +306,13 @@ namespace Umbraco.Tests.Services
|
||||
public void Can_RePublish_All_Content()
|
||||
{
|
||||
// Arrange
|
||||
var contentService = ServiceContext.ContentService;
|
||||
var contentService = (ContentService)ServiceContext.ContentService;
|
||||
var rootContent = contentService.GetRootContent();
|
||||
foreach (var c in rootContent)
|
||||
{
|
||||
contentService.PublishWithChildren(c);
|
||||
}
|
||||
var allContent = rootContent.Concat(rootContent.SelectMany(x => x.Descendants()));
|
||||
//for testing we need to clear out the contentXml table so we can see if it worked
|
||||
var provider = new PetaPocoUnitOfWorkProvider();
|
||||
var uow = provider.GetUnitOfWork();
|
||||
@@ -319,13 +320,49 @@ namespace Umbraco.Tests.Services
|
||||
{
|
||||
uow.Database.TruncateTable("cmsContentXml");
|
||||
}
|
||||
|
||||
//for this test we are also going to save a revision for a content item that is not published, this is to ensure
|
||||
//that it's published version still makes it into the cmsContentXml table!
|
||||
contentService.Save(allContent.Last());
|
||||
|
||||
// Act
|
||||
var published = contentService.RePublishAll(0);
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(published);
|
||||
uow = provider.GetUnitOfWork();
|
||||
using (var repo = RepositoryResolver.Current.ResolveByType<IContentRepository>(uow))
|
||||
{
|
||||
Assert.AreEqual(allContent.Count(), uow.Database.ExecuteScalar<int>("select count(*) from cmsContentXml"));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_RePublish_All_Content_Of_Type()
|
||||
{
|
||||
// Arrange
|
||||
var contentService = (ContentService)ServiceContext.ContentService;
|
||||
var rootContent = contentService.GetRootContent();
|
||||
foreach (var c in rootContent)
|
||||
{
|
||||
contentService.PublishWithChildren(c);
|
||||
}
|
||||
var allContent = rootContent.Concat(rootContent.SelectMany(x => x.Descendants()));
|
||||
//for testing we need to clear out the contentXml table so we can see if it worked
|
||||
var provider = new PetaPocoUnitOfWorkProvider();
|
||||
var uow = provider.GetUnitOfWork();
|
||||
using (RepositoryResolver.Current.ResolveByType<IContentRepository>(uow))
|
||||
{
|
||||
uow.Database.TruncateTable("cmsContentXml");
|
||||
}
|
||||
//for this test we are also going to save a revision for a content item that is not published, this is to ensure
|
||||
//that it's published version still makes it into the cmsContentXml table!
|
||||
contentService.Save(allContent.Last());
|
||||
|
||||
|
||||
// Act
|
||||
contentService.RePublishAll(new int[]{allContent.Last().ContentTypeId});
|
||||
|
||||
// Assert
|
||||
uow = provider.GetUnitOfWork();
|
||||
using (var repo = RepositoryResolver.Current.ResolveByType<IContentRepository>(uow))
|
||||
{
|
||||
|
||||
362
src/Umbraco.Tests/Services/PerformanceTests.cs
Normal file
362
src/Umbraco.Tests/Services/PerformanceTests.cs
Normal file
@@ -0,0 +1,362 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Data.SqlServerCe;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Xml.Linq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Caching;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Tests.TestHelpers.Entities;
|
||||
|
||||
namespace Umbraco.Tests.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests covering all methods in the ContentService class.
|
||||
/// This is more of an integration test as it involves multiple layers
|
||||
/// as well as configuration.
|
||||
/// </summary>
|
||||
[TestFixture, RequiresSTA]
|
||||
[NUnit.Framework.Ignore("These should not be run by the server, only directly as they are only benchmark tests")]
|
||||
public class PerformanceTests : BaseDatabaseFactoryTest
|
||||
{
|
||||
[SetUp]
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
protected override string GetDbConnectionString()
|
||||
{
|
||||
return @"server=.\SQLEXPRESS;database=UmbTest;user id=sa;password=test";
|
||||
}
|
||||
|
||||
protected override string GetDbProviderName()
|
||||
{
|
||||
return "System.Data.SqlClient";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// new schema per test
|
||||
/// </summary>
|
||||
protected override DatabaseBehavior DatabaseTestBehavior
|
||||
{
|
||||
get { return DatabaseBehavior.NewSchemaPerTest; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// don't create anything, we're testing against our own server
|
||||
/// </summary>
|
||||
protected override void CreateSqlCeDatabase()
|
||||
{
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public override void TearDown()
|
||||
{
|
||||
base.TearDown();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Get_All_Published_Content()
|
||||
{
|
||||
var result = PrimeDbWithLotsOfContent();
|
||||
var contentSvc = (ContentService) ServiceContext.ContentService;
|
||||
|
||||
var countOfPublished = result.Count(x => x.Published);
|
||||
var contentTypeId = result.First().ContentTypeId;
|
||||
|
||||
using (DisposableTimer.DebugDuration<PerformanceTests>("Getting published content normally"))
|
||||
{
|
||||
//do this 10x!
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
//clear the cache to make this test valid
|
||||
RuntimeCacheProvider.Current.Clear();
|
||||
|
||||
var published = new List<IContent>();
|
||||
//get all content items that are published
|
||||
var rootContent = contentSvc.GetRootContent();
|
||||
foreach (var content in rootContent.Where(content => content.Published))
|
||||
{
|
||||
published.Add(content);
|
||||
published.AddRange(contentSvc.GetPublishedDescendants(content));
|
||||
}
|
||||
Assert.AreEqual(countOfPublished, published.Count(x => x.ContentTypeId == contentTypeId));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
using (DisposableTimer.DebugDuration<PerformanceTests>("Getting published content optimized"))
|
||||
{
|
||||
|
||||
//do this 10x!
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
//clear the cache to make this test valid
|
||||
RuntimeCacheProvider.Current.Clear();
|
||||
|
||||
//get all content items that are published
|
||||
var published = contentSvc.GetAllPublished();
|
||||
|
||||
Assert.AreEqual(countOfPublished, published.Count(x => x.ContentTypeId == contentTypeId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Get_All_Published_Content_Of_Type()
|
||||
{
|
||||
var result = PrimeDbWithLotsOfContent();
|
||||
var contentSvc = (ContentService)ServiceContext.ContentService;
|
||||
|
||||
var countOfPublished = result.Count(x => x.Published);
|
||||
var contentTypeId = result.First().ContentTypeId;
|
||||
|
||||
using (DisposableTimer.DebugDuration<PerformanceTests>("Getting published content of type normally"))
|
||||
{
|
||||
//do this 10x!
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
//clear the cache to make this test valid
|
||||
RuntimeCacheProvider.Current.Clear();
|
||||
//get all content items that are published of this type
|
||||
var published = contentSvc.GetContentOfContentType(contentTypeId).Where(content => content.Published);
|
||||
Assert.AreEqual(countOfPublished, published.Count(x => x.ContentTypeId == contentTypeId));
|
||||
}
|
||||
}
|
||||
|
||||
using (DisposableTimer.DebugDuration<PerformanceTests>("Getting published content of type optimized"))
|
||||
{
|
||||
|
||||
//do this 10x!
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
//clear the cache to make this test valid
|
||||
RuntimeCacheProvider.Current.Clear();
|
||||
//get all content items that are published of this type
|
||||
var published = contentSvc.GetPublishedContentOfContentType(contentTypeId);
|
||||
Assert.AreEqual(countOfPublished, published.Count(x => x.ContentTypeId == contentTypeId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Truncate_Insert_Vs_Update_Insert()
|
||||
{
|
||||
var customObjectType = Guid.NewGuid();
|
||||
//chuck lots of data in the db
|
||||
var nodes = PrimeDbWithLotsOfContentXmlRecords(customObjectType);
|
||||
|
||||
//now we need to test the difference between truncating all records and re-inserting them as we do now,
|
||||
//vs updating them (which might result in checking if they exist for or listening on an exception).
|
||||
using (DisposableTimer.DebugDuration<PerformanceTests>("Starting truncate + normal insert test"))
|
||||
{
|
||||
//do this 10x!
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
//clear all the xml entries
|
||||
DatabaseContext.Database.Execute(@"DELETE FROM cmsContentXml WHERE nodeId IN
|
||||
(SELECT DISTINCT cmsContentXml.nodeId FROM cmsContentXml
|
||||
INNER JOIN cmsContent ON cmsContentXml.nodeId = cmsContent.nodeId)");
|
||||
|
||||
//now we insert each record for the ones we've deleted like we do in the content service.
|
||||
var xmlItems = nodes.Select(node => new ContentXmlDto { NodeId = node.NodeId, Xml = UpdatedXmlStructure }).ToList();
|
||||
foreach (var xml in xmlItems)
|
||||
{
|
||||
var result = DatabaseContext.Database.Insert(xml);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//now, isntead of truncating, we'll attempt to update and if it doesn't work then we insert
|
||||
using (DisposableTimer.DebugDuration<PerformanceTests>("Starting update test"))
|
||||
{
|
||||
//do this 10x!
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
//now we insert each record for the ones we've deleted like we do in the content service.
|
||||
var xmlItems = nodes.Select(node => new ContentXmlDto { NodeId = node.NodeId, Xml = UpdatedXmlStructure }).ToList();
|
||||
foreach (var xml in xmlItems)
|
||||
{
|
||||
var result = DatabaseContext.Database.Update(xml);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//now, test truncating but then do bulk insertion of records
|
||||
using (DisposableTimer.DebugDuration<PerformanceTests>("Starting truncate + bulk insert test"))
|
||||
{
|
||||
//do this 10x!
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
//clear all the xml entries
|
||||
DatabaseContext.Database.Execute(@"DELETE FROM cmsContentXml WHERE nodeId IN
|
||||
(SELECT DISTINCT cmsContentXml.nodeId FROM cmsContentXml
|
||||
INNER JOIN cmsContent ON cmsContentXml.nodeId = cmsContent.nodeId)");
|
||||
|
||||
//now we insert each record for the ones we've deleted like we do in the content service.
|
||||
var xmlItems = nodes.Select(node => new ContentXmlDto { NodeId = node.NodeId, Xml = UpdatedXmlStructure }).ToList();
|
||||
DatabaseContext.Database.BulkInsertRecords(xmlItems);
|
||||
}
|
||||
}
|
||||
|
||||
//now, test truncating but then do bulk insertion of records
|
||||
using (DisposableTimer.DebugDuration<PerformanceTests>("Starting truncate + bulk insert test in one transaction"))
|
||||
{
|
||||
//do this 10x!
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
//now we insert each record for the ones we've deleted like we do in the content service.
|
||||
var xmlItems = nodes.Select(node => new ContentXmlDto { NodeId = node.NodeId, Xml = UpdatedXmlStructure }).ToList();
|
||||
|
||||
using (var tr = DatabaseContext.Database.GetTransaction())
|
||||
{
|
||||
//clear all the xml entries
|
||||
DatabaseContext.Database.Execute(@"DELETE FROM cmsContentXml WHERE nodeId IN
|
||||
(SELECT DISTINCT cmsContentXml.nodeId FROM cmsContentXml
|
||||
INNER JOIN cmsContent ON cmsContentXml.nodeId = cmsContent.nodeId)");
|
||||
|
||||
|
||||
DatabaseContext.Database.BulkInsertRecords(xmlItems, tr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private IEnumerable<IContent> PrimeDbWithLotsOfContent()
|
||||
{
|
||||
var contentType1 = MockedContentTypes.CreateSimpleContentType();
|
||||
contentType1.AllowedAsRoot = true;
|
||||
ServiceContext.ContentTypeService.Save(contentType1);
|
||||
contentType1.AllowedContentTypes = new List<ContentTypeSort>
|
||||
{
|
||||
new ContentTypeSort
|
||||
{
|
||||
Alias = contentType1.Alias,
|
||||
Id = new Lazy<int>(() => contentType1.Id),
|
||||
SortOrder = 0
|
||||
}
|
||||
};
|
||||
var result = new List<IContent>();
|
||||
ServiceContext.ContentTypeService.Save(contentType1);
|
||||
IContent lastParent = MockedContent.CreateSimpleContent(contentType1);
|
||||
ServiceContext.ContentService.SaveAndPublish(lastParent);
|
||||
result.Add(lastParent);
|
||||
//create 20 deep
|
||||
for (var i = 0; i < 20; i++)
|
||||
{
|
||||
//for each level, create 20
|
||||
IContent content = null;
|
||||
for (var j = 1; j <= 10; j++)
|
||||
{
|
||||
content = MockedContent.CreateSimpleContent(contentType1, "Name" + j, lastParent);
|
||||
//only publish evens
|
||||
if (j % 2 == 0)
|
||||
{
|
||||
ServiceContext.ContentService.SaveAndPublish(content);
|
||||
}
|
||||
else
|
||||
{
|
||||
ServiceContext.ContentService.Save(content);
|
||||
}
|
||||
result.Add(content);
|
||||
}
|
||||
|
||||
//assign the last one as the next parent
|
||||
lastParent = content;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private IEnumerable<NodeDto> PrimeDbWithLotsOfContentXmlRecords(Guid customObjectType)
|
||||
{
|
||||
var nodes = new List<NodeDto>();
|
||||
for (var i = 1; i < 10000; i++)
|
||||
{
|
||||
nodes.Add(new NodeDto
|
||||
{
|
||||
Level = 1,
|
||||
ParentId = -1,
|
||||
NodeObjectType = customObjectType,
|
||||
Text = i.ToString(CultureInfo.InvariantCulture),
|
||||
UserId = 0,
|
||||
CreateDate = DateTime.Now,
|
||||
Trashed = false,
|
||||
SortOrder = 0,
|
||||
Path = ""
|
||||
});
|
||||
}
|
||||
DatabaseContext.Database.BulkInsertRecords(nodes);
|
||||
|
||||
//re-get the nodes with ids
|
||||
var sql = new Sql();
|
||||
sql.Select("*").From<NodeDto>().Where<NodeDto>(x => x.NodeObjectType == customObjectType);
|
||||
nodes = DatabaseContext.Database.Fetch<NodeDto>(sql);
|
||||
|
||||
//create the cmsContent data, each with a new content type id (so we can query on it later if needed)
|
||||
var contentTypeId = 0;
|
||||
var cmsContentItems = nodes.Select(node => new ContentDto { NodeId = node.NodeId, ContentTypeId = contentTypeId++ }).ToList();
|
||||
DatabaseContext.Database.BulkInsertRecords(cmsContentItems);
|
||||
|
||||
//create the xml data
|
||||
var xmlItems = nodes.Select(node => new ContentXmlDto { NodeId = node.NodeId, Xml = TestXmlStructure }).ToList();
|
||||
DatabaseContext.Database.BulkInsertRecords(xmlItems);
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
private const string TestXmlStructure = @"<Homepage id='1231' parentID='-1' level='1' creatorID='0' sortOrder='0' createDate='2013-07-05T12:05:28' updateDate='2013-07-26T11:58:52' nodeName='Home (1)' urlName='home-(1)' path='-1,1231' isDoc='' nodeType='1221' creatorName='admin' writerName='admin' writerID='0' template='2629' nodeTypeAlias='Homepage'>
|
||||
<umbracoNaviHide>0</umbracoNaviHide>
|
||||
<title>Standard Site for Umbraco by Koiak</title>
|
||||
<description><![CDATA[]]></description>
|
||||
<keywords><![CDATA[]]></keywords>
|
||||
<slideshow><![CDATA[1250,1251,1252]]></slideshow>
|
||||
<panelContent1><![CDATA[<h2>Built by Creative Founds</h2>
|
||||
<p><img src='/images/web_applications.jpg' alt='Web Applications' class='fr'/><strong>Creative Founds</strong> design and build first class software solutions that deliver big results. We provide ASP.NET web and mobile applications, Umbraco development service & technical consultancy.</p>
|
||||
<p><a href='http://www.creativefounds.co.uk' target='_blank'>www.creativefounds.co.uk</a></p>]]></panelContent1>
|
||||
<panelContent2><![CDATA[<h2>Umbraco Development</h2>
|
||||
<p><img src='/images/umbraco_square.jpg' alt='Umbraco' class='fr'/>Umbraco the the leading ASP.NET open source CMS, under pinning over 150,000 websites. Our Certified Developers are experts in developing high performance and feature rich websites.</p>]]></panelContent2>
|
||||
<panelContent3><![CDATA[<h2>Contact Us</h2>
|
||||
<p><a href='http://www.twitter.com/chriskoiak' target='_blank'><img src='/images/twitter_square.png' alt='Contact Us on Twitter' class='fr'/></a>We'd love to hear how this package has helped you and how it can be improved. Get in touch on the <a href='http://our.umbraco.org/projects/starter-kits/standard-website-mvc' target='_blank'>project website</a> or via <a href='http://www.twitter.com/chriskoiak' target='_blank'>twitter</a></p>]]></panelContent3>
|
||||
<primaryNavigation><![CDATA[1231,1232,1236,1238,1239]]></primaryNavigation>
|
||||
<address>Standard Website MVC, Company Address, Glasgow, Postcode</address>
|
||||
<copyright>Copyright &copy; 2012 Your Company</copyright>
|
||||
<affiliationLink1>http://www.umbraco.org</affiliationLink1>
|
||||
<affiliationImage1>/media/1477/umbraco_logo.png</affiliationImage1>
|
||||
<affiliationLink2></affiliationLink2>
|
||||
<affiliationImage2></affiliationImage2>
|
||||
<affiliationLink3></affiliationLink3>
|
||||
<affiliationImage3></affiliationImage3>
|
||||
<headerNavigation><![CDATA[1242,1243]]></headerNavigation>
|
||||
<myList><![CDATA[]]></myList>
|
||||
<myDate>2013-07-01T00:00:00</myDate>
|
||||
</Homepage>";
|
||||
|
||||
private const string UpdatedXmlStructure = @"<Standard id='1238' parentID='1231' level='2' creatorID='0' sortOrder='2' createDate='2013-07-05T12:05:29' updateDate='2013-07-05T12:05:34' nodeName='Clients' urlName='clients' path='-1,1231,1238' isDoc='' nodeType='1217' creatorName='admin' writerName='admin' writerID='0' template='1213' nodeTypeAlias='Standard'>
|
||||
<umbracoNaviHide>0</umbracoNaviHide>
|
||||
<title></title>
|
||||
<description><![CDATA[]]></description>
|
||||
<keywords><![CDATA[]]></keywords>
|
||||
<bodyText><![CDATA[<h2>Clients</h2>
|
||||
<p><strong>This is a standard content page.</strong></p>
|
||||
<p><span>Vestibulum malesuada aliquet ante, vitae ullamcorper felis faucibus vel. Vestibulum condimentum faucibus tellus porta ultrices. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. </span></p>
|
||||
<p><span>Cras at auctor orci. Praesent facilisis erat nec odio consequat at posuere ligula pretium. Nulla eget felis id nisl volutpat pellentesque. Ut id augue id ligula placerat rutrum a nec purus. Maecenas sed lectus ac mi pellentesque luctus quis sit amet turpis. Vestibulum adipiscing convallis vestibulum. </span></p>
|
||||
<p><span>Duis condimentum lectus at orci placerat vitae imperdiet lorem cursus. Duis hendrerit porta lorem, non suscipit quam consectetur vitae. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean elit augue, tincidunt nec tincidunt id, elementum vel est.</span></p>]]></bodyText>
|
||||
<contentPanels><![CDATA[]]></contentPanels>
|
||||
</Standard>";
|
||||
|
||||
}
|
||||
}
|
||||
@@ -56,20 +56,23 @@ namespace Umbraco.Tests.TestHelpers
|
||||
var path = TestHelper.CurrentAssemblyDirectory;
|
||||
AppDomain.CurrentDomain.SetData("DataDirectory", path);
|
||||
|
||||
var dbFactory = new DefaultDatabaseFactory(
|
||||
GetDbConnectionString(),
|
||||
GetDbProviderName());
|
||||
ApplicationContext.Current = new ApplicationContext(
|
||||
//assign the db context
|
||||
new DatabaseContext(new DefaultDatabaseFactory()),
|
||||
new DatabaseContext(dbFactory),
|
||||
//assign the service context
|
||||
new ServiceContext(new PetaPocoUnitOfWorkProvider(), new FileUnitOfWorkProvider(), new PublishingStrategy()),
|
||||
new ServiceContext(new PetaPocoUnitOfWorkProvider(dbFactory), new FileUnitOfWorkProvider(), new PublishingStrategy()),
|
||||
//disable cache
|
||||
false)
|
||||
{
|
||||
IsReady = true
|
||||
};
|
||||
|
||||
DatabaseContext.Initialize();
|
||||
DatabaseContext.Initialize(dbFactory.ProviderName, dbFactory.ConnectionString);
|
||||
|
||||
CreateDatabase();
|
||||
CreateSqlCeDatabase();
|
||||
|
||||
InitializeDatabase();
|
||||
|
||||
@@ -85,10 +88,23 @@ namespace Umbraco.Tests.TestHelpers
|
||||
get { return DatabaseBehavior.NewSchemaPerTest; }
|
||||
}
|
||||
|
||||
protected virtual string GetDbProviderName()
|
||||
{
|
||||
return "System.Data.SqlServerCe.4.0";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the db conn string
|
||||
/// </summary>
|
||||
protected virtual string GetDbConnectionString()
|
||||
{
|
||||
return @"Datasource=|DataDirectory|UmbracoPetaPocoTests.sdf";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the SqlCe database if required
|
||||
/// </summary>
|
||||
protected virtual void CreateDatabase()
|
||||
protected virtual void CreateSqlCeDatabase()
|
||||
{
|
||||
if (DatabaseTestBehavior == DatabaseBehavior.NoDatabasePerFixture)
|
||||
return;
|
||||
@@ -97,7 +113,9 @@ namespace Umbraco.Tests.TestHelpers
|
||||
|
||||
//Get the connectionstring settings from config
|
||||
var settings = ConfigurationManager.ConnectionStrings[Core.Configuration.GlobalSettings.UmbracoConnectionName];
|
||||
ConfigurationManager.AppSettings.Set(Core.Configuration.GlobalSettings.UmbracoConnectionName, @"datalayer=SQLCE4Umbraco.SqlCEHelper,SQLCE4Umbraco;data source=|DataDirectory|\UmbracoPetaPocoTests.sdf");
|
||||
ConfigurationManager.AppSettings.Set(
|
||||
Core.Configuration.GlobalSettings.UmbracoConnectionName,
|
||||
GetDbConnectionString());
|
||||
|
||||
string dbFilePath = string.Concat(path, "\\UmbracoPetaPocoTests.sdf");
|
||||
|
||||
|
||||
@@ -188,6 +188,7 @@
|
||||
<Compile Include="Persistence\PetaPocoExtensionsTest.cs" />
|
||||
<Compile Include="Persistence\Repositories\UserRepositoryTest.cs" />
|
||||
<Compile Include="Persistence\Repositories\UserTypeRepositoryTest.cs" />
|
||||
<Compile Include="Services\PerformanceTests.cs" />
|
||||
<Compile Include="Services\UserServiceTests.cs" />
|
||||
<Compile Include="TestHelpers\BaseSeleniumTest.cs" />
|
||||
<Compile Include="Integration\InstallPackage.cs" />
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
<appender-ref ref="ConsoleAppender" />
|
||||
</root>
|
||||
|
||||
<logger name="Umbraco.Core.Publishing.PublishingStrategy">
|
||||
<level value="WARN" />
|
||||
</logger>
|
||||
|
||||
<logger name="Umbraco.Core.PluginManager">
|
||||
<level value="WARN" />
|
||||
</logger>
|
||||
@@ -13,6 +17,10 @@
|
||||
<level value="WARN" />
|
||||
</logger>
|
||||
|
||||
<logger name="Umbraco.Core.Persistence.Migrations.Initial.DatabaseSchemaCreation">
|
||||
<level value="WARN" />
|
||||
</logger>
|
||||
|
||||
<logger name="Umbraco.Core.Persistence.Migrations.Initial.BaseDataCreation">
|
||||
<level value="WARN" />
|
||||
</logger>
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace umbraco.cms.presentation
|
||||
if (Request.GetItemAsString("xml") != "")
|
||||
{
|
||||
Server.ScriptTimeout = 100000;
|
||||
umbraco.cms.businesslogic.web.Document.RePublishAll();
|
||||
Services.ContentService.RePublishAll();
|
||||
}
|
||||
else if (Request.GetItemAsString("previews") != "")
|
||||
{
|
||||
|
||||
@@ -160,13 +160,6 @@ namespace umbraco.presentation.webservices
|
||||
if (parentNode != null)
|
||||
content.SortNodes(ref parentNode);
|
||||
|
||||
// Load balancing - then refresh entire cache
|
||||
// NOTE: SD: This seems a bit excessive to do simply for sorting! I'm going to leave this here for now but
|
||||
// the sort order should be updated in distributed calls when an item is Published (and it most likely is)
|
||||
// but I guess this was put here for a reason at some point.
|
||||
if (UmbracoSettings.UseDistributedCalls)
|
||||
library.RefreshContent();
|
||||
|
||||
// fire actionhandler, check for content
|
||||
BusinessLogic.Actions.Action.RunActionHandlers(new Document(parentId), ActionSort.Instance);
|
||||
}
|
||||
|
||||
@@ -428,48 +428,7 @@ namespace umbraco.cms.businesslogic.web
|
||||
[Obsolete("Obsolete, Use Umbraco.Core.Services.ContentService.RePublishAll()", false)]
|
||||
public static void RePublishAll()
|
||||
{
|
||||
var xd = new XmlDocument();
|
||||
|
||||
//Remove all Documents (not media or members), only Documents are stored in the cmsDocument table
|
||||
SqlHelper.ExecuteNonQuery(@"DELETE FROM cmsContentXml WHERE nodeId IN
|
||||
(SELECT DISTINCT cmsContentXml.nodeId FROM cmsContentXml
|
||||
INNER JOIN cmsDocument ON cmsContentXml.nodeId = cmsDocument.nodeId)");
|
||||
|
||||
var dr = SqlHelper.ExecuteReader("select nodeId from cmsDocument where published = 1");
|
||||
|
||||
while (dr.Read())
|
||||
{
|
||||
try
|
||||
{
|
||||
//create the document in optimized mode!
|
||||
// (not sure why we wouldn't always do that ?!)
|
||||
|
||||
new Document(true, dr.GetInt("nodeId"))
|
||||
.XmlGenerate(xd);
|
||||
|
||||
//The benchmark results that I found based contructing the Document object with 'true' for optimized
|
||||
//mode, vs using the normal ctor. Clearly optimized mode is better!
|
||||
/*
|
||||
* The average page rendering time (after 10 iterations) for submitting /umbraco/dialogs/republish?xml=true when using
|
||||
* optimized mode is
|
||||
*
|
||||
* 0.060400555555556
|
||||
*
|
||||
* The average page rendering time (after 10 iterations) for submitting /umbraco/dialogs/republish?xml=true when not
|
||||
* using optimized mode is
|
||||
*
|
||||
* 0.107037777777778
|
||||
*
|
||||
* This means that by simply changing this to use optimized mode, it is a 45% improvement!
|
||||
*
|
||||
*/
|
||||
}
|
||||
catch (Exception ee)
|
||||
{
|
||||
LogHelper.Error<Document>("Error generating xml", ee);
|
||||
}
|
||||
}
|
||||
dr.Close();
|
||||
ApplicationContext.Current.Services.ContentService.RePublishAll();
|
||||
}
|
||||
|
||||
public static void RegeneratePreviews()
|
||||
|
||||
Reference in New Issue
Block a user