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:
Shannon Deminick
2012-12-15 10:33:29 +05:00
parent ad7aa66b0b
commit b73efd16dc
6 changed files with 98 additions and 71 deletions

View File

@@ -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)
{

View File

@@ -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;
}
}
}

View File

@@ -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;
}

View File

@@ -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; }
}
}
}

View File

@@ -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();
}

View File

@@ -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);
}
}