diff --git a/src/SQLCE4Umbraco/SqlCeContextGuardian.cs b/src/SQLCE4Umbraco/SqlCeContextGuardian.cs index eb47a15bfe..8206ac6c53 100644 --- a/src/SQLCE4Umbraco/SqlCeContextGuardian.cs +++ b/src/SQLCE4Umbraco/SqlCeContextGuardian.cs @@ -11,7 +11,7 @@ namespace SQLCE4Umbraco public static class SqlCeContextGuardian { private static SqlCeConnection _constantOpenConnection; - private static object objLock = new object(); + private static readonly object Lock = new object(); // Awesome SQL CE 4 speed improvement by Erik Ejskov Jensen - SQL CE 4 MVP // It's not an issue with SQL CE 4 that we never close the connection @@ -29,7 +29,7 @@ namespace SQLCE4Umbraco connectionStringBuilder.Remove("datalayer"); // SQL CE 4 performs better when there's always a connection open in the background - ensureOpenBackgroundConnection(connectionStringBuilder.ConnectionString); + EnsureOpenBackgroundConnection(connectionStringBuilder.ConnectionString); SqlCeConnection conn = new SqlCeConnection(connectionStringBuilder.ConnectionString); conn.Open(); @@ -38,9 +38,18 @@ namespace SQLCE4Umbraco } - private static void ensureOpenBackgroundConnection(string connectionString) + /// + /// Sometimes we need to ensure this is closed especially in unit tests + /// + internal static void CloseBackgroundConnection() + { + if (_constantOpenConnection != null) + _constantOpenConnection.Close(); + } + + private static void EnsureOpenBackgroundConnection(string connectionString) { - lock (objLock) + lock (Lock) { if (_constantOpenConnection == null) { diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs index 5d1fa309b7..6f9f5fc220 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs @@ -18,9 +18,6 @@ namespace Umbraco.Core.Persistence.UnitOfWork private Guid _key; private readonly List _operations = new List(); - private readonly Transaction _transaction; - private bool _committed = false; - /// /// Creates a new unit of work instance @@ -34,7 +31,6 @@ namespace Umbraco.Core.Persistence.UnitOfWork Database = database; _key = Guid.NewGuid(); InstanceId = Guid.NewGuid(); - _transaction = Database.GetTransaction(); } /// @@ -91,34 +87,37 @@ namespace Umbraco.Core.Persistence.UnitOfWork /// /// Commits all batched changes within the scope of a PetaPoco transaction /// + /// + /// Unlike a typical unit of work, this UOW will let you commit more than once since a new transaction is creaed per + /// Commit() call instead of having one Transaction per UOW. + /// public void Commit() { - if (_committed) + + using (var transaction = Database.GetTransaction()) { - throw new InvalidOperationException("Cannot has already been called for this unit of work"); - } - - foreach (var operation in _operations.OrderBy(o => o.ProcessDate)) - { - switch (operation.Type) + foreach (var operation in _operations.OrderBy(o => o.ProcessDate)) { - case TransactionType.Insert: - operation.Repository.PersistNewItem(operation.Entity); - break; - case TransactionType.Delete: - operation.Repository.PersistDeletedItem(operation.Entity); - break; - case TransactionType.Update: - operation.Repository.PersistUpdatedItem(operation.Entity); - break; + switch (operation.Type) + { + case TransactionType.Insert: + operation.Repository.PersistNewItem(operation.Entity); + break; + case TransactionType.Delete: + operation.Repository.PersistDeletedItem(operation.Entity); + break; + case TransactionType.Update: + operation.Repository.PersistUpdatedItem(operation.Entity); + break; + } } + transaction.Complete(); } - _transaction.Complete(); + // Clear everything _operations.Clear(); _key = Guid.NewGuid(); - _committed = true; } public object Key @@ -171,9 +170,6 @@ namespace Umbraco.Core.Persistence.UnitOfWork protected override void DisposeResources() { _operations.Clear(); - _transaction.Dispose(); - //ensure the local object will be gargabe collected - Database = null; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/PublishedContentExtensions.cs b/src/Umbraco.Core/PublishedContentExtensions.cs index c039cf881c..c4ccf3af1c 100644 --- a/src/Umbraco.Core/PublishedContentExtensions.cs +++ b/src/Umbraco.Core/PublishedContentExtensions.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Data; using System.Net.Mime; using System.Web; +using Umbraco.Core.Dynamics; using Umbraco.Core.Models; using umbraco.interfaces; @@ -83,6 +84,13 @@ namespace Umbraco.Core /// public static string GetRecursiveValue(this IPublishedContent publishedContent, string fieldname) { + //check for the cached value in the objects properties first + var cachedVal = publishedContent["__recursive__" + fieldname]; + if (cachedVal != null) + { + return cachedVal.ToString(); + } + var contentValue = ""; var currentContent = publishedContent; @@ -102,6 +110,10 @@ namespace Umbraco.Core contentValue = val.ToString(); //we've found a recursive val } } + + //cache this lookup in a new custom (hidden) property + publishedContent.Properties.Add(new PropertyResult("__recursive__" + fieldname, contentValue, Guid.Empty, PropertyResultType.CustomProperty)); + return contentValue; } diff --git a/src/Umbraco.Core/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs index 4e375733de..4f5d0d696e 100644 --- a/src/Umbraco.Core/Services/ServiceContext.cs +++ b/src/Umbraco.Core/Services/ServiceContext.cs @@ -1,3 +1,4 @@ +using System; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.Publishing; @@ -11,14 +12,14 @@ namespace Umbraco.Core.Services /// public class ServiceContext { - private ContentService _contentService; - private UserService _userService; - private MediaService _mediaService; - private MacroService _macroService; - private ContentTypeService _contentTypeService; - private DataTypeService _dataTypeService; - private FileService _fileService; - private LocalizationService _localizationService; + private Lazy _contentService; + private Lazy _userService; + private Lazy _mediaService; + private Lazy _macroService; + private Lazy _contentTypeService; + private Lazy _dataTypeService; + private Lazy _fileService; + private Lazy _localizationService; /// /// Constructor @@ -28,7 +29,10 @@ namespace Umbraco.Core.Services /// internal ServiceContext(IDatabaseUnitOfWorkProvider dbUnitOfWorkProvider, IUnitOfWorkProvider fileUnitOfWorkProvider, IPublishingStrategy publishingStrategy) { - BuildServiceCache(dbUnitOfWorkProvider, fileUnitOfWorkProvider, publishingStrategy, RepositoryResolver.Current.Factory); + BuildServiceCache(dbUnitOfWorkProvider, fileUnitOfWorkProvider, publishingStrategy, + //this needs to be lazy because when we create the service context it's generally before the + //resolvers have been initialized! + new Lazy(() => RepositoryResolver.Current.Factory)); } /// @@ -38,34 +42,34 @@ namespace Umbraco.Core.Services IDatabaseUnitOfWorkProvider dbUnitOfWorkProvider, IUnitOfWorkProvider fileUnitOfWorkProvider, IPublishingStrategy publishingStrategy, - RepositoryFactory repositoryFactory) + Lazy repositoryFactory) { var provider = dbUnitOfWorkProvider; - var fileProvider = fileUnitOfWorkProvider; + var fileProvider = fileUnitOfWorkProvider; - if(_userService == null) - _userService = new UserService(provider, repositoryFactory); + if (_userService == null) + _userService = new Lazy(() => new UserService(provider, repositoryFactory.Value)); if (_contentService == null) - _contentService = new ContentService(provider, repositoryFactory, publishingStrategy, _userService); + _contentService = new Lazy(() => new ContentService(provider, repositoryFactory.Value, publishingStrategy, _userService.Value)); if(_mediaService == null) - _mediaService = new MediaService(provider, repositoryFactory); + _mediaService = new Lazy(() => new MediaService(provider, repositoryFactory.Value)); if(_macroService == null) - _macroService = new MacroService(fileProvider, repositoryFactory); + _macroService = new Lazy(() => new MacroService(fileProvider, repositoryFactory.Value)); if(_contentTypeService == null) - _contentTypeService = new ContentTypeService(provider, repositoryFactory, _contentService, _mediaService); + _contentTypeService = new Lazy(() => new ContentTypeService(provider, repositoryFactory.Value, _contentService.Value, _mediaService.Value)); if(_dataTypeService == null) - _dataTypeService = new DataTypeService(provider, repositoryFactory); + _dataTypeService = new Lazy(() => new DataTypeService(provider, repositoryFactory.Value)); if(_fileService == null) - _fileService = new FileService(fileProvider, provider, repositoryFactory); + _fileService = new Lazy(() => new FileService(fileProvider, provider, repositoryFactory.Value)); if(_localizationService == null) - _localizationService = new LocalizationService(provider, repositoryFactory); + _localizationService = new Lazy(() => new LocalizationService(provider, repositoryFactory.Value)); } /// @@ -73,7 +77,7 @@ namespace Umbraco.Core.Services /// public IContentService ContentService { - get { return _contentService; } + get { return _contentService.Value; } } /// @@ -81,7 +85,7 @@ namespace Umbraco.Core.Services /// public IContentTypeService ContentTypeService { - get { return _contentTypeService; } + get { return _contentTypeService.Value; } } /// @@ -89,7 +93,7 @@ namespace Umbraco.Core.Services /// public IDataTypeService DataTypeService { - get { return _dataTypeService; } + get { return _dataTypeService.Value; } } /// @@ -97,7 +101,7 @@ namespace Umbraco.Core.Services /// public IFileService FileService { - get { return _fileService; } + get { return _fileService.Value; } } /// @@ -105,7 +109,7 @@ namespace Umbraco.Core.Services /// public ILocalizationService LocalizationService { - get { return _localizationService; } + get { return _localizationService.Value; } } /// @@ -113,7 +117,7 @@ namespace Umbraco.Core.Services /// public IMediaService MediaService { - get { return _mediaService; } + get { return _mediaService.Value; } } /// @@ -121,7 +125,7 @@ namespace Umbraco.Core.Services /// internal IMacroService MacroService { - get { return _macroService; } + get { return _macroService.Value; } } /// @@ -129,7 +133,7 @@ namespace Umbraco.Core.Services /// internal IUserService UserService { - get { return _userService; } + get { return _userService.Value; } } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index d04cd84b26..d474752823 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using NUnit.Framework; +using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; @@ -11,6 +12,8 @@ using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; +using umbraco.editorControls.tinyMCE3; +using umbraco.interfaces; namespace Umbraco.Tests.Services { @@ -26,17 +29,17 @@ namespace Umbraco.Tests.Services public override void Initialize() { //this ensures its reset - //PluginManager.Current = new PluginManager(); + PluginManager.Current = new PluginManager(); //for testing, we'll specify which assemblies are scanned for the PluginTypeResolver - /*PluginManager.Current.AssembliesToScan = new[] + PluginManager.Current.AssembliesToScan = new[] { typeof(IDataType).Assembly, typeof(tinyMCE3dataType).Assembly }; DataTypesResolver.Current = new DataTypesResolver( - PluginManager.Current.ResolveDataTypes());*/ + PluginManager.Current.ResolveDataTypes()); base.Initialize(); @@ -47,8 +50,8 @@ namespace Umbraco.Tests.Services public override void TearDown() { //reset the app context - //DataTypesResolver.Reset(); - //PluginManager.Current = null; + DataTypesResolver.Reset(); + PluginManager.Current = null; base.TearDown(); } diff --git a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs index 4644977b5a..f21b02e14a 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs @@ -5,6 +5,7 @@ using System.IO; using System.Web.Routing; using System.Xml; using NUnit.Framework; +using SQLCE4Umbraco; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.ObjectResolution; @@ -74,23 +75,25 @@ namespace Umbraco.Tests.TestHelpers public virtual void TearDown() { DatabaseContext.Database.Dispose(); + //reset the app context + ApplicationContext.ApplicationCache.ClearAllCache(); + + //legacy API database connection close + SqlCeContextGuardian.CloseBackgroundConnection(); + + ApplicationContext.Current = null; + Resolution.IsFrozen = false; + RepositoryResolver.Reset(); TestHelper.CleanContentDirectories(); - - //reset the app context - ApplicationContext.ApplicationCache.ClearAllCache(); - ApplicationContext.Current = null; - Resolution.IsFrozen = false; - - RepositoryResolver.Reset(); - + string path = TestHelper.CurrentAssemblyDirectory; AppDomain.CurrentDomain.SetData("DataDirectory", null); string filePath = string.Concat(path, "\\UmbracoPetaPocoTests.sdf"); if (File.Exists(filePath)) { - //File.Delete(filePath); + File.Delete(filePath); } }