Refactor new Services for thread safety, plus unit tests. Not all services are updated to be correct, only the

ContentService, the rest need to be updated as well.
This commit is contained in:
Shannon Deminick
2012-12-11 12:03:36 +05:00
parent aeab577627
commit feff68ada8
49 changed files with 987 additions and 490 deletions
+4 -1
View File
@@ -49,7 +49,10 @@ namespace Umbraco.Core
/// </remarks>
public Database Database
{
get { return DatabaseFactory.Current.Database; }
get
{
return DatabaseFactory.Current.Database;
}
}
/// <summary>
+28 -2
View File
@@ -1,8 +1,11 @@
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Services;
namespace Umbraco.Core
{
//Publishing Events
//Publishing Events
public class PublishingEventArgs : System.ComponentModel.CancelEventArgs { }
public class SendToPublishEventArgs : System.ComponentModel.CancelEventArgs { }
@@ -39,7 +42,30 @@ namespace Umbraco.Core
/// </summary>
public int Id { get; set; }
}
public class SaveEventArgs : System.ComponentModel.CancelEventArgs { }
public class SaveEventArgs : System.ComponentModel.CancelEventArgs
{
/// <summary>
/// public constructor
/// </summary>
public SaveEventArgs()
{
}
/// <summary>
/// internal constructor used for unit testing
/// </summary>
/// <param name="unitOfWork"></param>
internal SaveEventArgs(IUnitOfWork unitOfWork)
{
UnitOfWork = unitOfWork;
}
/// <summary>
/// Used for unit testing
/// </summary>
internal IUnitOfWork UnitOfWork { get; private set; }
}
public class NewEventArgs : System.ComponentModel.CancelEventArgs
{
/// <summary>
+3 -2
View File
@@ -1,5 +1,6 @@
using System;
using System.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Repositories;
@@ -51,7 +52,7 @@ namespace Umbraco.Core.Models
public static IProfile GetCreatorProfile(this IContent content)
{
var repository = RepositoryResolver.Current.Factory.CreateUserRepository(
new PetaPocoUnitOfWork());
new PetaPocoUnitOfWork(DatabaseContext.Current.Database));
return repository.GetProfileById(content.CreatorId);
}
@@ -61,7 +62,7 @@ namespace Umbraco.Core.Models
public static IProfile GetWriterProfile(this IContent content)
{
var repository = RepositoryResolver.Current.Factory.CreateUserRepository(
new PetaPocoUnitOfWork());
new PetaPocoUnitOfWork(DatabaseContext.Current.Database));
return repository.GetProfileById(content.WriterId);
}
}
+46 -13
View File
@@ -1,23 +1,27 @@
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Web;
using Umbraco.Core.Configuration;
namespace Umbraco.Core.Persistence
{
/// <summary>
/// Provides access to the PetaPoco database as Singleton, so the database is created once in app lifetime.
/// This is necessary for transactions to work properly.
/// Creates a database object for PetaPoco depending on the context. If we are running in an http context
/// it will create on per context, otherwise it will create a global singleton
/// </summary>
/// <remarks>
/// Because the Database is created static, the configuration has to be checked and set in
/// another class, which is where the DatabaseContext comes in.
/// </remarks>
public sealed class DatabaseFactory
internal sealed class DatabaseFactory
{
#region Singleton
private static readonly Database _database = new Database(GlobalSettings.UmbracoConnectionName);
private static readonly Lazy<DatabaseFactory> lazy = new Lazy<DatabaseFactory>(() => new DatabaseFactory());
private static readonly Lazy<DatabaseFactory> lazy = new Lazy<DatabaseFactory>(() => new DatabaseFactory());
private static volatile Database _globalInstance = null;
private static readonly object Locker = new object();
public static DatabaseFactory Current { get { return lazy.Value; } }
private DatabaseFactory()
@@ -26,12 +30,41 @@ namespace Umbraco.Core.Persistence
#endregion
/// <summary>
/// Returns an instance of the PetaPoco database
/// </summary>
public Database Database
{
get { return _database; }
}
/// <summary>
/// Returns an instance of the PetaPoco database
/// </summary>
/// <remarks>
/// If we are running in an http context
/// it will create on per context, otherwise it will create transient objects (new instance each time)
/// </remarks>
public Database Database
{
get
{
//no http context, create the singleton global object
if (HttpContext.Current == null)
{
if (_globalInstance == null)
{
lock(Locker)
{
//double check
if (_globalInstance == null)
{
_globalInstance = new Database(GlobalSettings.UmbracoConnectionName);
}
}
}
return _globalInstance;
}
//we have an http context, so only create one per request
if (!HttpContext.Current.Items.Contains(typeof (DatabaseFactory)))
{
HttpContext.Current.Items.Add(typeof (DatabaseFactory), new Database(GlobalSettings.UmbracoConnectionName));
}
return (Database) HttpContext.Current.Items[typeof (DatabaseFactory)];
}
}
}
}
@@ -20,14 +20,14 @@ namespace Umbraco.Core.Persistence.Repositories
private readonly IContentTypeRepository _contentTypeRepository;
private readonly ITemplateRepository _templateRepository;
public ContentRepository(IUnitOfWork work, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository)
public ContentRepository(IDatabaseUnitOfWork work, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository)
: base(work)
{
_contentTypeRepository = contentTypeRepository;
_templateRepository = templateRepository;
}
public ContentRepository(IUnitOfWork work, IRepositoryCacheProvider cache, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository)
public ContentRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository)
: base(work, cache)
{
_contentTypeRepository = contentTypeRepository;
@@ -22,11 +22,13 @@ namespace Umbraco.Core.Persistence.Repositories
internal abstract class ContentTypeBaseRepository<TId, TEntity> : PetaPocoRepositoryBase<TId, TEntity>
where TEntity : IContentTypeComposition
{
protected ContentTypeBaseRepository(IUnitOfWork work) : base(work)
protected ContentTypeBaseRepository(IDatabaseUnitOfWork work)
: base(work)
{
}
protected ContentTypeBaseRepository(IUnitOfWork work, IRepositoryCacheProvider cache) : base(work, cache)
protected ContentTypeBaseRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache)
: base(work, cache)
{
}
@@ -18,13 +18,13 @@ namespace Umbraco.Core.Persistence.Repositories
{
private readonly ITemplateRepository _templateRepository;
public ContentTypeRepository(IUnitOfWork work, ITemplateRepository templateRepository)
public ContentTypeRepository(IDatabaseUnitOfWork work, ITemplateRepository templateRepository)
: base(work)
{
_templateRepository = templateRepository;
}
public ContentTypeRepository(IUnitOfWork work, IRepositoryCacheProvider cache, ITemplateRepository templateRepository)
public ContentTypeRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, ITemplateRepository templateRepository)
: base(work, cache)
{
_templateRepository = templateRepository;
@@ -17,11 +17,12 @@ namespace Umbraco.Core.Persistence.Repositories
/// </summary>
internal class DataTypeDefinitionRepository : PetaPocoRepositoryBase<int, IDataTypeDefinition>, IDataTypeDefinitionRepository
{
public DataTypeDefinitionRepository(IUnitOfWork work) : base(work)
public DataTypeDefinitionRepository(IDatabaseUnitOfWork work)
: base(work)
{
}
public DataTypeDefinitionRepository(IUnitOfWork work, IRepositoryCacheProvider cache)
public DataTypeDefinitionRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache)
: base(work, cache)
{
}
@@ -19,12 +19,13 @@ namespace Umbraco.Core.Persistence.Repositories
{
private readonly ILanguageRepository _languageRepository;
public DictionaryRepository(IUnitOfWork work, ILanguageRepository languageRepository) : base(work)
public DictionaryRepository(IDatabaseUnitOfWork work, ILanguageRepository languageRepository)
: base(work)
{
_languageRepository = languageRepository;
}
public DictionaryRepository(IUnitOfWork work, IRepositoryCacheProvider cache, ILanguageRepository languageRepository)
public DictionaryRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, ILanguageRepository languageRepository)
: base(work, cache)
{
_languageRepository = languageRepository;
@@ -8,7 +8,7 @@ using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Persistence.Repositories
{
internal abstract class FileRepository<TId, TEntity> : IUnitOfWorkRepository, IRepository<TId, TEntity>
internal abstract class FileRepository<TId, TEntity> : DisposableObject, IUnitOfWorkRepository, IRepository<TId, TEntity>
where TEntity : IFile
{
private IUnitOfWork _work;
@@ -110,5 +110,16 @@ namespace Umbraco.Core.Persistence.Repositories
}
#endregion
/// <summary>
/// Dispose any disposable properties
/// </summary>
/// <remarks>
/// Dispose the unit of work
/// </remarks>
protected override void DisposeResources()
{
_work.DisposeIfDisposable();
}
}
}
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace Umbraco.Core.Persistence.Repositories
{
@@ -9,7 +10,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// Currently this interface is empty but it is useful for flagging a repository without having generic parameters, it also might
/// come in handy if we need to add anything to the base/non-generic repository interface.
/// </remarks>
public interface IRepository
public interface IRepository : IDisposable
{
}
@@ -16,11 +16,12 @@ namespace Umbraco.Core.Persistence.Repositories
/// </summary>
internal class LanguageRepository : PetaPocoRepositoryBase<int, ILanguage>, ILanguageRepository
{
public LanguageRepository(IUnitOfWork work) : base(work)
public LanguageRepository(IDatabaseUnitOfWork work)
: base(work)
{
}
public LanguageRepository(IUnitOfWork work, IRepositoryCacheProvider cache)
public LanguageRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache)
: base(work, cache)
{
}
@@ -20,12 +20,13 @@ namespace Umbraco.Core.Persistence.Repositories
private IFileSystem _fileSystem;
private SerializationService _serializationService;
public MacroRepository(IUnitOfWork work) : base(work)
public MacroRepository(IUnitOfWork work)
: base(work)
{
EnsureDependencies();
}
public MacroRepository(IUnitOfWork work, IRepositoryCacheProvider cache)
public MacroRepository(IUnitOfWork work, IRepositoryCacheProvider cache)
: base(work, cache)
{
EnsureDependencies();
@@ -19,13 +19,13 @@ namespace Umbraco.Core.Persistence.Repositories
{
private readonly IMediaTypeRepository _mediaTypeRepository;
public MediaRepository(IUnitOfWork work, IMediaTypeRepository mediaTypeRepository)
public MediaRepository(IDatabaseUnitOfWork work, IMediaTypeRepository mediaTypeRepository)
: base(work)
{
_mediaTypeRepository = mediaTypeRepository;
}
public MediaRepository(IUnitOfWork work, IRepositoryCacheProvider cache, IMediaTypeRepository mediaTypeRepository)
public MediaRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IMediaTypeRepository mediaTypeRepository)
: base(work, cache)
{
_mediaTypeRepository = mediaTypeRepository;
@@ -16,12 +16,12 @@ namespace Umbraco.Core.Persistence.Repositories
/// </summary>
internal class MediaTypeRepository : ContentTypeBaseRepository<int, IMediaType>, IMediaTypeRepository
{
public MediaTypeRepository(IUnitOfWork work)
public MediaTypeRepository(IDatabaseUnitOfWork work)
: base(work)
{
}
public MediaTypeRepository(IUnitOfWork work, IRepositoryCacheProvider cache)
public MediaTypeRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache)
: base(work, cache)
{
}
@@ -15,17 +15,27 @@ namespace Umbraco.Core.Persistence.Repositories
internal abstract class PetaPocoRepositoryBase<TId, TEntity> : RepositoryBase<TId, TEntity>
where TEntity : IAggregateRoot
{
protected PetaPocoRepositoryBase(IUnitOfWork work) : base(work)
protected PetaPocoRepositoryBase(IDatabaseUnitOfWork work)
: base(work)
{
}
protected PetaPocoRepositoryBase(IUnitOfWork work, IRepositoryCacheProvider cache) : base(work, cache)
protected PetaPocoRepositoryBase(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache)
: base(work, cache)
{
}
/// <summary>
/// Returns the database Unit of Work added to the repository
/// </summary>
protected internal new IDatabaseUnitOfWork UnitOfWork
{
get { return (IDatabaseUnitOfWork)base.UnitOfWork; }
}
protected Database Database
{
get { return DatabaseContext.Current.Database; }
get { return UnitOfWork.Database; }
}
#region Abstract Methods
@@ -18,12 +18,13 @@ namespace Umbraco.Core.Persistence.Repositories
{
private readonly IRelationTypeRepository _relationTypeRepository;
public RelationRepository(IUnitOfWork work, IRelationTypeRepository relationTypeRepository) : base(work)
public RelationRepository(IDatabaseUnitOfWork work, IRelationTypeRepository relationTypeRepository)
: base(work)
{
_relationTypeRepository = relationTypeRepository;
}
public RelationRepository(IUnitOfWork work, IRepositoryCacheProvider cache, IRelationTypeRepository relationTypeRepository)
public RelationRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IRelationTypeRepository relationTypeRepository)
: base(work, cache)
{
_relationTypeRepository = relationTypeRepository;
@@ -15,11 +15,12 @@ namespace Umbraco.Core.Persistence.Repositories
/// </summary>
internal class RelationTypeRepository : PetaPocoRepositoryBase<int, RelationType>, IRelationTypeRepository
{
public RelationTypeRepository(IUnitOfWork work) : base(work)
public RelationTypeRepository(IDatabaseUnitOfWork work)
: base(work)
{
}
public RelationTypeRepository(IUnitOfWork work, IRepositoryCacheProvider cache)
public RelationTypeRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache)
: base(work, cache)
{
}
@@ -13,18 +13,18 @@ namespace Umbraco.Core.Persistence.Repositories
/// </summary>
/// <typeparam name="TEntity">Type of <see cref="IAggregateRoot"/> entity for which the repository is used</typeparam>
/// <typeparam name="TId">Type of the Id used for this entity</typeparam>
internal abstract class RepositoryBase<TId, TEntity> : IRepositoryQueryable<TId, TEntity>,
IUnitOfWorkRepository where TEntity : IAggregateRoot
internal abstract class RepositoryBase<TId, TEntity> : DisposableObject, IRepositoryQueryable<TId, TEntity>, IUnitOfWorkRepository
where TEntity : IAggregateRoot
{
private IUnitOfWork _work;
private readonly IUnitOfWork _work;
private readonly IRepositoryCacheProvider _cache;
protected RepositoryBase(IUnitOfWork work)
protected RepositoryBase(IUnitOfWork work)
: this(work, RuntimeCacheProvider.Current)
{
}
internal RepositoryBase(IUnitOfWork work, IRepositoryCacheProvider cache)
internal RepositoryBase(IUnitOfWork work, IRepositoryCacheProvider cache)
{
_work = work;
_cache = cache;
@@ -33,7 +33,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// <summary>
/// Returns the Unit of Work added to the repository
/// </summary>
protected IUnitOfWork UnitOfWork
protected internal IUnitOfWork UnitOfWork
{
get { return _work; }
}
@@ -181,16 +181,7 @@ namespace Umbraco.Core.Persistence.Repositories
public int Count(IQuery<TEntity> query)
{
return PerformCount(query);
}
/// <summary>
/// Sets the repository's Unit Of Work with the passed in <see cref="IUnitOfWork"/>
/// </summary>
/// <param name="work"></param>
public void SetUnitOfWork(IUnitOfWork work)
{
_work = work;
}
}
#endregion
@@ -259,5 +250,16 @@ namespace Umbraco.Core.Persistence.Repositories
{
return id.EncodeAsGuid();
}
/// <summary>
/// Dispose disposable properties
/// </summary>
/// <remarks>
/// Ensure the unit of work is disposed
/// </remarks>
protected override void DisposeResources()
{
UnitOfWork.DisposeIfDisposable();
}
}
}
@@ -23,17 +23,20 @@ namespace Umbraco.Core.Persistence.Repositories
private IFileSystem _masterpagesFileSystem;
private IFileSystem _viewsFileSystem;
public TemplateRepository(IUnitOfWork work) : base(work)
public TemplateRepository(IDatabaseUnitOfWork work)
: base(work)
{
EnsureDepedencies();
}
public TemplateRepository(IUnitOfWork work, IRepositoryCacheProvider cache) : base(work, cache)
public TemplateRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache)
: base(work, cache)
{
EnsureDepedencies();
}
internal TemplateRepository(IUnitOfWork work, IRepositoryCacheProvider cache, IFileSystem masterpageFileSystem, IFileSystem viewFileSystem) : base(work, cache)
internal TemplateRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IFileSystem masterpageFileSystem, IFileSystem viewFileSystem)
: base(work, cache)
{
_masterpagesFileSystem = masterpageFileSystem;
_viewsFileSystem = viewFileSystem;
@@ -17,13 +17,13 @@ namespace Umbraco.Core.Persistence.Repositories
{
private readonly IUserTypeRepository _userTypeRepository;
public UserRepository(IUnitOfWork work, IUserTypeRepository userTypeRepository)
public UserRepository(IDatabaseUnitOfWork work, IUserTypeRepository userTypeRepository)
: base(work)
{
_userTypeRepository = userTypeRepository;
}
public UserRepository(IUnitOfWork work, IRepositoryCacheProvider cache, IUserTypeRepository userTypeRepository)
public UserRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache, IUserTypeRepository userTypeRepository)
: base(work, cache)
{
_userTypeRepository = userTypeRepository;
@@ -15,11 +15,13 @@ namespace Umbraco.Core.Persistence.Repositories
/// </summary>
internal class UserTypeRepository : PetaPocoRepositoryBase<int, IUserType>, IUserTypeRepository
{
public UserTypeRepository(IUnitOfWork work) : base(work)
public UserTypeRepository(IDatabaseUnitOfWork work)
: base(work)
{
}
public UserTypeRepository(IUnitOfWork work, IRepositoryCacheProvider cache) : base(work, cache)
public UserTypeRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache)
: base(work, cache)
{
}
@@ -9,14 +9,14 @@ namespace Umbraco.Core.Persistence
/// </summary>
public class RepositoryFactory
{
internal virtual IUserTypeRepository CreateUserTypeRepository(IUnitOfWork uow)
internal virtual IUserTypeRepository CreateUserTypeRepository(IDatabaseUnitOfWork uow)
{
return new UserTypeRepository(
uow,
NullCacheProvider.Current);
}
internal virtual IUserRepository CreateUserRepository(IUnitOfWork uow)
internal virtual IUserRepository CreateUserRepository(IDatabaseUnitOfWork uow)
{
return new UserRepository(
uow,
@@ -24,7 +24,7 @@ namespace Umbraco.Core.Persistence
CreateUserTypeRepository(uow));
}
public virtual IContentRepository CreateContentRepository(IUnitOfWork uow)
public virtual IContentRepository CreateContentRepository(IDatabaseUnitOfWork uow)
{
return new ContentRepository(
uow,
@@ -33,7 +33,7 @@ namespace Umbraco.Core.Persistence
CreateTemplateRepository(uow));
}
public virtual IContentTypeRepository CreateContentTypeRepository(IUnitOfWork uow)
public virtual IContentTypeRepository CreateContentTypeRepository(IDatabaseUnitOfWork uow)
{
return new ContentTypeRepository(
uow,
@@ -41,14 +41,14 @@ namespace Umbraco.Core.Persistence
new TemplateRepository(uow, NullCacheProvider.Current));
}
public virtual IDataTypeDefinitionRepository CreateDataTypeDefinitionRepository(IUnitOfWork uow)
public virtual IDataTypeDefinitionRepository CreateDataTypeDefinitionRepository(IDatabaseUnitOfWork uow)
{
return new DataTypeDefinitionRepository(
uow,
NullCacheProvider.Current);
}
public virtual IDictionaryRepository CreateDictionaryRepository(IUnitOfWork uow)
public virtual IDictionaryRepository CreateDictionaryRepository(IDatabaseUnitOfWork uow)
{
return new DictionaryRepository(
uow,
@@ -56,21 +56,21 @@ namespace Umbraco.Core.Persistence
CreateLanguageRepository(uow));
}
public virtual ILanguageRepository CreateLanguageRepository(IUnitOfWork uow)
public virtual ILanguageRepository CreateLanguageRepository(IDatabaseUnitOfWork uow)
{
return new LanguageRepository(
uow,
InMemoryCacheProvider.Current);
}
internal virtual IMacroRepository CreateMacroRepository(IUnitOfWork uow)
internal virtual IMacroRepository CreateMacroRepository(IUnitOfWork uow)
{
return new MacroRepository(
uow,
InMemoryCacheProvider.Current);
}
public virtual IMediaRepository CreateMediaRepository(IUnitOfWork uow)
public virtual IMediaRepository CreateMediaRepository(IDatabaseUnitOfWork uow)
{
return new MediaRepository(
uow,
@@ -78,14 +78,14 @@ namespace Umbraco.Core.Persistence
CreateMediaTypeRepository(uow));
}
public virtual IMediaTypeRepository CreateMediaTypeRepository(IUnitOfWork uow)
public virtual IMediaTypeRepository CreateMediaTypeRepository(IDatabaseUnitOfWork uow)
{
return new MediaTypeRepository(
uow,
InMemoryCacheProvider.Current);
}
public virtual IRelationRepository CreateRelationRepository(IUnitOfWork uow)
public virtual IRelationRepository CreateRelationRepository(IDatabaseUnitOfWork uow)
{
return new RelationRepository(
uow,
@@ -93,7 +93,7 @@ namespace Umbraco.Core.Persistence
CreateRelationTypeRepository(uow));
}
public virtual IRelationTypeRepository CreateRelationTypeRepository(IUnitOfWork uow)
public virtual IRelationTypeRepository CreateRelationTypeRepository(IDatabaseUnitOfWork uow)
{
return new RelationTypeRepository(
uow,
@@ -110,7 +110,7 @@ namespace Umbraco.Core.Persistence
return new StylesheetRepository(uow);
}
public virtual ITemplateRepository CreateTemplateRepository(IUnitOfWork uow)
public virtual ITemplateRepository CreateTemplateRepository(IDatabaseUnitOfWork uow)
{
return new TemplateRepository(uow, NullCacheProvider.Current);
}
@@ -140,5 +140,6 @@ namespace Umbraco.Core.Persistence.UnitOfWork
}
#endregion
}
}
@@ -0,0 +1,12 @@
using System;
namespace Umbraco.Core.Persistence.UnitOfWork
{
/// <summary>
/// Defines a unit of work when working with a database object
/// </summary>
public interface IDatabaseUnitOfWork : IUnitOfWork, IDisposable
{
Database Database { get; }
}
}
@@ -0,0 +1,10 @@
namespace Umbraco.Core.Persistence.UnitOfWork
{
/// <summary>
/// Defines a Unit of Work Provider for working with an IDatabaseUnitOfWork
/// </summary>
public interface IDatabaseUnitOfWorkProvider
{
IDatabaseUnitOfWork GetUnitOfWork();
}
}
@@ -1,12 +1,13 @@
using Umbraco.Core.Models.EntityBase;
using System;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Persistence.UnitOfWork
{
/// <summary>
/// <summary>
/// Defines a Unit Of Work
/// </summary>
public interface IUnitOfWork
{
{
void RegisterAdded(IEntity entity, IUnitOfWorkRepository repository);
void RegisterChanged(IEntity entity, IUnitOfWorkRepository repository);
void RegisterRemoved(IEntity entity, IUnitOfWorkRepository repository);
@@ -1,6 +1,6 @@
namespace Umbraco.Core.Persistence.UnitOfWork
{
/// <summary>
/// <summary>
/// Defines a Unit of Work Provider
/// </summary>
public interface IUnitOfWorkProvider
@@ -8,17 +8,26 @@ namespace Umbraco.Core.Persistence.UnitOfWork
/// <summary>
/// Represents the Unit of Work implementation for PetaPoco
/// </summary>
internal class PetaPocoUnitOfWork : IUnitOfWork
internal class PetaPocoUnitOfWork : DisposableObject, IDatabaseUnitOfWork
{
/// <summary>
/// Used for testing
/// </summary>
internal Guid InstanceId { get; private set; }
private Guid _key;
private readonly List<Operation> _operations = new List<Operation>();
public PetaPocoUnitOfWork()
{
_key = Guid.NewGuid();
}
/// <summary>
public PetaPocoUnitOfWork(Database database)
{
Database = database;
_key = Guid.NewGuid();
InstanceId = Guid.NewGuid();
}
/// <summary>
/// Registers an <see cref="IEntity" /> instance to be added through this <see cref="UnitOfWork" />
/// </summary>
/// <param name="entity">The <see cref="IEntity" /></param>
@@ -74,7 +83,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork
/// </summary>
public void Commit()
{
using(Transaction transaction = DatabaseFactory.Current.Database.GetTransaction())
using(Transaction transaction = Database.GetTransaction())
{
foreach (var operation in _operations.OrderBy(o => o.ProcessDate))
{
@@ -104,6 +113,8 @@ namespace Umbraco.Core.Persistence.UnitOfWork
get { return _key; }
}
public Database Database { get; private set; }
#region Operation
/// <summary>
@@ -137,5 +148,19 @@ namespace Umbraco.Core.Persistence.UnitOfWork
}
#endregion
/// <summary>
/// Ensures disposable objects are disposed
/// </summary>
/// <remarks>
/// We will not dispose the database because this will get disposed of automatically when
/// in the HttpContext by the UmbracoModule because the DatabaseFactory stores the instance in HttpContext.Items
/// when in a web context.
/// When not in a web context, we may possibly be re-using the database context.
/// </remarks>
protected override void DisposeResources()
{
_operations.Clear();
}
}
}
@@ -1,15 +1,21 @@
namespace Umbraco.Core.Persistence.UnitOfWork
using System;
using System.Threading;
using System.Web;
using Umbraco.Core.Configuration;
namespace Umbraco.Core.Persistence.UnitOfWork
{
/// <summary>
/// Represents a Unit of Work Provider for creating a <see cref="PetaPocoUnitOfWork"/>
/// </summary>
internal class PetaPocoUnitOfWorkProvider : IUnitOfWorkProvider
internal class PetaPocoUnitOfWorkProvider : IDatabaseUnitOfWorkProvider
{
#region Implementation of IUnitOfWorkProvider
#region Implementation of IUnitOfWorkProvider
public IUnitOfWork GetUnitOfWork()
public IDatabaseUnitOfWork GetUnitOfWork()
{
return new PetaPocoUnitOfWork();
return new PetaPocoUnitOfWork(DatabaseFactory.Current.Database);
}
#endregion
+389 -340
View File
@@ -18,28 +18,22 @@ namespace Umbraco.Core.Services
/// </summary>
public class ContentService : IContentService
{
private readonly IPublishingStrategy _publishingStrategy;
private readonly IUserService _userService;
private readonly IUnitOfWork _unitOfWork;
private readonly IDatabaseUnitOfWorkProvider _uowProvider;
private readonly IPublishingStrategy _publishingStrategy;
private readonly IUserService _userService;
private HttpContextBase _httpContext;
private readonly IContentRepository _contentRepository;
private readonly IContentTypeRepository _contentTypeRepository;
public ContentService(IUnitOfWorkProvider provider, IPublishingStrategy publishingStrategy)
public ContentService(IDatabaseUnitOfWorkProvider provider, IPublishingStrategy publishingStrategy)
{
_publishingStrategy = publishingStrategy;
_unitOfWork = provider.GetUnitOfWork();
_contentRepository = RepositoryResolver.Current.Factory.CreateContentRepository(_unitOfWork);
_contentTypeRepository = RepositoryResolver.Current.Factory.CreateContentTypeRepository(_unitOfWork);
_uowProvider = provider;
_publishingStrategy = publishingStrategy;
}
internal ContentService(IUnitOfWorkProvider provider, IPublishingStrategy publishingStrategy, IUserService userService)
internal ContentService(IDatabaseUnitOfWorkProvider provider, IPublishingStrategy publishingStrategy, IUserService userService)
{
_publishingStrategy = publishingStrategy;
_userService = userService;
_unitOfWork = provider.GetUnitOfWork();
_contentRepository = RepositoryResolver.Current.Factory.CreateContentRepository(_unitOfWork);
_contentTypeRepository = RepositoryResolver.Current.Factory.CreateContentTypeRepository(_unitOfWork);
_uowProvider = provider;
}
//TODO Add GetLatestUnpublishedVersions(int id){}
@@ -54,7 +48,8 @@ namespace Umbraco.Core.Services
/// <returns><see cref="IContent"/></returns>
public IContent CreateContent(int parentId, string contentTypeAlias, int userId = -1)
{
var repository = _contentTypeRepository;
var uow = _uowProvider.GetUnitOfWork();
var repository = RepositoryResolver.Current.Factory.CreateContentTypeRepository(uow);
var query = Query<IContentType>.Builder.Where(x => x.Alias == contentTypeAlias);
var contentTypes = repository.GetByQuery(query);
@@ -92,8 +87,11 @@ namespace Umbraco.Core.Services
/// <returns><see cref="IContent"/></returns>
public IContent GetById(int id)
{
var repository = _contentRepository;
return repository.Get(id);
using(var repository = RepositoryResolver.Current.Factory.CreateContentRepository(_uowProvider.GetUnitOfWork()))
{
return repository.Get(id);
}
}
/// <summary>
@@ -103,10 +101,12 @@ namespace Umbraco.Core.Services
/// <returns><see cref="IContent"/></returns>
public IContent GetById(Guid key)
{
var repository = _contentRepository;
var query = Query<IContent>.Builder.Where(x => x.Key == key);
var contents = repository.GetByQuery(query);
return contents.SingleOrDefault();
using(var repository = RepositoryResolver.Current.Factory.CreateContentRepository(_uowProvider.GetUnitOfWork()))
{
var query = Query<IContent>.Builder.Where(x => x.Key == key);
var contents = repository.GetByQuery(query);
return contents.SingleOrDefault();
}
}
@@ -117,12 +117,13 @@ namespace Umbraco.Core.Services
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable<IContent> GetContentOfContentType(int id)
{
var repository = _contentRepository;
using (var repository = RepositoryResolver.Current.Factory.CreateContentRepository(_uowProvider.GetUnitOfWork()))
{
var query = Query<IContent>.Builder.Where(x => x.ContentTypeId == id);
var contents = repository.GetByQuery(query);
var query = Query<IContent>.Builder.Where(x => x.ContentTypeId == id);
var contents = repository.GetByQuery(query);
return contents;
return contents;
}
}
/// <summary>
@@ -132,12 +133,13 @@ namespace Umbraco.Core.Services
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable<IContent> GetByLevel(int level)
{
var repository = _contentRepository;
using (var repository = RepositoryResolver.Current.Factory.CreateContentRepository(_uowProvider.GetUnitOfWork()))
{
var query = Query<IContent>.Builder.Where(x => x.Level == level);
var contents = repository.GetByQuery(query);
var query = Query<IContent>.Builder.Where(x => x.Level == level);
var contents = repository.GetByQuery(query);
return contents;
return contents;
}
}
/// <summary>
@@ -147,12 +149,13 @@ namespace Umbraco.Core.Services
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable<IContent> GetChildren(int id)
{
var repository = _contentRepository;
using(var repository = RepositoryResolver.Current.Factory.CreateContentRepository(_uowProvider.GetUnitOfWork()))
{
var query = Query<IContent>.Builder.Where(x => x.ParentId == id);
var contents = repository.GetByQuery(query);
var query = Query<IContent>.Builder.Where(x => x.ParentId == id);
var contents = repository.GetByQuery(query);
return contents;
return contents;
}
}
/// <summary>
@@ -162,9 +165,12 @@ namespace Umbraco.Core.Services
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable<IContent> GetVersions(int id)
{
var repository = _contentRepository;
var versions = repository.GetAllVersions(id);
return versions;
using (var repository = RepositoryResolver.Current.Factory.CreateContentRepository(_uowProvider.GetUnitOfWork()))
{
var versions = repository.GetAllVersions(id);
return versions;
}
}
/// <summary>
@@ -173,12 +179,13 @@ namespace Umbraco.Core.Services
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable<IContent> GetRootContent()
{
var repository = _contentRepository;
using (var repository = RepositoryResolver.Current.Factory.CreateContentRepository(_uowProvider.GetUnitOfWork()))
{
var query = Query<IContent>.Builder.Where(x => x.ParentId == -1);
var contents = repository.GetByQuery(query);
var query = Query<IContent>.Builder.Where(x => x.ParentId == -1);
var contents = repository.GetByQuery(query);
return contents;
return contents;
}
}
/// <summary>
@@ -187,12 +194,13 @@ namespace Umbraco.Core.Services
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable<IContent> GetContentForExpiration()
{
var repository = _contentRepository;
using (var repository = RepositoryResolver.Current.Factory.CreateContentRepository(_uowProvider.GetUnitOfWork()))
{
var query = Query<IContent>.Builder.Where(x => x.Published == true && x.ExpireDate <= DateTime.UtcNow);
var contents = repository.GetByQuery(query);
var query = Query<IContent>.Builder.Where(x => x.Published == true && x.ExpireDate <= DateTime.UtcNow);
var contents = repository.GetByQuery(query);
return contents;
return contents;
}
}
/// <summary>
@@ -201,12 +209,13 @@ namespace Umbraco.Core.Services
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable<IContent> GetContentForRelease()
{
var repository = _contentRepository;
using (var repository = RepositoryResolver.Current.Factory.CreateContentRepository(_uowProvider.GetUnitOfWork()))
{
var query = Query<IContent>.Builder.Where(x => x.Published == false && x.ReleaseDate <= DateTime.UtcNow);
var contents = repository.GetByQuery(query);
var query = Query<IContent>.Builder.Where(x => x.Published == false && x.ReleaseDate <= DateTime.UtcNow);
var contents = repository.GetByQuery(query);
return contents;
return contents;
}
}
/// <summary>
@@ -215,12 +224,13 @@ namespace Umbraco.Core.Services
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable<IContent> GetContentInRecycleBin()
{
var repository = _contentRepository;
using (var repository = RepositoryResolver.Current.Factory.CreateContentRepository(_uowProvider.GetUnitOfWork()))
{
var query = Query<IContent>.Builder.Where(x => x.ParentId == -20);
var contents = repository.GetByQuery(query);
var query = Query<IContent>.Builder.Where(x => x.ParentId == -20);
var contents = repository.GetByQuery(query);
return contents;
return contents;
}
}
/// <summary>
@@ -230,41 +240,43 @@ namespace Umbraco.Core.Services
/// <returns>True if publishing succeeded, otherwise False</returns>
public bool RePublishAll(int userId = -1)
{
var repository = _contentRepository;
var list = new List<IContent>();
//Consider creating a Path query instead of recursive method:
//var query = Query<IContent>.Builder.Where(x => x.Path.StartsWith("-1"));
var rootContent = GetRootContent();
foreach (var content in rootContent)
var uow = _uowProvider.GetUnitOfWork();
using (var repository = RepositoryResolver.Current.Factory.CreateContentRepository(uow))
{
if(content.IsValid())
{
list.Add(content);
list.AddRange(GetChildrenDeep(content.Id));
}
var list = new List<IContent>();
//Consider creating a Path query instead of recursive method:
//var query = Query<IContent>.Builder.Where(x => x.Path.StartsWith("-1"));
var rootContent = GetRootContent();
foreach (var content in rootContent)
{
if (content.IsValid())
{
list.Add(content);
list.AddRange(GetChildrenDeep(content.Id));
}
}
//Publish and then update the database with new status
var published = _publishingStrategy.PublishWithChildren(list, userId);
if (published)
{
//Only loop through content where the Published property has been updated
foreach (var item in list.Where(x => ((ICanBeDirty)x).IsPropertyDirty("Published")))
{
SetWriter(item, userId);
repository.AddOrUpdate(item);
}
uow.Commit();
//TODO Change this so we can avoid a depencency to the horrible library method / umbraco.content (singleton) class.
//global::umbraco.library.RefreshContent();
}
return published;
}
//Publish and then update the database with new status
var published = _publishingStrategy.PublishWithChildren(list, userId);
if (published)
{
//Only loop through content where the Published property has been updated
foreach (var item in list.Where(x => ((ICanBeDirty)x).IsPropertyDirty("Published")))
{
SetWriter(item, userId);
repository.AddOrUpdate(item);
}
_unitOfWork.Commit();
//TODO Change this so we can avoid a depencency to the horrible library method / umbraco.content (singleton) class.
//global::umbraco.library.RefreshContent();
}
return published;
}
/// <summary>
@@ -286,52 +298,54 @@ namespace Umbraco.Core.Services
/// <returns>True if publishing succeeded, otherwise False</returns>
public bool PublishWithChildren(IContent content, int userId = -1)
{
var repository = _contentRepository;
//Check if parent is published (although not if its a root node) - if parent isn't published this Content cannot be published
if (content.ParentId != -1 && content.ParentId != -20 && !GetById(content.ParentId).Published)
var uow = _uowProvider.GetUnitOfWork();
using (var repository = RepositoryResolver.Current.Factory.CreateContentRepository(uow))
{
LogHelper.Info<ContentService>(
string.Format("Content '{0}' with Id '{1}' could not be published because its parent is not published.",
content.Name, content.Id));
return false;
}
//Check if parent is published (although not if its a root node) - if parent isn't published this Content cannot be published
if (content.ParentId != -1 && content.ParentId != -20 && !GetById(content.ParentId).Published)
{
LogHelper.Info<ContentService>(
string.Format("Content '{0}' with Id '{1}' could not be published because its parent is not published.",
content.Name, content.Id));
return false;
}
//Content contains invalid property values and can therefore not be published - fire event?
if (!content.IsValid())
{
LogHelper.Info<ContentService>(
string.Format("Content '{0}' with Id '{1}' could not be published because of invalid properties.",
content.Name, content.Id));
return false;
}
//Content contains invalid property values and can therefore not be published - fire event?
if (!content.IsValid())
{
LogHelper.Info<ContentService>(
string.Format("Content '{0}' with Id '{1}' could not be published because of invalid properties.",
content.Name, content.Id));
return false;
}
//Consider creating a Path query instead of recursive method:
//var query = Query<IContent>.Builder.Where(x => x.Path.StartsWith(content.Path));
//Consider creating a Path query instead of recursive method:
//var query = Query<IContent>.Builder.Where(x => x.Path.StartsWith(content.Path));
var list = new List<IContent>();
list.Add(content);
list.AddRange(GetChildrenDeep(content.Id));
var list = new List<IContent>();
list.Add(content);
list.AddRange(GetChildrenDeep(content.Id));
//Publish and then update the database with new status
var published = _publishingStrategy.PublishWithChildren(list, userId);
if (published)
{
//Only loop through content where the Published property has been updated
foreach (var item in list.Where(x => ((ICanBeDirty)x).IsPropertyDirty("Published")))
{
SetWriter(item, userId);
repository.AddOrUpdate(item);
}
//Publish and then update the database with new status
var published = _publishingStrategy.PublishWithChildren(list, userId);
if (published)
{
//Only loop through content where the Published property has been updated
foreach (var item in list.Where(x => ((ICanBeDirty)x).IsPropertyDirty("Published")))
{
SetWriter(item, userId);
repository.AddOrUpdate(item);
}
_unitOfWork.Commit();
uow.Commit();
//TODO Change this so we can avoid a depencency to the horrible library method / umbraco.content (singleton) class.
//TODO Need to investigate if it will also update the cache for children of the Content object
//global::umbraco.library.UpdateDocumentCache(content.Id);
}
//TODO Change this so we can avoid a depencency to the horrible library method / umbraco.content (singleton) class.
//TODO Need to investigate if it will also update the cache for children of the Content object
//global::umbraco.library.UpdateDocumentCache(content.Id);
}
return published;
return published;
}
}
/// <summary>
@@ -342,39 +356,41 @@ namespace Umbraco.Core.Services
/// <returns>True if unpublishing succeeded, otherwise False</returns>
public bool UnPublish(IContent content, int userId = -1)
{
var repository = _contentRepository;
//Look for children and unpublish them if any exists, otherwise just unpublish the passed in Content.
var children = GetChildrenDeep(content.Id);
var hasChildren = children.Any();
if(hasChildren)
children.Add(content);
var unpublished = hasChildren
? _publishingStrategy.UnPublish(children, userId)
: _publishingStrategy.UnPublish(content, userId);
if (unpublished)
var uow = _uowProvider.GetUnitOfWork();
using(var repository = RepositoryResolver.Current.Factory.CreateContentRepository(uow))
{
repository.AddOrUpdate(content);
//Look for children and unpublish them if any exists, otherwise just unpublish the passed in Content.
var children = GetChildrenDeep(content.Id);
var hasChildren = children.Any();
if (hasChildren)
{
foreach (var child in children)
{
SetWriter(child, userId);
repository.AddOrUpdate(child);
}
}
if (hasChildren)
children.Add(content);
_unitOfWork.Commit();
var unpublished = hasChildren
? _publishingStrategy.UnPublish(children, userId)
: _publishingStrategy.UnPublish(content, userId);
//TODO Change this so we can avoid a depencency to the horrible library method / umbraco.content class.
//global::umbraco.library.UnPublishSingleNode(content.Id);
if (unpublished)
{
repository.AddOrUpdate(content);
if (hasChildren)
{
foreach (var child in children)
{
SetWriter(child, userId);
repository.AddOrUpdate(child);
}
}
uow.Commit();
//TODO Change this so we can avoid a depencency to the horrible library method / umbraco.content class.
//global::umbraco.library.UnPublishSingleNode(content.Id);
}
return unpublished;
}
return unpublished;
}
/// <summary>
@@ -410,54 +426,58 @@ namespace Umbraco.Core.Services
/// <param name="userId">Optional Id of the User issueing the publishing</param>
/// <returns>True if publishing succeeded, otherwise False</returns>
public bool SaveAndPublish(IContent content, int userId = -1)
{
var e = new SaveEventArgs();
if (Saving != null)
Saving(content, e);
{
var uow = _uowProvider.GetUnitOfWork();
using (var repository = RepositoryResolver.Current.Factory.CreateContentRepository(uow))
{
var e = new SaveEventArgs(uow);
if (Saving != null)
Saving(content, e);
if (!e.Cancel)
{
var repository = _contentRepository;
if (!e.Cancel)
{
//Check if parent is published (although not if its a root node) - if parent isn't published this Content cannot be published
if (content.ParentId != -1 && content.ParentId != -20 && GetById(content.ParentId).Published == false)
{
LogHelper.Info<ContentService>(
string.Format(
"Content '{0}' with Id '{1}' could not be published because its parent is not published.",
content.Name, content.Id));
return false;
}
//Check if parent is published (although not if its a root node) - if parent isn't published this Content cannot be published
if (content.ParentId != -1 && content.ParentId != -20 && GetById(content.ParentId).Published == false)
{
LogHelper.Info<ContentService>(
string.Format(
"Content '{0}' with Id '{1}' could not be published because its parent is not published.",
content.Name, content.Id));
return false;
}
//Content contains invalid property values and can therefore not be published - fire event?
if (!content.IsValid())
{
LogHelper.Info<ContentService>(
string.Format(
"Content '{0}' with Id '{1}' could not be published because of invalid properties.",
content.Name, content.Id));
return false;
}
//Content contains invalid property values and can therefore not be published - fire event?
if (!content.IsValid())
{
LogHelper.Info<ContentService>(
string.Format(
"Content '{0}' with Id '{1}' could not be published because of invalid properties.",
content.Name, content.Id));
return false;
}
//Publish and then update the database with new status
bool published = _publishingStrategy.Publish(content, userId);
if (published)
{
SetWriter(content, userId);
repository.AddOrUpdate(content);
_unitOfWork.Commit();
//Publish and then update the database with new status
bool published = _publishingStrategy.Publish(content, userId);
if (published)
{
SetWriter(content, userId);
repository.AddOrUpdate(content);
uow.Commit();
//TODO Change this so we can avoid a depencency to the horrible library method / umbraco.content (singleton) class.
//global::umbraco.library.UpdateDocumentCache(content.Id);
}
//TODO Change this so we can avoid a depencency to the horrible library method / umbraco.content (singleton) class.
//global::umbraco.library.UpdateDocumentCache(content.Id);
}
if (Saved != null)
Saved(content, e);
if (Saved != null)
Saved(content, e);
return published;
}
return published;
}
return false;
return false;
}
}
/// <summary>
@@ -467,22 +487,25 @@ namespace Umbraco.Core.Services
/// <param name="userId">Optional Id of the User saving the Content</param>
public void Save(IContent content, int userId = -1)
{
var e = new SaveEventArgs();
if (Saving != null)
Saving(content, e);
var uow = _uowProvider.GetUnitOfWork();
using (var repository = RepositoryResolver.Current.Factory.CreateContentRepository(uow))
{
var e = new SaveEventArgs(uow);
if (Saving != null)
Saving(content, e);
if (!e.Cancel)
{
var repository = _contentRepository;
if (!e.Cancel)
{
SetWriter(content, userId);
content.ChangePublishedState(false);
repository.AddOrUpdate(content);
_unitOfWork.Commit();
SetWriter(content, userId);
content.ChangePublishedState(false);
repository.AddOrUpdate(content);
uow.Commit();
if (Saved != null)
Saved(content, e);
}
if (Saved != null)
Saved(content, e);
}
}
}
/// <summary>
@@ -496,41 +519,44 @@ namespace Umbraco.Core.Services
/// <param name="userId">Optional Id of the User saving the Content</param>
public void Save(IEnumerable<IContent> contents, int userId = -1)
{
var repository = _contentRepository;
var containsNew = contents.Any(x => x.HasIdentity == false);
var e = new SaveEventArgs();
if (Saving != null)
Saving(contents, e);
if (!e.Cancel)
var uow = _uowProvider.GetUnitOfWork();
using (var repository = RepositoryResolver.Current.Factory.CreateContentRepository(uow))
{
if (containsNew)
{
foreach (var content in contents)
{
SetWriter(content, userId);
content.ChangePublishedState(false);
repository.AddOrUpdate(content);
_unitOfWork.Commit();
}
}
else
{
foreach (var content in contents)
{
if (Saving != null)
Saving(content, e);
var containsNew = contents.Any(x => x.HasIdentity == false);
SetWriter(content, userId);
repository.AddOrUpdate(content);
}
_unitOfWork.Commit();
}
var e = new SaveEventArgs(uow);
if (Saving != null)
Saving(contents, e);
if (Saved != null)
Saved(contents, e);
}
if (!e.Cancel)
{
if (containsNew)
{
foreach (var content in contents)
{
SetWriter(content, userId);
content.ChangePublishedState(false);
repository.AddOrUpdate(content);
uow.Commit();
}
}
else
{
foreach (var content in contents)
{
if (Saving != null)
Saving(content, e);
SetWriter(content, userId);
repository.AddOrUpdate(content);
}
uow.Commit();
}
if (Saved != null)
Saved(contents, e);
}
}
}
/// <summary>
@@ -544,24 +570,26 @@ namespace Umbraco.Core.Services
/// <param name="userId">Optional Id of the User saving the Content</param>
public void Save(IEnumerable<Lazy<IContent>> contents, int userId = -1)
{
var repository = _contentRepository;
var uow = _uowProvider.GetUnitOfWork();
using (var repository = RepositoryResolver.Current.Factory.CreateContentRepository(uow))
{
var e = new SaveEventArgs(uow);
if (Saving != null)
Saving(contents, e);
var e = new SaveEventArgs();
if (Saving != null)
Saving(contents, e);
if (!e.Cancel)
{
foreach (var content in contents)
{
SetWriter(content.Value, userId);
content.Value.ChangePublishedState(false);
repository.AddOrUpdate(content.Value);
_unitOfWork.Commit();
}
if (Saved != null)
Saved(contents, e);
}
if (!e.Cancel)
{
foreach (var content in contents)
{
SetWriter(content.Value, userId);
content.Value.ChangePublishedState(false);
repository.AddOrUpdate(content.Value);
uow.Commit();
}
if (Saved != null)
Saved(contents, e);
}
}
}
/// <summary>
@@ -571,29 +599,31 @@ namespace Umbraco.Core.Services
/// <param name="contentTypeId">Id of the <see cref="IContentType"/></param>
public void DeleteContentOfType(int contentTypeId)
{
var repository = _contentRepository;
//NOTE What about content that has the contenttype as part of its composition?
var query = Query<IContent>.Builder.Where(x => x.ContentTypeId == contentTypeId);
var contents = repository.GetByQuery(query);
var e = new DeleteEventArgs { Id = contentTypeId };
if (Deleting != null)
Deleting(contents, e);
if (!e.Cancel)
var uow = _uowProvider.GetUnitOfWork();
using (var repository = RepositoryResolver.Current.Factory.CreateContentRepository(uow))
{
foreach (var content in contents)
{
((Content) content).ChangeTrashedState(true);
repository.AddOrUpdate(content);
}
//NOTE What about content that has the contenttype as part of its composition?
var query = Query<IContent>.Builder.Where(x => x.ContentTypeId == contentTypeId);
var contents = repository.GetByQuery(query);
_unitOfWork.Commit();
var e = new DeleteEventArgs { Id = contentTypeId };
if (Deleting != null)
Deleting(contents, e);
if (Deleted != null)
Deleted(contents, e);
}
if (!e.Cancel)
{
foreach (var content in contents)
{
((Content)content).ChangeTrashedState(true);
repository.AddOrUpdate(content);
}
uow.Commit();
if (Deleted != null)
Deleted(contents, e);
}
}
}
/// <summary>
@@ -614,13 +644,17 @@ namespace Umbraco.Core.Services
if (!e.Cancel)
{
var repository = _contentRepository;
SetWriter(content, userId);
repository.Delete(content);
_unitOfWork.Commit();
var uow = _uowProvider.GetUnitOfWork();
using (var repository = RepositoryResolver.Current.Factory.CreateContentRepository(uow))
{
SetWriter(content, userId);
repository.Delete(content);
uow.Commit();
if (Deleted != null)
Deleted(content, e);
if (Deleted != null)
Deleted(content, e);
}
}
}
@@ -661,11 +695,14 @@ namespace Umbraco.Core.Services
if (!e.Cancel)
{
var repository = _contentRepository;
repository.Delete(id, versionDate);
using (var repository = RepositoryResolver.Current.Factory.CreateContentRepository(_uowProvider.GetUnitOfWork()))
{
repository.Delete(id, versionDate);
if (Deleted != null)
Deleted(versionDate, e);
if (Deleted != null)
Deleted(versionDate, e);
}
}
}
@@ -678,25 +715,26 @@ namespace Umbraco.Core.Services
/// <param name="userId">Optional Id of the User deleting versions of a Content object</param>
public void Delete(int id, Guid versionId, bool deletePriorVersions, int userId = -1)
{
var repository = _contentRepository;
if(deletePriorVersions)
using (var repository = RepositoryResolver.Current.Factory.CreateContentRepository(_uowProvider.GetUnitOfWork()))
{
var content = repository.GetByVersion(id, versionId);
Delete(id, content.UpdateDate, userId);
}
if (deletePriorVersions)
{
var content = repository.GetByVersion(id, versionId);
Delete(id, content.UpdateDate, userId);
}
var e = new DeleteEventArgs {Id = id};
if (Deleting != null)
Deleting(versionId, e);
var e = new DeleteEventArgs { Id = id };
if (Deleting != null)
Deleting(versionId, e);
if (!e.Cancel)
{
repository.Delete(id, versionId);
if (!e.Cancel)
{
repository.Delete(id, versionId);
if (Deleted != null)
Deleted(versionId, e);
}
if (Deleted != null)
Deleted(versionId, e);
}
}
}
/// <summary>
@@ -709,11 +747,15 @@ namespace Umbraco.Core.Services
{
//TODO If content item has children those should also be moved to the recycle bin
//TODO Unpublish deleted content + children
var repository = _contentRepository;
SetWriter(content, userId);
content.ChangeTrashedState(true);
repository.AddOrUpdate(content);
_unitOfWork.Commit();
var uow = _uowProvider.GetUnitOfWork();
using (var repository = RepositoryResolver.Current.Factory.CreateContentRepository(uow))
{
SetWriter(content, userId);
content.ChangeTrashedState(true);
repository.AddOrUpdate(content);
uow.Commit();
}
}
/// <summary>
@@ -767,16 +809,18 @@ namespace Umbraco.Core.Services
/// </summary>
public void EmptyRecycleBin()
{
var repository = _contentRepository;
var query = Query<IContent>.Builder.Where(x => x.ParentId == -20);
var contents = repository.GetByQuery(query);
foreach (var content in contents)
var uow = _uowProvider.GetUnitOfWork();
using (var repository = RepositoryResolver.Current.Factory.CreateContentRepository(uow))
{
repository.Delete(content);
}
_unitOfWork.Commit();
var query = Query<IContent>.Builder.Where(x => x.ParentId == -20);
var contents = repository.GetByQuery(query);
foreach (var content in contents)
{
repository.Delete(content);
}
uow.Commit();
}
}
/// <summary>
@@ -801,12 +845,14 @@ namespace Umbraco.Core.Services
copy.ParentId = parentId;
copy.Name = copy.Name + " (1)";
var repository = _contentRepository;
var uow = _uowProvider.GetUnitOfWork();
using (var repository = RepositoryResolver.Current.Factory.CreateContentRepository(uow))
{
SetWriter(content, userId);
SetWriter(content, userId);
repository.AddOrUpdate(copy);
_unitOfWork.Commit();
repository.AddOrUpdate(copy);
uow.Commit();
}
}
if(Copied != null)
@@ -867,25 +913,28 @@ namespace Umbraco.Core.Services
{
var e = new RollbackEventArgs();
var repository = _contentRepository;
var content = repository.GetByVersion(id, versionId);
if (Rollingback != null)
Rollingback(content, e);
if (!e.Cancel)
var uow = _uowProvider.GetUnitOfWork();
using (var repository = RepositoryResolver.Current.Factory.CreateContentRepository(uow))
{
SetUser(content, userId);
SetWriter(content, userId);
var content = repository.GetByVersion(id, versionId);
repository.AddOrUpdate(content);
_unitOfWork.Commit();
if (Rollingback != null)
Rollingback(content, e);
if (Rolledback != null)
Rolledback(content, e);
if (!e.Cancel)
{
SetUser(content, userId);
SetWriter(content, userId);
repository.AddOrUpdate(content);
uow.Commit();
if (Rolledback != null)
Rolledback(content, e);
}
return content;
}
return content;
}
/// <summary>
@@ -19,7 +19,7 @@ namespace Umbraco.Core.Services
{
private readonly IContentService _contentService;
private readonly IMediaService _mediaService;
private readonly IUnitOfWork _unitOfWork;
private readonly IDatabaseUnitOfWork _unitOfWork;
private readonly IContentTypeRepository _contentTypeRepository;
private readonly IMediaTypeRepository _mediaTypeRepository;
@@ -27,7 +27,7 @@ namespace Umbraco.Core.Services
: this(contentService, mediaService, new PetaPocoUnitOfWorkProvider())
{}
public ContentTypeService(IContentService contentService, IMediaService mediaService, IUnitOfWorkProvider provider)
public ContentTypeService(IContentService contentService, IMediaService mediaService, IDatabaseUnitOfWorkProvider provider)
{
_contentService = contentService;
_mediaService = mediaService;
+2 -2
View File
@@ -15,7 +15,7 @@ namespace Umbraco.Core.Services
/// </summary>
public class DataTypeService : IDataTypeService
{
private readonly IUnitOfWork _unitOfWork;
private readonly IDatabaseUnitOfWork _unitOfWork;
private readonly IDataTypeDefinitionRepository _dataTypeService;
private readonly IContentTypeRepository _contentTypeRepository;
@@ -23,7 +23,7 @@ namespace Umbraco.Core.Services
{
}
public DataTypeService(IUnitOfWorkProvider provider)
public DataTypeService(IDatabaseUnitOfWorkProvider provider)
{
_unitOfWork = provider.GetUnitOfWork();
_dataTypeService = RepositoryResolver.Current.Factory.CreateDataTypeDefinitionRepository(_unitOfWork);
+2 -2
View File
@@ -12,7 +12,7 @@ namespace Umbraco.Core.Services
public class FileService : IFileService
{
private readonly IUnitOfWork _fileUnitOfWork;
private readonly IUnitOfWork _dataUnitOfWork;
private readonly IDatabaseUnitOfWork _dataUnitOfWork;
private readonly IStylesheetRepository _stylesheetRepository;
private readonly IScriptRepository _scriptRepository;
private readonly ITemplateRepository _templateRepository;
@@ -21,7 +21,7 @@ namespace Umbraco.Core.Services
{
}
public FileService(IUnitOfWorkProvider fileProvider, IUnitOfWorkProvider dataProvider)
public FileService(IUnitOfWorkProvider fileProvider, IDatabaseUnitOfWorkProvider dataProvider)
{
_fileUnitOfWork = fileProvider.GetUnitOfWork();
_dataUnitOfWork = dataProvider.GetUnitOfWork();
@@ -15,7 +15,7 @@ namespace Umbraco.Core.Services
public class LocalizationService : ILocalizationService
{
private static readonly Guid RootParentId = new Guid("41c7638d-f529-4bff-853e-59a0c2fb1bde");
private readonly IUnitOfWork _unitOfWork;
private readonly IDatabaseUnitOfWork _unitOfWork;
private readonly IDictionaryRepository _dictionaryRepository;
private readonly ILanguageRepository _languageRepository;
@@ -23,7 +23,7 @@ namespace Umbraco.Core.Services
{
}
public LocalizationService(IUnitOfWorkProvider provider)
public LocalizationService(IDatabaseUnitOfWorkProvider provider)
{
_unitOfWork = provider.GetUnitOfWork();
_dictionaryRepository = RepositoryResolver.Current.Factory.CreateDictionaryRepository(_unitOfWork);
+26 -4
View File
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence;
@@ -12,14 +13,14 @@ namespace Umbraco.Core.Services
/// </summary>
public class MediaService : IMediaService
{
private readonly IUnitOfWork _unitOfWork;
private readonly IDatabaseUnitOfWork _unitOfWork;
private readonly IMediaRepository _mediaRepository;
public MediaService() : this(new PetaPocoUnitOfWorkProvider())
{
}
public MediaService(IUnitOfWorkProvider provider)
public MediaService(IDatabaseUnitOfWorkProvider provider)
{
_unitOfWork = provider.GetUnitOfWork();
_mediaRepository = RepositoryResolver.Current.Factory.CreateMediaRepository(_unitOfWork);
@@ -202,8 +203,19 @@ namespace Umbraco.Core.Services
public void Save(IMedia media, int userId)
{
var repository = _mediaRepository;
repository.AddOrUpdate(media);
_unitOfWork.Commit();
var e = new SaveEventArgs(_unitOfWork);
if (Saving != null)
Saving(media, e);
if (!e.Cancel)
{
repository.AddOrUpdate(media);
_unitOfWork.Commit();
if (Saved != null)
Saved(media, e);
}
}
/// <summary>
@@ -220,5 +232,15 @@ namespace Umbraco.Core.Services
}
_unitOfWork.Commit();
}
/// <summary>
/// Occurs before Save
/// </summary>
public static event EventHandler<SaveEventArgs> Saving;
/// <summary>
/// Occurs after Save
/// </summary>
public static event EventHandler<SaveEventArgs> Saved;
}
}
+16 -5
View File
@@ -31,18 +31,29 @@ namespace Umbraco.Core.Services
private ServiceContext()
{
BuildServiceCache();
BuildServiceCache(new PetaPocoUnitOfWorkProvider(), new FileUnitOfWorkProvider(), new PublishingStrategy());
}
/// <summary>
/// Internal constructor used for unit tests
/// </summary>
/// <param name="dbUnitOfWorkProvider"></param>
/// <param name="fileUnitOfWorkProvider"></param>
/// <param name="publishingStrategy"></param>
internal ServiceContext(IDatabaseUnitOfWorkProvider dbUnitOfWorkProvider, IUnitOfWorkProvider fileUnitOfWorkProvider, IPublishingStrategy publishingStrategy)
{
BuildServiceCache(dbUnitOfWorkProvider, fileUnitOfWorkProvider, publishingStrategy);
}
#endregion
/// <summary>
/// Builds the various services
/// </summary>
private void BuildServiceCache()
private void BuildServiceCache(IDatabaseUnitOfWorkProvider dbUnitOfWorkProvider, IUnitOfWorkProvider fileUnitOfWorkProvider, IPublishingStrategy publishingStrategy)
{
var provider = new PetaPocoUnitOfWorkProvider();
var fileProvider = new FileUnitOfWorkProvider();
var publishingStrategy = new PublishingStrategy();
var provider = dbUnitOfWorkProvider;
var fileProvider = fileUnitOfWorkProvider;
if(_userService == null)
_userService = new UserService(provider);
+2 -2
View File
@@ -12,10 +12,10 @@ namespace Umbraco.Core.Services
/// </summary>
internal class UserService : IUserService
{
private readonly IUnitOfWork _unitOfWork;
private readonly IDatabaseUnitOfWork _unitOfWork;
private readonly IUserRepository _userRepository;
public UserService(IUnitOfWorkProvider provider)
public UserService(IDatabaseUnitOfWorkProvider provider)
{
_unitOfWork = provider.GetUnitOfWork();
_userRepository = RepositoryResolver.Current.Factory.CreateUserRepository(_unitOfWork);
+2
View File
@@ -400,6 +400,8 @@
<Compile Include="Persistence\TransactionType.cs" />
<Compile Include="Persistence\UnitOfWork\FileUnitOfWork.cs" />
<Compile Include="Persistence\UnitOfWork\FileUnitOfWorkProvider.cs" />
<Compile Include="Persistence\UnitOfWork\IDatabaseUnitOfWork.cs" />
<Compile Include="Persistence\UnitOfWork\IDatabaseUnitOfWorkProvider.cs" />
<Compile Include="Persistence\UnitOfWork\IUnitOfWork.cs" />
<Compile Include="Persistence\UnitOfWork\IUnitOfWorkProvider.cs" />
<Compile Include="Persistence\UnitOfWork\IUnitOfWorkRepository.cs" />
@@ -228,9 +228,6 @@ namespace Umbraco.Tests.CodeFirst
[TearDown]
public override void TearDown()
{
DatabaseContext.Database.Dispose();
base.TearDown();
//reset the app context
@@ -38,8 +38,6 @@ namespace Umbraco.Tests.Models
[TearDown]
public override void TearDown()
{
DatabaseContext.Database.Dispose();
//reset the app context
DataTypesResolver.Reset();
@@ -13,7 +13,7 @@ using Umbraco.Tests.TestHelpers.Entities;
namespace Umbraco.Tests.Persistence.Repositories
{
[TestFixture]
[TestFixture]
public class ContentRepositoryTest : BaseDatabaseFactoryTest
{
[SetUp]
@@ -29,14 +29,14 @@ namespace Umbraco.Tests.Persistence
//[TestCase(typeof(IUserTypeRepository))]
//[TestCase(typeof(IUserRepository))]
//[TestCase(typeof(IMacroRepository))]
[TestCase(typeof(IContentRepository))]
[TestCase(typeof(IMediaRepository))]
[TestCase(typeof(IMediaTypeRepository))]
[TestCase(typeof(IContentTypeRepository))]
[TestCase(typeof(IDataTypeDefinitionRepository))]
[TestCase(typeof(IDictionaryRepository))]
[TestCase(typeof(ILanguageRepository))]
//[TestCase(typeof(IMacroRepository))]
[TestCase(typeof(ILanguageRepository))]
[TestCase(typeof(IRelationRepository))]
[TestCase(typeof(IRelationTypeRepository))]
[TestCase(typeof(IScriptRepository))]
@@ -46,29 +46,16 @@ namespace Umbraco.Tests.Persistence
{
var method = typeof(RepositoryResolver).GetMethod("ResolveByType", BindingFlags.NonPublic | BindingFlags.Instance);
var gMethod = method.MakeGenericMethod(repoType);
var repo = gMethod.Invoke(RepositoryResolver.Current, new object[] { new PetaPocoUnitOfWork() });
var repo = gMethod.Invoke(RepositoryResolver.Current, new object[] { new PetaPocoUnitOfWork(DatabaseFactory.Current.Database) });
Assert.IsNotNull(repo);
Assert.IsTrue(TypeHelper.IsTypeAssignableFrom(repoType, repo.GetType()));
}
//[Test]
//public void Can_Resolve_All_Repositories()
//{
// // Arrange
// var uow = new PetaPocoUnitOfWork();
// // Act
// RepositoryResolver.RegisterRepositories();
// // Assert
// Assert.That(RepositoryResolver.RegisteredRepositories(), Is.EqualTo(15));
//}
[Test]
[Test]
public void Can_Verify_UOW_In_Repository()
{
// Arrange
var uow = new PetaPocoUnitOfWork();
var uow = new PetaPocoUnitOfWork(DatabaseFactory.Current.Database);
// Act
var repository = RepositoryResolver.Current.ResolveByType<IContentRepository>(uow);
@@ -87,8 +74,8 @@ namespace Umbraco.Tests.Persistence
Assert.That(isSubclassOf, Is.False);
Assert.That(isAssignableFrom, Is.True);
var uow = new PetaPocoUnitOfWork();
var uow = new PetaPocoUnitOfWork(DatabaseFactory.Current.Database);
var repository = RepositoryResolver.Current.ResolveByType<IContentRepository>(uow);
bool subclassOf = repository.GetType().IsSubclassOf(typeof (IRepository<int, IContent>));
@@ -46,8 +46,8 @@ namespace Umbraco.Tests.Publishing
[TearDown]
public override void TearDown()
{
DatabaseContext.Database.Dispose();
base.TearDown();
//TestHelper.ClearDatabase();
//reset the app context
@@ -14,7 +14,7 @@ using Umbraco.Tests.TestHelpers.Entities;
namespace Umbraco.Tests.Services
{
/// <summary>
/// <summary>
/// Tests covering all methods in the ContentService class.
/// This is more of an integration test as it involves multiple layers
/// as well as configuration.
@@ -668,7 +668,7 @@ namespace Umbraco.Tests.Services
[Test]
public void Can_Save_Lazy_Content()
{
var unitOfWork = new PetaPocoUnitOfWork();
var unitOfWork = new PetaPocoUnitOfWork(DatabaseFactory.Current.Database);
var contentType = ServiceContext.ContentTypeService.GetContentType("umbTextpage");
var root = ServiceContext.ContentService.GetById(1046);
@@ -0,0 +1,257 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Publishing;
using Umbraco.Core.Services;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.TestHelpers.Entities;
namespace Umbraco.Tests.Services
{
[TestFixture]
public class ThreadSafetyServiceTest : BaseDatabaseFactoryTest
{
private PerThreadPetaPocoUnitOfWorkProvider _uowProvider;
[SetUp]
public override void Initialize()
{
base.Initialize();
//here we are going to override the ServiceContext because normally with our test cases we use a
//global Database object but this is NOT how it should work in the web world or in any multi threaded scenario.
//we need a new Database object for each thread.
_uowProvider = new PerThreadPetaPocoUnitOfWorkProvider();
ServiceContext = new ServiceContext(_uowProvider, new FileUnitOfWorkProvider(), new PublishingStrategy());
CreateTestData();
}
[TearDown]
public override void TearDown()
{
_error = null;
_lastUowIdWithThread = null;
//dispose!
_uowProvider.Dispose();
base.TearDown();
ServiceContext = null;
}
/// <summary>
/// Used to track exceptions during multi-threaded tests, volatile so that it is not locked in CPU registers.
/// </summary>
private volatile Exception _error = null;
private int _maxThreadCount = 20;
private object _locker = new object();
private Tuple<int, Guid> _lastUowIdWithThread = null;
[Test]
public void Ensure_All_Threads_Reference_Different_Units_Of_Work_Content_Service()
{
//we will mimick the ServiceContext in that each repository in a service (i.e. ContentService) is a singleton
var contentService = (ContentService)ServiceContext.ContentService;
var threads = new List<Thread>();
Debug.WriteLine("Starting test...");
//bind to event to determine what is going on during the saving process
ContentService.Saving += HandleSaving;
for (var i = 0; i < _maxThreadCount; i++)
{
var t = new Thread(() =>
{
try
{
Debug.WriteLine("Created content on thread: " + Thread.CurrentThread.ManagedThreadId);
//create 2 content items
var content1 = contentService.CreateContent(-1, "umbTextpage", 0);
content1.Name = "test" + Guid.NewGuid();
Debug.WriteLine("Saving content1 on thread: " + Thread.CurrentThread.ManagedThreadId);
contentService.Save(content1);
Thread.Sleep(100); //quick pause for maximum overlap!
var content2 = contentService.CreateContent(-1, "umbTextpage", 0);
content2.Name = "test" + Guid.NewGuid();
Debug.WriteLine("Saving content2 on thread: " + Thread.CurrentThread.ManagedThreadId);
contentService.Save(content2);
}
catch(Exception e)
{
_error = e;
}
});
threads.Add(t);
}
//start all threads
threads.ForEach(x => x.Start());
//wait for all to complete
threads.ForEach(x => x.Join());
//kill them all
threads.ForEach(x => x.Abort());
if (_error == null)
{
//now look up all items, there should be 40!
var items = contentService.GetRootContent();
Assert.AreEqual(40, items.Count());
}
else
{
Assert.Fail("ERROR! " + _error);
}
}
[Test]
public void Ensure_All_Threads_Reference_Different_Units_Of_Work_Media_Service()
{
//we will mimick the ServiceContext in that each repository in a service (i.e. ContentService) is a singleton
var mediaService = (MediaService)ServiceContext.MediaService;
var threads = new List<Thread>();
Debug.WriteLine("Starting test...");
//bind to event to determine what is going on during the saving process
ContentService.Saving += HandleSaving;
for (var i = 0; i < _maxThreadCount; i++)
{
var t = new Thread(() =>
{
try
{
var folderMediaType = ServiceContext.ContentTypeService.GetMediaType(1031);
Debug.WriteLine("Created content on thread: " + Thread.CurrentThread.ManagedThreadId);
//create 2 content items
var folder1 = MockedMedia.CreateMediaFolder(folderMediaType, -1);
folder1.Name = "test" + Guid.NewGuid();
Debug.WriteLine("Saving folder1 on thread: " + Thread.CurrentThread.ManagedThreadId);
mediaService.Save(folder1, 0);
Thread.Sleep(100); //quick pause for maximum overlap!
var folder2 = MockedMedia.CreateMediaFolder(folderMediaType, -1);
folder2.Name = "test" + Guid.NewGuid();
Debug.WriteLine("Saving folder2 on thread: " + Thread.CurrentThread.ManagedThreadId);
mediaService.Save(folder2, 0);
}
catch (Exception e)
{
_error = e;
}
});
threads.Add(t);
}
//start all threads
threads.ForEach(x => x.Start());
//wait for all to complete
threads.ForEach(x => x.Join());
//kill them all
threads.ForEach(x => x.Abort());
if (_error == null)
{
//now look up all items, there should be 40!
var items = mediaService.GetRootMedia();
Assert.AreEqual(40, items.Count());
}
else
{
Assert.Fail("ERROR! " + _error);
}
}
private void HandleSaving(object sender, SaveEventArgs args)
{
if (_error == null)
{
lock (_locker)
{
//get the instance id of the unit of work
var uowId = ((PetaPocoUnitOfWork)args.UnitOfWork).InstanceId;
if (_lastUowIdWithThread == null)
{
Debug.WriteLine("Initial UOW found = " + uowId + " with thread id " + Thread.CurrentThread.ManagedThreadId);
_lastUowIdWithThread = new Tuple<int, Guid>(
Thread.CurrentThread.ManagedThreadId, uowId);
}
else
{
Debug.WriteLine("Next thread running. UOW found = " + uowId + " with thread id " + Thread.CurrentThread.ManagedThreadId);
var newTuple = new Tuple<int, Guid>(
Thread.CurrentThread.ManagedThreadId, uowId);
//check if the uowId is the same as the last and if the thread Id's are different then we have a problem
if (newTuple.Item2 == _lastUowIdWithThread.Item2
&& newTuple.Item1 != _lastUowIdWithThread.Item1)
{
_error = new Exception("The threads: " + newTuple.Item1 + " and " + _lastUowIdWithThread.Item1 + " are both referencing the Unit of work: " + _lastUowIdWithThread.Item2);
}
}
}
}
}
public void CreateTestData()
{
//Create and Save ContentType "umbTextpage" -> 1045
ContentType contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage", "Textpage");
contentType.Key = new Guid("1D3A8E6E-2EA9-4CC1-B229-1AEE19821522");
ServiceContext.ContentTypeService.Save(contentType);
}
/// <summary>
/// Creates a Database object per thread, this mimics the web context which is per HttpContext
/// </summary>
internal class PerThreadPetaPocoUnitOfWorkProvider : DisposableObject, IDatabaseUnitOfWorkProvider
{
private readonly ConcurrentDictionary<int, Database> _databases = new ConcurrentDictionary<int, Database>();
public IDatabaseUnitOfWork GetUnitOfWork()
{
//Create or get a database instance for this thread.
var db = _databases.GetOrAdd(Thread.CurrentThread.ManagedThreadId, i => new Database(Umbraco.Core.Configuration.GlobalSettings.UmbracoConnectionName));
return new PetaPocoUnitOfWork(db);
}
protected override void DisposeResources()
{
//dispose the databases
_databases.ForEach(x => x.Value.Dispose());
}
}
}
}
@@ -10,6 +10,8 @@ using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.ObjectResolution;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Publishing;
using Umbraco.Core.Services;
using Umbraco.Tests.Stubs;
using Umbraco.Web;
@@ -18,6 +20,7 @@ using umbraco.BusinessLogic;
namespace Umbraco.Tests.TestHelpers
{
/// <summary>
/// Use this abstract class for tests that requires a Sql Ce database populated with the umbraco db schema.
/// The PetaPoco Database class should be used through the <see cref="DatabaseFactory"/> singleton.
@@ -57,7 +60,7 @@ namespace Umbraco.Tests.TestHelpers
Resolution.Freeze();
ApplicationContext = new ApplicationContext() { IsReady = true };
DatabaseContext = DatabaseContext.Current;
ServiceContext = ServiceContext.Current;
ServiceContext = new ServiceContext(new PetaPocoUnitOfWorkProvider(), new FileUnitOfWorkProvider(), new PublishingStrategy());
//Configure the Database and Sql Syntax based on connection string set in config
DatabaseContext.Initialize();
@@ -69,6 +72,8 @@ namespace Umbraco.Tests.TestHelpers
[TearDown]
public virtual void TearDown()
{
DatabaseContext.Database.Dispose();
TestHelper.CleanContentDirectories();
//reset the app context
+1
View File
@@ -235,6 +235,7 @@
<Compile Include="Routing\UrlsWithNestedDomains.cs" />
<Compile Include="Services\ContentServicePerformanceTest.cs" />
<Compile Include="Services\ContentServiceTests.cs" />
<Compile Include="Services\ThreadSafetyServiceTest.cs" />
<Compile Include="Surface\PluginControllerAreaTests.cs" />
<Compile Include="Templates\MasterPageHelperTests.cs" />
<Compile Include="TestHelpers\BaseDatabaseFactoryTest.cs" />
+15 -2
View File
@@ -421,13 +421,15 @@ namespace Umbraco.Web
app.EndRequest += (sender, args) =>
{
var httpContext = ((HttpApplication)sender).Context;
var httpContext = ((HttpApplication)sender).Context;
if (UmbracoContext.Current != null && UmbracoContext.Current.IsFrontEndUmbracoRequest)
{
//write the trace output for diagnostics at the end of the request
httpContext.Trace.Write("UmbracoModule", "Umbraco request completed");
LogHelper.Debug<UmbracoModule>("Total milliseconds for umbraco request to process: " + DateTime.Now.Subtract(UmbracoContext.Current.ObjectCreated).TotalMilliseconds);
}
}
DisposeHttpContextItems(httpContext);
};
}
@@ -438,5 +440,16 @@ namespace Umbraco.Web
#endregion
/// <summary>
/// Any object that is in the HttpContext.Items collection that is IDisposable will get disposed on the end of the request
/// </summary>
/// <param name="http"></param>
private static void DisposeHttpContextItems(HttpContext http)
{
foreach(var i in http.Items)
{
i.DisposeIfDisposable();
}
}
}
}