Massively improves performance for the packaging service (converting docs to XML) which is used in republishing the tree.
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -79,3 +79,5 @@ src/Umbraco.Web.UI.Client/[Bb]uild/[Bb]elle/
|
||||
src/Umbraco.Web.UI/[Uu]ser[Cc]ontrols/
|
||||
build/_BuildOutput/
|
||||
tools/NDepend/
|
||||
src/*.vspx
|
||||
src/*.psess
|
||||
|
||||
@@ -22,10 +22,15 @@ namespace Umbraco.Core.Models
|
||||
/// <returns>Xml of the property and its value</returns>
|
||||
public static XElement ToXml(this Property property)
|
||||
{
|
||||
string nodeName = UmbracoSettings.UseLegacyXmlSchema ? "data" : property.Alias.ToSafeAlias();
|
||||
return property.ToXml(ApplicationContext.Current.Services.DataTypeService);
|
||||
}
|
||||
|
||||
internal static XElement ToXml(this Property property, IDataTypeService dataTypeService)
|
||||
{
|
||||
var nodeName = UmbracoSettings.UseLegacyXmlSchema ? "data" : property.Alias.ToSafeAlias();
|
||||
|
||||
var xd = new XmlDocument();
|
||||
XmlNode xmlNode = xd.CreateNode(XmlNodeType.Element, nodeName, "");
|
||||
var xmlNode = xd.CreateNode(XmlNodeType.Element, nodeName, "");
|
||||
|
||||
//Add the property alias to the legacy schema
|
||||
if (UmbracoSettings.UseLegacyXmlSchema)
|
||||
@@ -37,9 +42,17 @@ namespace Umbraco.Core.Models
|
||||
|
||||
//This seems to fail during testing
|
||||
//SD: With the new null checks below, this shouldn't fail anymore.
|
||||
var dt = property.PropertyType.DataType(property.Id);
|
||||
var dt = property.PropertyType.DataType(property.Id, dataTypeService);
|
||||
if (dt != null && dt.Data != null)
|
||||
{
|
||||
{
|
||||
//We've already got the value for the property so we're going to give it to the
|
||||
// data type's data property so it doesn't go re-look up the value from the db again.
|
||||
var defaultData = dt.Data as IDataValueSetter;
|
||||
if (defaultData != null)
|
||||
{
|
||||
defaultData.SetValue(property.Value, property.PropertyType.DataTypeDatabaseType.ToString());
|
||||
}
|
||||
|
||||
xmlNode.AppendChild(dt.Data.ToXMl(xd));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using umbraco.interfaces;
|
||||
using Umbraco.Core.Services;
|
||||
using umbraco.interfaces;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
@@ -9,6 +10,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
/// <param name="propertyType">PropertyType that references a DataType</param>
|
||||
/// <param name="propertyId">Id of the Property which references this DataType through its PropertyType</param>
|
||||
/// <param name="dataTypeService"></param>
|
||||
/// <returns><see cref="IDataType"/></returns>
|
||||
/// <remarks>
|
||||
/// This extension method is left internal because we don't want to take
|
||||
@@ -16,10 +18,10 @@ namespace Umbraco.Core.Models
|
||||
/// be replaced by PropertyEditors. It is however needed to generate xml
|
||||
/// for a property/propertytype when publishing.
|
||||
/// </remarks>
|
||||
internal static IDataType DataType(this PropertyType propertyType, int propertyId)
|
||||
internal static IDataType DataType(this PropertyType propertyType, int propertyId, IDataTypeService dataTypeService)
|
||||
{
|
||||
Mandate.ParameterNotNull(propertyType, "propertyType");
|
||||
var dataType = ApplicationContext.Current.Services.DataTypeService.GetDataTypeById(propertyType.DataTypeId);
|
||||
var dataType = dataTypeService.GetDataTypeById(propertyType.DataTypeId);
|
||||
if (dataType == null)
|
||||
{
|
||||
return null;
|
||||
|
||||
102
src/Umbraco.Tests/Models/DataValueSetterTests.cs
Normal file
102
src/Umbraco.Tests/Models/DataValueSetterTests.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Xml;
|
||||
using NUnit.Framework;
|
||||
using Rhino.Mocks;
|
||||
using Rhino.Mocks.Interfaces;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Strings;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using umbraco.cms.businesslogic.datatype;
|
||||
using umbraco.interfaces;
|
||||
|
||||
namespace Umbraco.Tests.Models
|
||||
{
|
||||
[TestFixture]
|
||||
public class DataValueSetterTests : BaseUmbracoApplicationTest
|
||||
{
|
||||
protected override void FreezeResolution()
|
||||
{
|
||||
ShortStringHelperResolver.Current = new ShortStringHelperResolver(new DefaultShortStringHelper());
|
||||
base.FreezeResolution();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LoadValueFromDatabase_Is_Not_Called_When_SetValue_Is_Used()
|
||||
{
|
||||
// Arrange
|
||||
var baseDataType = MockRepository.GenerateStub<BaseDataType>();
|
||||
var dataTypeData = MockRepository.GenerateMock<DefaultData>(baseDataType);
|
||||
dataTypeData.Stub(x => x.Value).CallOriginalMethod(OriginalCallOptions.NoExpectation);
|
||||
|
||||
// Act
|
||||
|
||||
((IDataValueSetter)dataTypeData).SetValue("Hello world", DataTypeDatabaseType.Nvarchar.ToString());
|
||||
var val = dataTypeData.Value;
|
||||
|
||||
// Assert
|
||||
|
||||
dataTypeData.AssertWasNotCalled(data => data.LoadValueFromDatabase());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LoadValueFromDatabase_Is_Called_When_SetValue_Is_Not_Used()
|
||||
{
|
||||
// Arrange
|
||||
var baseDataType = MockRepository.GenerateStub<BaseDataType>();
|
||||
var dataTypeData = MockRepository.GenerateMock<DefaultData>(baseDataType);
|
||||
dataTypeData
|
||||
.Stub(data => data.LoadValueFromDatabase()).WhenCalled(invocation => Debug.WriteLine("asdf"));
|
||||
dataTypeData.Stub(x => x.Value).CallOriginalMethod(OriginalCallOptions.NoExpectation);
|
||||
|
||||
// Act
|
||||
|
||||
var val = dataTypeData.Value;
|
||||
|
||||
// Assert
|
||||
|
||||
dataTypeData.AssertWasCalled(data => data.LoadValueFromDatabase());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SetValue_Is_Called_When_Executing_ToXml_On_A_Property_With_DataType_That_Implements_IDataValueSetter()
|
||||
{
|
||||
// Arrange
|
||||
var dataTypeId = Guid.NewGuid();
|
||||
|
||||
var dataTypeData = MockRepository.GenerateMock<IData, IDataValueSetter>();
|
||||
dataTypeData
|
||||
.Stub(data => data.ToXMl(Arg<XmlDocument>.Is.Anything))
|
||||
.Return(null) // you have to call Return() even though we're about to override it
|
||||
.WhenCalled(invocation =>
|
||||
{
|
||||
var xmlDoc = (XmlDocument) invocation.Arguments[0];
|
||||
invocation.ReturnValue = xmlDoc.CreateElement("test");
|
||||
});
|
||||
|
||||
var dataType = MockRepository.GenerateStub<IDataType>();
|
||||
dataType.Stub(type => type.Data).Return(dataTypeData);
|
||||
|
||||
var dataTypeSvc = MockRepository.GenerateStub<IDataTypeService>();
|
||||
dataTypeSvc.Stub(service => service.GetDataTypeById(dataTypeId)).Return(dataType);
|
||||
|
||||
var property = new Property(
|
||||
1234,
|
||||
Guid.NewGuid(),
|
||||
new PropertyType(dataTypeId, DataTypeDatabaseType.Nvarchar)
|
||||
{
|
||||
Alias = "test"
|
||||
}, "Hello world");
|
||||
|
||||
// Act
|
||||
|
||||
var xml = property.ToXml(dataTypeSvc);
|
||||
|
||||
// Assert
|
||||
|
||||
((IDataValueSetter)dataTypeData).AssertWasCalled(setter => setter.SetValue("Hello world", DataTypeDatabaseType.Nvarchar.ToString()));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
41
src/Umbraco.Tests/Services/PackagingServiceTests.cs
Normal file
41
src/Umbraco.Tests/Services/PackagingServiceTests.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Tests.TestHelpers.Entities;
|
||||
|
||||
namespace Umbraco.Tests.Services
|
||||
{
|
||||
//[TestFixture]
|
||||
//public class PackagingServiceTests : BaseServiceTest
|
||||
//{
|
||||
// [Test]
|
||||
// public void Export_Content()
|
||||
// {
|
||||
// var yesNo = DataTypesResolver.Current.GetById(new Guid(Constants.PropertyEditors.TrueFalse));
|
||||
// var txtField = DataTypesResolver.Current.GetById(new Guid(Constants.PropertyEditors.Textbox));
|
||||
|
||||
// var contentWithDataType = MockedContentTypes.CreateSimpleContentType(
|
||||
// "test",
|
||||
// "Test",
|
||||
// new PropertyTypeCollection(
|
||||
// new PropertyType[]
|
||||
// {
|
||||
// new PropertyType(new DataTypeDefinition(-1, txtField.Id)
|
||||
// {
|
||||
// Name = "Testing Textfield", DatabaseType = DataTypeDatabaseType.Ntext
|
||||
// }),
|
||||
// new PropertyType(new DataTypeDefinition(-1, yesNo.Id)
|
||||
// {
|
||||
// Name = "Testing intfield", DatabaseType = DataTypeDatabaseType.Integer
|
||||
// })
|
||||
// }));
|
||||
|
||||
// var content = MockedContent.CreateSimpleContent(contentWithDataType);
|
||||
// content.Name = "Test";
|
||||
|
||||
// var exported = ServiceContext.PackagingService.Export(content);
|
||||
|
||||
// }
|
||||
//}
|
||||
}
|
||||
@@ -46,6 +46,8 @@ namespace Umbraco.Tests.TestHelpers
|
||||
//Used to flag if its the first test in the current fixture
|
||||
private bool _isFirstTestInFixture = false;
|
||||
|
||||
private ApplicationContext _appContext;
|
||||
|
||||
[SetUp]
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -59,7 +61,7 @@ namespace Umbraco.Tests.TestHelpers
|
||||
var dbFactory = new DefaultDatabaseFactory(
|
||||
GetDbConnectionString(),
|
||||
GetDbProviderName());
|
||||
ApplicationContext.Current = new ApplicationContext(
|
||||
_appContext = new ApplicationContext(
|
||||
//assign the db context
|
||||
new DatabaseContext(dbFactory),
|
||||
//assign the service context
|
||||
@@ -79,7 +81,12 @@ namespace Umbraco.Tests.TestHelpers
|
||||
//ensure the configuration matches the current version for tests
|
||||
SettingsForTests.ConfigurationStatus = UmbracoVersion.Current.ToString(3);
|
||||
}
|
||||
|
||||
|
||||
protected override void SetupApplicationContext()
|
||||
{
|
||||
ApplicationContext.Current = _appContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The database behavior to use for the test/fixture
|
||||
/// </summary>
|
||||
|
||||
@@ -25,6 +25,8 @@ namespace Umbraco.Tests.TestHelpers
|
||||
|
||||
SetupPluginManager();
|
||||
|
||||
SetupApplicationContext();
|
||||
|
||||
FreezeResolution();
|
||||
}
|
||||
|
||||
@@ -40,7 +42,7 @@ namespace Umbraco.Tests.TestHelpers
|
||||
ApplicationContext.Current = null;
|
||||
ResetPluginManager();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// By default this returns false which means the plugin manager will not be reset so it doesn't need to re-scan
|
||||
/// all of the assemblies. Inheritors can override this if plugin manager resetting is required, generally needs
|
||||
|
||||
@@ -185,9 +185,11 @@
|
||||
<Compile Include="Configurations\FileSystemProviderTests.cs" />
|
||||
<Compile Include="CoreXml\FrameworkXmlTests.cs" />
|
||||
<Compile Include="Integration\CreateContent.cs" />
|
||||
<Compile Include="Models\DataValueSetterTests.cs" />
|
||||
<Compile Include="Persistence\PetaPocoExtensionsTest.cs" />
|
||||
<Compile Include="Persistence\Repositories\UserRepositoryTest.cs" />
|
||||
<Compile Include="Persistence\Repositories\UserTypeRepositoryTest.cs" />
|
||||
<Compile Include="Services\PackagingServiceTests.cs" />
|
||||
<Compile Include="Services\PerformanceTests.cs" />
|
||||
<Compile Include="Services\UserServiceTests.cs" />
|
||||
<Compile Include="TestHelpers\BaseSeleniumTest.cs" />
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace umbraco.cms.businesslogic.datatype
|
||||
/// <summary>
|
||||
/// Default implementation of the <c>IData</c> interface that stores data inside the Umbraco database.
|
||||
/// </summary>
|
||||
public class DefaultData : IData, IDataWithPreview
|
||||
public class DefaultData : IData, IDataWithPreview, IDataValueSetter
|
||||
{
|
||||
private int _propertyId;
|
||||
private object _value;
|
||||
@@ -57,10 +57,29 @@ namespace umbraco.cms.businesslogic.datatype
|
||||
_value = InitValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is here for performance reasons since in some cases we will have already resolved the value from the db
|
||||
/// and want to just give this object the value so it doesn't go re-look it up from the database.
|
||||
/// </summary>
|
||||
/// <param name="val"></param>
|
||||
/// <param name="strDbType"></param>
|
||||
void IDataValueSetter.SetValue(object val, string strDbType)
|
||||
{
|
||||
_value = val;
|
||||
//now that we've set our value, we can update our BaseDataType object with the correct values from the db
|
||||
//instead of making it query for itself. This is a peformance optimization enhancement.
|
||||
var dbType = BaseDataType.GetDBType(strDbType);
|
||||
var fieldName = BaseDataType.GetDataFieldName(dbType);
|
||||
_dataType.SetDataTypeProperties(fieldName, dbType);
|
||||
|
||||
//ensures that it doesn't go back to the db
|
||||
_valueLoaded = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the data value from the database.
|
||||
/// </summary>
|
||||
protected virtual void LoadValueFromDatabase()
|
||||
protected internal virtual void LoadValueFromDatabase()
|
||||
{
|
||||
var sql = new Sql();
|
||||
sql.Select("*")
|
||||
@@ -243,5 +262,7 @@ namespace umbraco.cms.businesslogic.datatype
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,15 @@ using System.Xml;
|
||||
|
||||
namespace umbraco.interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal interface used to decorate any IData that can be optimized when exporting
|
||||
/// XML like in the packaging service. Instead of relying on the IData to go get the value
|
||||
/// from the db, any IData that implements this can have it's value set from the packaging service.
|
||||
/// </summary>
|
||||
internal interface IDataValueSetter
|
||||
{
|
||||
void SetValue(object val, string strDbType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The IData is part of the IDataType interface for creating new data types in the umbraco backoffice.
|
||||
|
||||
@@ -9,4 +9,11 @@ using System.Runtime.CompilerServices;
|
||||
[assembly: AssemblyTitle("umbraco.interfaces")]
|
||||
[assembly: AssemblyDescription("Core assembly containing legacy interfaces")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyProduct("Umbraco CMS")]
|
||||
[assembly: AssemblyProduct("Umbraco CMS")]
|
||||
|
||||
[assembly: InternalsVisibleTo("cms")]
|
||||
[assembly: InternalsVisibleTo("Umbraco.Core")]
|
||||
[assembly: InternalsVisibleTo("Umbraco.Tests")]
|
||||
|
||||
//allow this to be mocked in our unit tests
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
|
||||
|
||||
@@ -67,6 +67,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuSpecs", "NuSpecs", "{227C
|
||||
..\build\NuSpecs\UmbracoCms.nuspec = ..\build\NuSpecs\UmbracoCms.nuspec
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{99794542-89EC-43BA-88BE-E31A9D61423B}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
Performance1.psess = Performance1.psess
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -157,4 +162,7 @@ Global
|
||||
{73529637-28F5-419C-A6BB-D094E39DE614} = {DD32977B-EF54-475B-9A1B-B97A502C6E58}
|
||||
{B555AAE6-0F56-442F-AC9F-EF497DB38DE7} = {DD32977B-EF54-475B-9A1B-B97A502C6E58}
|
||||
EndGlobalSection
|
||||
GlobalSection(Performance) = preSolution
|
||||
HasPerformanceSessions = true
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
Reference in New Issue
Block a user