Changes PetaPocoUnitOfWorkFactory to accept an IDatabaseFactory as ctor. Changes PetaPocoUnitOfWork to not dispose of the Database since
it will be shared with the current thread in order to support nested transactions and changes how the Transaction object is created and used and then disposed of. Changes PetaPocoUnitOfWorkFactory to ensure that the UOW is created with the shared Database instance. Fixes a method in UserService to ensure that the UOW is disposed since it wasn't using a repository.
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
using Umbraco.Core.Persistence;
|
||||
@@ -37,8 +38,12 @@ namespace Umbraco.Core
|
||||
_timer = DisposableTimer.Start(x => LogHelper.Info<CoreBootManager>("Umbraco application startup complete" + " (took " + x + "ms)"));
|
||||
|
||||
//create database and service contexts for the app context
|
||||
var dbContext = new DatabaseContext(new DefaultDatabaseFactory());
|
||||
var serviceContext = new ServiceContext(new PetaPocoUnitOfWorkProvider(), new FileUnitOfWorkProvider(), new PublishingStrategy());
|
||||
var dbFactory = new DefaultDatabaseFactory(GlobalSettings.UmbracoConnectionName);
|
||||
var dbContext = new DatabaseContext(dbFactory);
|
||||
var serviceContext = new ServiceContext(
|
||||
new PetaPocoUnitOfWorkProvider(dbFactory),
|
||||
new FileUnitOfWorkProvider(),
|
||||
new PublishingStrategy());
|
||||
|
||||
//create the ApplicationContext
|
||||
ApplicationContext = ApplicationContext.Current = new ApplicationContext(dbContext, serviceContext);
|
||||
|
||||
@@ -13,9 +13,42 @@ namespace Umbraco.Core.Persistence
|
||||
/// </remarks>
|
||||
internal class DefaultDatabaseFactory : DisposableObject, IDatabaseFactory
|
||||
{
|
||||
private readonly string _connectionString;
|
||||
private readonly string _providerName;
|
||||
private static volatile UmbracoDatabase _globalInstance = null;
|
||||
private static readonly object Locker = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor initialized with the GlobalSettings.UmbracoConnectionName
|
||||
/// </summary>
|
||||
public DefaultDatabaseFactory() : this(GlobalSettings.UmbracoConnectionName)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor accepting custom connection string
|
||||
/// </summary>
|
||||
/// <param name="connectionString"></param>
|
||||
public DefaultDatabaseFactory(string connectionString)
|
||||
{
|
||||
Mandate.ParameterNotNullOrEmpty(connectionString, "connectionString");
|
||||
_connectionString = connectionString;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor accepting custom connectino string and provider name
|
||||
/// </summary>
|
||||
/// <param name="connectionString"></param>
|
||||
/// <param name="providerName"></param>
|
||||
public DefaultDatabaseFactory(string connectionString, string providerName)
|
||||
{
|
||||
Mandate.ParameterNotNullOrEmpty(connectionString, "connectionString");
|
||||
Mandate.ParameterNotNullOrEmpty(providerName, "providerName");
|
||||
_connectionString = connectionString;
|
||||
_providerName = providerName;
|
||||
}
|
||||
|
||||
public UmbracoDatabase CreateDatabase()
|
||||
{
|
||||
//no http context, create the singleton global object
|
||||
@@ -28,7 +61,9 @@ namespace Umbraco.Core.Persistence
|
||||
//double check
|
||||
if (_globalInstance == null)
|
||||
{
|
||||
_globalInstance = new UmbracoDatabase(GlobalSettings.UmbracoConnectionName);
|
||||
_globalInstance = string.IsNullOrEmpty(_providerName)
|
||||
? new UmbracoDatabase(_connectionString)
|
||||
: new UmbracoDatabase(_connectionString, _providerName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,7 +73,10 @@ namespace Umbraco.Core.Persistence
|
||||
//we have an http context, so only create one per request
|
||||
if (!HttpContext.Current.Items.Contains(typeof(DefaultDatabaseFactory)))
|
||||
{
|
||||
HttpContext.Current.Items.Add(typeof(DefaultDatabaseFactory), new UmbracoDatabase(GlobalSettings.UmbracoConnectionName));
|
||||
HttpContext.Current.Items.Add(typeof (DefaultDatabaseFactory),
|
||||
string.IsNullOrEmpty(_providerName)
|
||||
? new UmbracoDatabase(_connectionString)
|
||||
: new UmbracoDatabase(_connectionString, _providerName));
|
||||
}
|
||||
return (UmbracoDatabase)HttpContext.Current.Items[typeof(DefaultDatabaseFactory)];
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
|
||||
@@ -13,6 +14,18 @@ namespace Umbraco.Core.Persistence
|
||||
/// </remarks>
|
||||
public class UmbracoDatabase : Database
|
||||
{
|
||||
|
||||
|
||||
|
||||
private readonly Guid _instanceId = Guid.NewGuid();
|
||||
/// <summary>
|
||||
/// Used for testing
|
||||
/// </summary>
|
||||
internal Guid InstanceId
|
||||
{
|
||||
get { return _instanceId; }
|
||||
}
|
||||
|
||||
public UmbracoDatabase(IDbConnection connection) : base(connection)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -5,19 +5,21 @@ using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Persistence.UnitOfWork
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the Unit of Work implementation for PetaPoco
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// Represents the Unit of Work implementation for PetaPoco
|
||||
/// </summary>
|
||||
internal class PetaPocoUnitOfWork : DisposableObject, IDatabaseUnitOfWork
|
||||
{
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Used for testing
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// Used for testing
|
||||
/// </summary>
|
||||
internal Guid InstanceId { get; private set; }
|
||||
|
||||
private Guid _key;
|
||||
private readonly List<Operation> _operations = new List<Operation>();
|
||||
private Guid _key;
|
||||
private readonly List<Operation> _operations = new List<Operation>();
|
||||
private readonly Transaction _transaction;
|
||||
private bool _committed = false;
|
||||
|
||||
|
||||
/// <summary>
|
||||
@@ -26,150 +28,152 @@ namespace Umbraco.Core.Persistence.UnitOfWork
|
||||
/// <param name="database"></param>
|
||||
/// <remarks>
|
||||
/// This should normally not be used directly and should be created with the UnitOfWorkProvider
|
||||
///
|
||||
/// The Database instance used for this unit of work should not be shared with other unit's of work, other repositories, etc...
|
||||
/// as it will get disposed of when this unit of work is disposed.
|
||||
/// </remarks>
|
||||
internal PetaPocoUnitOfWork(UmbracoDatabase database)
|
||||
{
|
||||
Database = database;
|
||||
_key = Guid.NewGuid();
|
||||
InstanceId = Guid.NewGuid();
|
||||
}
|
||||
{
|
||||
Database = database;
|
||||
_key = Guid.NewGuid();
|
||||
InstanceId = Guid.NewGuid();
|
||||
_transaction = Database.GetTransaction();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers an <see cref="IEntity" /> instance to be added through this <see cref="UnitOfWork" />
|
||||
/// </summary>
|
||||
/// <param name="entity">The <see cref="IEntity" /></param>
|
||||
/// <param name="repository">The <see cref="IUnitOfWorkRepository" /> participating in the transaction</param>
|
||||
public void RegisterAdded(IEntity entity, IUnitOfWorkRepository repository)
|
||||
{
|
||||
_operations.Add(
|
||||
new Operation
|
||||
{
|
||||
Entity = entity,
|
||||
ProcessDate = DateTime.Now,
|
||||
Repository = repository,
|
||||
Type = TransactionType.Insert
|
||||
});
|
||||
}
|
||||
/// <summary>
|
||||
/// Registers an <see cref="IEntity" /> instance to be added through this <see cref="UnitOfWork" />
|
||||
/// </summary>
|
||||
/// <param name="entity">The <see cref="IEntity" /></param>
|
||||
/// <param name="repository">The <see cref="IUnitOfWorkRepository" /> participating in the transaction</param>
|
||||
public void RegisterAdded(IEntity entity, IUnitOfWorkRepository repository)
|
||||
{
|
||||
_operations.Add(
|
||||
new Operation
|
||||
{
|
||||
Entity = entity,
|
||||
ProcessDate = DateTime.Now,
|
||||
Repository = repository,
|
||||
Type = TransactionType.Insert
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers an <see cref="IEntity" /> instance to be changed through this <see cref="UnitOfWork" />
|
||||
/// </summary>
|
||||
/// <param name="entity">The <see cref="IEntity" /></param>
|
||||
/// <param name="repository">The <see cref="IUnitOfWorkRepository" /> participating in the transaction</param>
|
||||
public void RegisterChanged(IEntity entity, IUnitOfWorkRepository repository)
|
||||
{
|
||||
_operations.Add(
|
||||
new Operation
|
||||
{
|
||||
Entity = entity,
|
||||
ProcessDate = DateTime.Now,
|
||||
Repository = repository,
|
||||
Type = TransactionType.Update
|
||||
});
|
||||
}
|
||||
/// <summary>
|
||||
/// Registers an <see cref="IEntity" /> instance to be changed through this <see cref="UnitOfWork" />
|
||||
/// </summary>
|
||||
/// <param name="entity">The <see cref="IEntity" /></param>
|
||||
/// <param name="repository">The <see cref="IUnitOfWorkRepository" /> participating in the transaction</param>
|
||||
public void RegisterChanged(IEntity entity, IUnitOfWorkRepository repository)
|
||||
{
|
||||
_operations.Add(
|
||||
new Operation
|
||||
{
|
||||
Entity = entity,
|
||||
ProcessDate = DateTime.Now,
|
||||
Repository = repository,
|
||||
Type = TransactionType.Update
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers an <see cref="IEntity" /> instance to be removed through this <see cref="UnitOfWork" />
|
||||
/// </summary>
|
||||
/// <param name="entity">The <see cref="IEntity" /></param>
|
||||
/// <param name="repository">The <see cref="IUnitOfWorkRepository" /> participating in the transaction</param>
|
||||
public void RegisterRemoved(IEntity entity, IUnitOfWorkRepository repository)
|
||||
{
|
||||
_operations.Add(
|
||||
new Operation
|
||||
{
|
||||
Entity = entity,
|
||||
ProcessDate = DateTime.Now,
|
||||
Repository = repository,
|
||||
Type = TransactionType.Delete
|
||||
});
|
||||
}
|
||||
/// <summary>
|
||||
/// Registers an <see cref="IEntity" /> instance to be removed through this <see cref="UnitOfWork" />
|
||||
/// </summary>
|
||||
/// <param name="entity">The <see cref="IEntity" /></param>
|
||||
/// <param name="repository">The <see cref="IUnitOfWorkRepository" /> participating in the transaction</param>
|
||||
public void RegisterRemoved(IEntity entity, IUnitOfWorkRepository repository)
|
||||
{
|
||||
_operations.Add(
|
||||
new Operation
|
||||
{
|
||||
Entity = entity,
|
||||
ProcessDate = DateTime.Now,
|
||||
Repository = repository,
|
||||
Type = TransactionType.Delete
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Commits all batched changes within the scope of a PetaPoco transaction <see cref="Transaction"/>
|
||||
/// </summary>
|
||||
public void Commit()
|
||||
{
|
||||
using(Transaction transaction = Database.GetTransaction())
|
||||
{
|
||||
foreach (var operation in _operations.OrderBy(o => o.ProcessDate))
|
||||
{
|
||||
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();
|
||||
}
|
||||
/// <summary>
|
||||
/// Commits all batched changes within the scope of a PetaPoco transaction <see cref="Transaction"/>
|
||||
/// </summary>
|
||||
public void Commit()
|
||||
{
|
||||
if (_committed)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot has already been called for this unit of work");
|
||||
}
|
||||
|
||||
// Clear everything
|
||||
_operations.Clear();
|
||||
_key = Guid.NewGuid();
|
||||
}
|
||||
foreach (var operation in _operations.OrderBy(o => o.ProcessDate))
|
||||
{
|
||||
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();
|
||||
|
||||
public object Key
|
||||
{
|
||||
get { return _key; }
|
||||
}
|
||||
// Clear everything
|
||||
_operations.Clear();
|
||||
_key = Guid.NewGuid();
|
||||
_committed = true;
|
||||
}
|
||||
|
||||
public object Key
|
||||
{
|
||||
get { return _key; }
|
||||
}
|
||||
|
||||
public UmbracoDatabase Database { get; private set; }
|
||||
|
||||
#region Operation
|
||||
#region Operation
|
||||
|
||||
/// <summary>
|
||||
/// Provides a snapshot of an entity and the repository reference it belongs to.
|
||||
/// </summary>
|
||||
private sealed class Operation
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the entity.
|
||||
/// </summary>
|
||||
/// <value>The entity.</value>
|
||||
public IEntity Entity { get; set; }
|
||||
/// <summary>
|
||||
/// Provides a snapshot of an entity and the repository reference it belongs to.
|
||||
/// </summary>
|
||||
private sealed class Operation
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the entity.
|
||||
/// </summary>
|
||||
/// <value>The entity.</value>
|
||||
public IEntity Entity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the process date.
|
||||
/// </summary>
|
||||
/// <value>The process date.</value>
|
||||
public DateTime ProcessDate { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the process date.
|
||||
/// </summary>
|
||||
/// <value>The process date.</value>
|
||||
public DateTime ProcessDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the repository.
|
||||
/// </summary>
|
||||
/// <value>The repository.</value>
|
||||
public IUnitOfWorkRepository Repository { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the repository.
|
||||
/// </summary>
|
||||
/// <value>The repository.</value>
|
||||
public IUnitOfWorkRepository Repository { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of operation.
|
||||
/// </summary>
|
||||
/// <value>The type of operation.</value>
|
||||
public TransactionType Type { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the type of operation.
|
||||
/// </summary>
|
||||
/// <value>The type of operation.</value>
|
||||
public TransactionType Type { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Ensures disposable objects are disposed
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Ensures that the Database instance is disposed of. As per the constructor documentation, the database instance
|
||||
/// used to construct this object should not be shared as it will get disposed of when this object is disposed.
|
||||
/// Ensures that the Transaction instance is disposed of
|
||||
/// </remarks>
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
_operations.Clear();
|
||||
Database.Dispose();
|
||||
}
|
||||
}
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
_operations.Clear();
|
||||
_transaction.Dispose();
|
||||
//ensure the local object will be gargabe collected
|
||||
Database = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Umbraco.Core.Configuration;
|
||||
using System;
|
||||
using Umbraco.Core.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Persistence.UnitOfWork
|
||||
{
|
||||
@@ -7,37 +8,26 @@ namespace Umbraco.Core.Persistence.UnitOfWork
|
||||
/// </summary>
|
||||
public class PetaPocoUnitOfWorkProvider : IDatabaseUnitOfWorkProvider
|
||||
{
|
||||
private readonly string _connectionString;
|
||||
private readonly string _providerName;
|
||||
private readonly IDatabaseFactory _dbFactory;
|
||||
|
||||
/// <summary>
|
||||
/// Parameterless constructor
|
||||
/// Parameterless constructor uses defaults
|
||||
/// </summary>
|
||||
public PetaPocoUnitOfWorkProvider()
|
||||
: this(new DefaultDatabaseFactory(GlobalSettings.UmbracoConnectionName))
|
||||
{
|
||||
_connectionString = GlobalSettings.UmbracoConnectionName;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor to explicitly set the connectionstring to use
|
||||
/// </summary>
|
||||
/// <param name="connectionString">Connection String to use</param>
|
||||
public PetaPocoUnitOfWorkProvider(string connectionString)
|
||||
{
|
||||
_connectionString = connectionString;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor to explicitly set the connectionstring and provider name to use,
|
||||
/// which will avoid the lookup of any additional config settings.
|
||||
/// </summary>
|
||||
/// <param name="connectionString">Connection String to use</param>
|
||||
/// <param name="providerName">Database Provider</param>
|
||||
public PetaPocoUnitOfWorkProvider(string connectionString, string providerName)
|
||||
{
|
||||
_connectionString = connectionString;
|
||||
_providerName = providerName;
|
||||
}
|
||||
/// <summary>
|
||||
/// Constructor accepting an IDatabaseFactory instance
|
||||
/// </summary>
|
||||
/// <param name="dbFactory"></param>
|
||||
internal PetaPocoUnitOfWorkProvider(IDatabaseFactory dbFactory)
|
||||
{
|
||||
Mandate.ParameterNotNull(dbFactory, "dbFactory");
|
||||
_dbFactory = dbFactory;
|
||||
}
|
||||
|
||||
#region Implementation of IUnitOfWorkProvider
|
||||
|
||||
@@ -52,10 +42,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork
|
||||
/// </remarks>
|
||||
public IDatabaseUnitOfWork GetUnitOfWork()
|
||||
{
|
||||
var database = string.IsNullOrEmpty(_providerName)
|
||||
? new UmbracoDatabase(_connectionString)
|
||||
: new UmbracoDatabase(_connectionString, _providerName);
|
||||
return new PetaPocoUnitOfWork(database);
|
||||
return new PetaPocoUnitOfWork(_dbFactory.CreateDatabase());
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -66,7 +53,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork
|
||||
/// <returns></returns>
|
||||
internal static IDatabaseUnitOfWork CreateUnitOfWork()
|
||||
{
|
||||
var provider = new PetaPocoUnitOfWorkProvider(GlobalSettings.UmbracoConnectionName);
|
||||
var provider = new PetaPocoUnitOfWorkProvider();
|
||||
return provider.GetUnitOfWork();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Umbraco.Core.Services
|
||||
private LocalizationService _localizationService;
|
||||
|
||||
/// <summary>
|
||||
/// Internal constructor used for unit tests
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="dbUnitOfWorkProvider"></param>
|
||||
/// <param name="fileUnitOfWorkProvider"></param>
|
||||
|
||||
@@ -53,16 +53,18 @@ namespace Umbraco.Core.Services
|
||||
|
||||
if(HttpRuntime.Cache[cacheKey] == null)
|
||||
{
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
userId =
|
||||
uow.Database.ExecuteScalar<int>(
|
||||
"select userID from umbracoUserLogins where contextID = @ContextId",
|
||||
new {ContextId = new Guid(contextId)});
|
||||
using (var uow = _uowProvider.GetUnitOfWork())
|
||||
{
|
||||
userId =
|
||||
uow.Database.ExecuteScalar<int>(
|
||||
"select userID from umbracoUserLogins where contextID = @ContextId",
|
||||
new { ContextId = new Guid(contextId) });
|
||||
|
||||
HttpRuntime.Cache.Insert(cacheKey, userId,
|
||||
null,
|
||||
System.Web.Caching.Cache.NoAbsoluteExpiration,
|
||||
new TimeSpan(0, (int)(Umbraco.Core.Configuration.GlobalSettings.TimeOutInMinutes / 10), 0));
|
||||
HttpRuntime.Cache.Insert(cacheKey, userId,
|
||||
null,
|
||||
System.Web.Caching.Cache.NoAbsoluteExpiration,
|
||||
new TimeSpan(0, (int)(Umbraco.Core.Configuration.GlobalSettings.TimeOutInMinutes / 10), 0));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user