public override (IReadOnlyCollection <Row>, IReadOnlyCollection <Row>, IReadOnlyCollection <Row>) CalculateChanges() { List <Row> inserted = new List <Row>(); List <Row> updated = new List <Row>(); List <Row> deleted = new List <Row>(); Db.Config.DatabaseConnectionString.WithReader(Query, reader => { Dictionary <object[], Row> oldState = new Dictionary <object[], Row>(_data, Schema.RowKeyEqualityComparer); while (reader.Read()) { var row = Schema.ReadRow(reader); if (_data.TryGetValue(row.Key, out Row oldRow)) { oldState.Remove(row.Key); if (Schema.RowEqualityComparer.Equals(row, oldRow) == false) { updated.Add(row); _data[row.Key] = row; } } else { inserted.Add(row); _data.Add(row.Key, row); } } deleted = oldState.Values.ToList(); _data.RemoveAll(oldState.Keys); }); return(inserted, updated, deleted); }
public override IReadOnlyCollection <Row> Initialize() { Db.Config.DatabaseConnectionString.WithReader(Query, reader => { Schema = new RowSchema(reader, PrimaryKeyColumns); _data = new ReducableDictionary <object[], Row>(Schema.RowKeyEqualityComparer); while (reader.Read()) { var row = Schema.ReadRow(reader); _data.Add(row.Key, row); } }); return(_data.Values.ToArray()); }
/// <summary> /// Create a new instance of <see cref="SqlCachedQuery"/> /// </summary> /// <param name="db">Database this query belongs to</param> /// <param name="query">UserQuery that is encapsulated by this query</param> /// <param name="keyExtractor">Function to extract <typeparamref name="TKey"/> from <typeparamref name="TRow"/></param> /// <param name="keyComparerFactory">Comparer to compare <typeparamref name="TKey"/></param> /// <param name="cachingType">Type of cache used to detect changes</param> /// <param name="primaryKeyColumn">The column used as primary key</param> /// <param name="additionalPrimaryKeyColumns">Additional column names for the primary key</param> /// <param name="rowFactory">Function to transform <see cref="Row"/> into <typeparamref name="TRow"/></param> internal MappedSqlCachedQuery(Database db, string query, Func <Row, TRow> rowFactory, Func <TRow, TKey> keyExtractor, Func <RowSchema, IEqualityComparer <TKey> > keyComparerFactory, CachingType cachingType, string primaryKeyColumn, params string[] additionalPrimaryKeyColumns) : base(Guid.NewGuid()) { _rowFactory = rowFactory; _keyExtractor = keyExtractor; switch (cachingType) { case CachingType.InMemory: _view = new InMemoryCache(db, query, new[] { primaryKeyColumn }.Union(additionalPrimaryKeyColumns).ToArray()); break; case CachingType.SqlTable: _view = new SqlTableCache(db, query, new[] { primaryKeyColumn }.Union(additionalPrimaryKeyColumns).ToArray()); break; case CachingType.SqlInMemoryTable: _view = new SqlMemoryTableCache(db, query, new[] { primaryKeyColumn }.Union(additionalPrimaryKeyColumns).ToArray()); break; default: throw new ArgumentOutOfRangeException(nameof(cachingType), cachingType, null); } lock (_dataAccessLock) { _view.Invalidated += ViewOnInvalidated; _disposeHelper.Attach(() => _view.Invalidated -= ViewOnInvalidated); var data = _view.Initialize(); _rows = new ReducableDictionary <TKey, TRow>(data.Count, keyComparerFactory(_view.Schema)); foreach (var row in data) { var entry = rowFactory(row); var key = keyExtractor(entry); _rows.Add(key, entry); } _disposeHelper.Attach(_view); } }
private void ViewOnInvalidated() { var changes = _view.CalculateChanges(); lock (_dataAccessLock) { foreach (var row in changes.Item1) { var entry = _rowFactory(row); var key = _keyExtractor(entry); _rows.Add(key, entry); } foreach (var row in changes.Item2) { var entry = _rowFactory(row); var key = _keyExtractor(entry); _rows[key] = entry; } _rows.RemoveAll(changes.Item3.Select(_rowFactory).Select(_keyExtractor)); } }