Fixes ServiceContext to ensure the repo's are lazy instantiated because their ctor's rely on the
RepositoryResolver.Current being initialized which normally doesn't occur until after the ServiceContext is constructed. Adds instance level caching for the GetRecursiveValue method in case this is called more than one time for a property in one view. Reverts PetaPocoUnitOfWork to allow more than one call to Commit().. this isn't 'best practices' per se but it is more for performance reasons because otherwise we'd have to create a new repo object + uow for any bulk saving operations... The end result is the same, bulk operations in the Services cannot be processed in one transaction. Fixing up the ContentServiceTests by ensuring that the shared and always open sqlce connection with the legacy SqlCeContextGuardian is closed on TearDown.
This commit is contained in:
@@ -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)
|
||||
/// <summary>
|
||||
/// Sometimes we need to ensure this is closed especially in unit tests
|
||||
/// </summary>
|
||||
internal static void CloseBackgroundConnection()
|
||||
{
|
||||
if (_constantOpenConnection != null)
|
||||
_constantOpenConnection.Close();
|
||||
}
|
||||
|
||||
private static void EnsureOpenBackgroundConnection(string connectionString)
|
||||
{
|
||||
lock (objLock)
|
||||
lock (Lock)
|
||||
{
|
||||
if (_constantOpenConnection == null)
|
||||
{
|
||||
|
||||
@@ -18,9 +18,6 @@ namespace Umbraco.Core.Persistence.UnitOfWork
|
||||
|
||||
private Guid _key;
|
||||
private readonly List<Operation> _operations = new List<Operation>();
|
||||
private readonly Transaction _transaction;
|
||||
private bool _committed = false;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -91,34 +87,37 @@ namespace Umbraco.Core.Persistence.UnitOfWork
|
||||
/// <summary>
|
||||
/// Commits all batched changes within the scope of a PetaPoco transaction <see cref="Transaction"/>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
/// </summary>
|
||||
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> _contentService;
|
||||
private Lazy<UserService> _userService;
|
||||
private Lazy<MediaService> _mediaService;
|
||||
private Lazy<MacroService> _macroService;
|
||||
private Lazy<ContentTypeService> _contentTypeService;
|
||||
private Lazy<DataTypeService> _dataTypeService;
|
||||
private Lazy<FileService> _fileService;
|
||||
private Lazy<LocalizationService> _localizationService;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
@@ -28,7 +29,10 @@ namespace Umbraco.Core.Services
|
||||
/// <param name="publishingStrategy"></param>
|
||||
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<RepositoryFactory>(() => RepositoryResolver.Current.Factory));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -38,34 +42,34 @@ namespace Umbraco.Core.Services
|
||||
IDatabaseUnitOfWorkProvider dbUnitOfWorkProvider,
|
||||
IUnitOfWorkProvider fileUnitOfWorkProvider,
|
||||
IPublishingStrategy publishingStrategy,
|
||||
RepositoryFactory repositoryFactory)
|
||||
Lazy<RepositoryFactory> repositoryFactory)
|
||||
{
|
||||
var provider = dbUnitOfWorkProvider;
|
||||
var fileProvider = fileUnitOfWorkProvider;
|
||||
var fileProvider = fileUnitOfWorkProvider;
|
||||
|
||||
if(_userService == null)
|
||||
_userService = new UserService(provider, repositoryFactory);
|
||||
if (_userService == null)
|
||||
_userService = new Lazy<UserService>(() => new UserService(provider, repositoryFactory.Value));
|
||||
|
||||
if (_contentService == null)
|
||||
_contentService = new ContentService(provider, repositoryFactory, publishingStrategy, _userService);
|
||||
_contentService = new Lazy<ContentService>(() => new ContentService(provider, repositoryFactory.Value, publishingStrategy, _userService.Value));
|
||||
|
||||
if(_mediaService == null)
|
||||
_mediaService = new MediaService(provider, repositoryFactory);
|
||||
_mediaService = new Lazy<MediaService>(() => new MediaService(provider, repositoryFactory.Value));
|
||||
|
||||
if(_macroService == null)
|
||||
_macroService = new MacroService(fileProvider, repositoryFactory);
|
||||
_macroService = new Lazy<MacroService>(() => new MacroService(fileProvider, repositoryFactory.Value));
|
||||
|
||||
if(_contentTypeService == null)
|
||||
_contentTypeService = new ContentTypeService(provider, repositoryFactory, _contentService, _mediaService);
|
||||
_contentTypeService = new Lazy<ContentTypeService>(() => new ContentTypeService(provider, repositoryFactory.Value, _contentService.Value, _mediaService.Value));
|
||||
|
||||
if(_dataTypeService == null)
|
||||
_dataTypeService = new DataTypeService(provider, repositoryFactory);
|
||||
_dataTypeService = new Lazy<DataTypeService>(() => new DataTypeService(provider, repositoryFactory.Value));
|
||||
|
||||
if(_fileService == null)
|
||||
_fileService = new FileService(fileProvider, provider, repositoryFactory);
|
||||
_fileService = new Lazy<FileService>(() => new FileService(fileProvider, provider, repositoryFactory.Value));
|
||||
|
||||
if(_localizationService == null)
|
||||
_localizationService = new LocalizationService(provider, repositoryFactory);
|
||||
_localizationService = new Lazy<LocalizationService>(() => new LocalizationService(provider, repositoryFactory.Value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -73,7 +77,7 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
public IContentService ContentService
|
||||
{
|
||||
get { return _contentService; }
|
||||
get { return _contentService.Value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -81,7 +85,7 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
public IContentTypeService ContentTypeService
|
||||
{
|
||||
get { return _contentTypeService; }
|
||||
get { return _contentTypeService.Value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -89,7 +93,7 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
public IDataTypeService DataTypeService
|
||||
{
|
||||
get { return _dataTypeService; }
|
||||
get { return _dataTypeService.Value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -97,7 +101,7 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
public IFileService FileService
|
||||
{
|
||||
get { return _fileService; }
|
||||
get { return _fileService.Value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -105,7 +109,7 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
public ILocalizationService LocalizationService
|
||||
{
|
||||
get { return _localizationService; }
|
||||
get { return _localizationService.Value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -113,7 +117,7 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
public IMediaService MediaService
|
||||
{
|
||||
get { return _mediaService; }
|
||||
get { return _mediaService.Value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -121,7 +125,7 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
internal IMacroService MacroService
|
||||
{
|
||||
get { return _macroService; }
|
||||
get { return _macroService.Value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -129,7 +133,7 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
internal IUserService UserService
|
||||
{
|
||||
get { return _userService; }
|
||||
get { return _userService.Value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user