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 } }