using System; using System.Collections.Generic; using System.Linq; using System.Threading; namespace ZeroLevel.Services.Collections { /// /// Класс обертывает коллекцию вида ключ-значение и позволяет проводить над ней транзакционные обновления /// /// Тип ключа коллекции /// Тип значения коллекции public class KeyValueTransactCollection : ITransactable { private ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); /// /// Коллекция /// readonly Dictionary _collection = new Dictionary(); public KeyValueTransactCollection() { } /// /// Проверка наличия ключа /// /// /// public bool HasKey(TKey key) { try { _rwLock.EnterReadLock(); return _collection.ContainsKey(key); } finally { _rwLock.ExitReadLock(); } } /// /// Получение значения коллекции по ключу /// /// Ключ /// Значение 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(); } } /// /// Количество записей /// public int Count { get { try { _rwLock.EnterReadLock(); return _collection.Count; } finally { _rwLock.ExitReadLock(); } } } /// /// Коллекция ключей /// public IEnumerable Keys { get { try { _rwLock.EnterReadLock(); return _collection.Keys; } finally { _rwLock.ExitReadLock(); } } } /// /// Список ключ-значений /// public IEnumerable> Items { get { try { _rwLock.EnterReadLock(); return _collection; } finally { _rwLock.ExitReadLock(); } } } /// /// Коллекция значений /// public IEnumerable Values { get { try { _rwLock.EnterReadLock(); return _collection.Values; } 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, value); } else { _newRecords[id] = value; } } else { 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) { if (_collection != null) { foreach (TKey key in _collection.Keys) { var disposable = _collection[key] as IDisposable; if (disposable != null) disposable.Dispose(); } _collection.Clear(); } } } #endregion } }