diff --git a/.gitignore b/.gitignore
index 67bcafd9ca..9496a86e70 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/src/Umbraco.Core/Models/PropertyExtensions.cs b/src/Umbraco.Core/Models/PropertyExtensions.cs
index b03f078a9f..ae01532c87 100644
--- a/src/Umbraco.Core/Models/PropertyExtensions.cs
+++ b/src/Umbraco.Core/Models/PropertyExtensions.cs
@@ -22,10 +22,15 @@ namespace Umbraco.Core.Models
/// Xml of the property and its value
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));
}
diff --git a/src/Umbraco.Core/Models/PropertyTypeExtensions.cs b/src/Umbraco.Core/Models/PropertyTypeExtensions.cs
index b8ee04df33..304d7e7b9d 100644
--- a/src/Umbraco.Core/Models/PropertyTypeExtensions.cs
+++ b/src/Umbraco.Core/Models/PropertyTypeExtensions.cs
@@ -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
///
/// PropertyType that references a DataType
/// Id of the Property which references this DataType through its PropertyType
+ ///
///
///
/// 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.
///
- 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;
diff --git a/src/Umbraco.Tests/Models/DataValueSetterTests.cs b/src/Umbraco.Tests/Models/DataValueSetterTests.cs
new file mode 100644
index 0000000000..db59217166
--- /dev/null
+++ b/src/Umbraco.Tests/Models/DataValueSetterTests.cs
@@ -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();
+ var dataTypeData = MockRepository.GenerateMock(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();
+ var dataTypeData = MockRepository.GenerateMock(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();
+ dataTypeData
+ .Stub(data => data.ToXMl(Arg.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();
+ dataType.Stub(type => type.Data).Return(dataTypeData);
+
+ var dataTypeSvc = MockRepository.GenerateStub();
+ 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()));
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/Services/PackagingServiceTests.cs b/src/Umbraco.Tests/Services/PackagingServiceTests.cs
new file mode 100644
index 0000000000..abdfb2f9f3
--- /dev/null
+++ b/src/Umbraco.Tests/Services/PackagingServiceTests.cs
@@ -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);
+
+ // }
+ //}
+}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs
index 5a98e303a4..b1a5b8b925 100644
--- a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs
+++ b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs
@@ -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;
+ }
+
///
/// The database behavior to use for the test/fixture
///
diff --git a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs
index dbf87b4aee..d898ef6c5f 100644
--- a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs
+++ b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs
@@ -25,6 +25,8 @@ namespace Umbraco.Tests.TestHelpers
SetupPluginManager();
+ SetupApplicationContext();
+
FreezeResolution();
}
@@ -40,7 +42,7 @@ namespace Umbraco.Tests.TestHelpers
ApplicationContext.Current = null;
ResetPluginManager();
}
-
+
///
/// 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
diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj
index 66764feae5..747cb52cee 100644
--- a/src/Umbraco.Tests/Umbraco.Tests.csproj
+++ b/src/Umbraco.Tests/Umbraco.Tests.csproj
@@ -185,9 +185,11 @@
+
+
diff --git a/src/umbraco.cms/businesslogic/datatype/DefaultData.cs b/src/umbraco.cms/businesslogic/datatype/DefaultData.cs
index d72212aa5f..a974fda7c2 100644
--- a/src/umbraco.cms/businesslogic/datatype/DefaultData.cs
+++ b/src/umbraco.cms/businesslogic/datatype/DefaultData.cs
@@ -14,7 +14,7 @@ namespace umbraco.cms.businesslogic.datatype
///
/// Default implementation of the IData interface that stores data inside the Umbraco database.
///
- 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;
}
+ ///
+ /// 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.
+ ///
+ ///
+ ///
+ 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;
+ }
+
///
/// Loads the data value from the database.
///
- 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
+
+
}
}
diff --git a/src/umbraco.interfaces/IData.cs b/src/umbraco.interfaces/IData.cs
index c0580f0fee..17a374ff05 100644
--- a/src/umbraco.interfaces/IData.cs
+++ b/src/umbraco.interfaces/IData.cs
@@ -3,6 +3,15 @@ using System.Xml;
namespace umbraco.interfaces
{
+ ///
+ /// 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.
+ ///
+ internal interface IDataValueSetter
+ {
+ void SetValue(object val, string strDbType);
+ }
///
/// The IData is part of the IDataType interface for creating new data types in the umbraco backoffice.
diff --git a/src/umbraco.interfaces/Properties/AssemblyInfo.cs b/src/umbraco.interfaces/Properties/AssemblyInfo.cs
index 06f4dbdf64..709f231e00 100644
--- a/src/umbraco.interfaces/Properties/AssemblyInfo.cs
+++ b/src/umbraco.interfaces/Properties/AssemblyInfo.cs
@@ -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")]
\ No newline at end of file
+[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")]
diff --git a/src/umbraco.sln b/src/umbraco.sln
index fee2486039..eba2ad7b9b 100644
--- a/src/umbraco.sln
+++ b/src/umbraco.sln
@@ -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