You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Zero/ZeroLevel/Services/Collections/KeyValueTransactCollection.cs

272 lines
7.9 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace ZeroLevel.Services.Collections
{
/// <summary>
/// Класс обертывает коллекцию вида ключ-значение и позволяет проводить над ней транзакционные обновления
/// </summary>
/// <typeparam name="TKey">Тип ключа коллекции</typeparam>
/// <typeparam name="TValue">Тип значения коллекции</typeparam>
public class KeyValueTransactCollection<TKey, TValue> :
ITransactable
{
private ReaderWriterLockSlim _rwLock =
new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
/// <summary>
/// Коллекция
/// </summary>
readonly Dictionary<TKey, TValue> _collection = new Dictionary<TKey, TValue>();
public KeyValueTransactCollection() { }
/// <summary>
/// Проверка наличия ключа
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public bool HasKey(TKey key)
{
try
{
_rwLock.EnterReadLock();
return _collection.ContainsKey(key);
}
finally
{
_rwLock.ExitReadLock();
}
}
/// <summary>
/// Получение значения коллекции по ключу
/// </summary>
/// <param name="key">Ключ</param>
/// <returns>Значение</returns>
public TValue this[TKey key]
{
get
{
try
{
_rwLock.EnterReadLock();
TValue value;
if (_collection.TryGetValue(key, out value))
return (value);
}
finally
{
_rwLock.ExitReadLock();
}
throw new KeyNotFoundException();
}
}
/// <summary>
/// Количество записей
/// </summary>
public int Count
{
get
{
try
{
_rwLock.EnterReadLock();
return _collection.Count;
}
finally
{
_rwLock.ExitReadLock();
}
}
}
/// <summary>
/// Коллекция ключей
/// </summary>
public IEnumerable<TKey> Keys
{
get
{
try
{
_rwLock.EnterReadLock();
return _collection.Keys;
}
finally
{
_rwLock.ExitReadLock();
}
}
}
/// <summary>
/// Список ключ-значений
/// </summary>
public IEnumerable<KeyValuePair<TKey, TValue>> Items
{
get
{
try
{
_rwLock.EnterReadLock();
return _collection;
}
finally
{
_rwLock.ExitReadLock();
}
}
}
/// <summary>
/// Коллекция значений
/// </summary>
public IEnumerable<TValue> Values
{
get
{
try
{
_rwLock.EnterReadLock();
return _collection.Values;
}
finally
{
_rwLock.ExitReadLock();
}
}
}
#region Transaction update
/// <summary>
/// Список не обновленных данных (т.е. тех которые удалены в базе)
/// </summary>
readonly List<TKey> _removingDate = new List<TKey>();
/// <summary>
/// Обновленные данные
/// </summary>
readonly Dictionary<TKey, TValue> _updatedRecords = new Dictionary<TKey, TValue>();
/// <summary>
/// Новые данные
/// </summary>
readonly Dictionary<TKey, TValue> _newRecords = new Dictionary<TKey, TValue>();
void ClearTransactionDate()
{
_removingDate.Clear();
_updatedRecords.Clear();
_newRecords.Clear();
}
/// <summary>
/// Добавление или обновления записи
/// </summary>
/// <param name="id">Идентификатор записи</param>
/// <param name="value">Значение</param>
public void Post(TKey id, TValue value)
{
if (_isUpdating.State == false)
{
throw new Exception("Method Post allowed only in transaction");
}
if (!HasKey(id))
{
if (_newRecords.ContainsKey(id) == false)
{
_newRecords.Add(id, value);
}
else
{
_newRecords[id] = value;
}
}
else
{
5 months ago
if (!_collection[id]!.Equals(value))
{
if (false == _updatedRecords.ContainsKey(id))
{
_updatedRecords.Add(id, value);
}
else
{
_updatedRecords[id] = value;
}
}
if (_removingDate.Contains(id))
_removingDate.Remove(id);
}
return;
}
#endregion
readonly AtomicBoolean _isUpdating = new AtomicBoolean();
public bool StartTransction()
{
if (_isUpdating.Set())
{
_removingDate.AddRange(_collection.Keys.ToArray());
return true;
}
return false;
}
public bool Commit()
{
if (_isUpdating.State == false) return false;
try
{
_rwLock.EnterWriteLock();
foreach (TKey id in _removingDate)
{
_collection.Remove(id);
}
foreach (TKey key in _newRecords.Keys)
{
_collection.Add(key, _newRecords[key]);
}
foreach (TKey key in _updatedRecords.Keys)
{
_collection[key] = _updatedRecords[key];
}
}
finally
{
_rwLock.ExitWriteLock();
ClearTransactionDate();
_isUpdating.Reset();
}
return true;
}
public bool Rollback()
{
if (_isUpdating.State == false) return false;
ClearTransactionDate();
_isUpdating.Reset();
return true;
}
#region IDisposable
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
5 months ago
if (_collection != null!)
{
foreach (TKey key in _collection.Keys)
{
var disposable = _collection[key] as IDisposable;
5 months ago
if (disposable != null!)
disposable.Dispose();
}
_collection.Clear();
}
}
}
#endregion
}
}

Powered by TurnKey Linux.