191 lines
4.7 KiB
C#
191 lines
4.7 KiB
C#
using System;
|
|
using JD.Kernel.Classes;
|
|
using JD.Kernel.StaticHelpers;
|
|
|
|
namespace JD.PropertyService
|
|
{
|
|
/// <summary>
|
|
/// Properties that cache changes made to them until they are bulk written to
|
|
/// the property object which is being cached.
|
|
/// </summary>
|
|
public class TransactionalProperties : Properties, IDisposable
|
|
{
|
|
#region Variables
|
|
|
|
private readonly Scope m_isSyncing = new Scope();
|
|
private IProperties m_internalProperties;
|
|
private bool m_isDirty;
|
|
|
|
#endregion
|
|
|
|
#region Constructor
|
|
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
public TransactionalProperties()
|
|
: this(new Properties())
|
|
{}
|
|
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="cachedProperties"></param>
|
|
public TransactionalProperties(IProperties cachedProperties)
|
|
{
|
|
Throw.IfNull(cachedProperties, "cachedProperties");
|
|
|
|
InternalProperties = cachedProperties;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
/// <summary>
|
|
/// Dirty flag
|
|
/// </summary>
|
|
public bool IsDirty
|
|
{
|
|
get { return m_isDirty; }
|
|
}
|
|
|
|
public override bool IsReadOnly
|
|
{
|
|
get
|
|
{
|
|
return m_internalProperties.IsReadOnly;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Access to the cached properties
|
|
/// </summary>
|
|
public IProperties InternalProperties
|
|
{
|
|
get
|
|
{
|
|
return m_internalProperties;
|
|
}
|
|
set
|
|
{
|
|
if (m_internalProperties != null)
|
|
{
|
|
m_internalProperties.PropertyChanged -= InternalPropertiesPropertyChanged;
|
|
}
|
|
|
|
m_internalProperties = value;
|
|
|
|
if (m_internalProperties != null)
|
|
{
|
|
m_internalProperties.PropertyChanged += InternalPropertiesPropertyChanged;
|
|
Revert();
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool Syncing
|
|
{
|
|
get { return m_isSyncing.InScope; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Methods
|
|
|
|
/// <summary>
|
|
/// Write dirty cache entries to cached properties, then reload the cache.
|
|
/// </summary>
|
|
public void Commit()
|
|
{
|
|
if (!Syncing)
|
|
{
|
|
using (m_isSyncing.Begin())
|
|
{
|
|
if (m_isDirty && OnPropertyChanging())
|
|
{
|
|
using (m_internalProperties.BeginScopedUpdate())
|
|
{
|
|
m_internalProperties.Clear();
|
|
CopyTo(m_internalProperties);
|
|
}
|
|
m_isDirty = false;
|
|
OnCommit();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load the cache from the properties being cached, discarding any previous cache content.
|
|
/// </summary>
|
|
public void Revert()
|
|
{
|
|
if (!Syncing)
|
|
{
|
|
using (m_isSyncing.Begin())
|
|
{
|
|
Clear();
|
|
m_internalProperties.CopyTo(this);
|
|
m_isDirty = false;
|
|
|
|
OnReverted();
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Events
|
|
|
|
private void OnCommit()
|
|
{
|
|
Commited?.Invoke(this, EventArgs.Empty);
|
|
}
|
|
|
|
private void OnReverted()
|
|
{
|
|
Reverted?.Invoke(this, EventArgs.Empty);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raised when the cache has been reloaded from the cached item.
|
|
/// </summary>
|
|
public event EventHandler Reverted;
|
|
|
|
/// <summary>
|
|
/// Raised when a dirty cache was written.
|
|
/// </summary>
|
|
public event EventHandler Commited;
|
|
|
|
#endregion
|
|
|
|
#region Event Handlers
|
|
|
|
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
|
|
{
|
|
m_isDirty = true;
|
|
base.OnPropertyChanged(e);
|
|
}
|
|
|
|
private void InternalPropertiesPropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
{
|
|
Revert();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IDisposable Members
|
|
|
|
/// <summary>
|
|
/// Interface implementation
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
// unbind event handlers
|
|
InternalProperties = null;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|