using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace ZeroLevel.Services.Collections
{
///
/// Класс обертывает коллекцию вида ключ-значение и позволяет проводить над ней транзакционные обновления
///
/// Тип ключа коллекции
/// Тип значения коллекции
public class KeyListValueTransactCollection :
ITransactable
{
///
/// Коллекция
///
readonly Dictionary> _collection = new Dictionary>();
private ReaderWriterLockSlim _rwLock =
new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
///
/// Проверка наличия ключа
///
///
///
public bool HasKey(TKey key)
{
try
{
_rwLock.EnterReadLock();
return _collection.ContainsKey(key);
}
finally
{
_rwLock.ExitReadLock();
}
}
///
/// Получение значения коллекции по ключу
///
/// Ключ
/// Значение
public IEnumerable this[TKey key]
{
get
{
try
{
_rwLock.EnterReadLock();
List value;
if (_collection.TryGetValue(key, out value))
return value;
}
finally
{
_rwLock.ExitReadLock();
}
throw new KeyNotFoundException();
}
}
///
/// Коллекция ключей
///
public IEnumerable Keys
{
get
{
try
{
_rwLock.EnterReadLock();
return _collection.Keys;
}
finally
{
_rwLock.ExitReadLock();
}
}
}
///
/// Коллекция значений
///
public IEnumerable> Values
{
get
{
try
{
_rwLock.EnterReadLock();
return _collection.Values.ToArray();
}
finally
{
_rwLock.ExitReadLock();
}
}
}
#region Transaction update
///
/// Список не обновленных данных (т.е. тех которые удалены в базе)
///
readonly List _removingDate = new List();
///
/// Обновленные данные
///
readonly Dictionary> _updatedRecords = new Dictionary>();
///
/// Новые данные
///
readonly Dictionary> _newRecords = new Dictionary>();
void ClearTransactionDate()
{
_removingDate.Clear();
_updatedRecords.Clear();
_newRecords.Clear();
}
///
/// Добавление или обновления записи
///
/// Идентификатор записи
/// Значение
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, new List());
}
_newRecords[id].Add(value);
}
else
{
if (!_updatedRecords.ContainsKey(id))
{
_updatedRecords.Add(id, new List());
}
_updatedRecords[id].Add(value);
if (_removingDate.Contains(id))
_removingDate.Remove(id);
}
return;
}
readonly AtomicBoolean _isUpdating = new AtomicBoolean();
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;
}
public bool StartTransction()
{
if (_isUpdating.Set())
{
_removingDate.AddRange(_collection.Keys.ToArray());
return true;
}
return false;
}
#endregion
#region IDisposable
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_collection != null)
{
_collection.Clear();
}
}
}
#endregion
}
}