async Task <BotData> IBotDataStore <BotData> .LoadAsync(BotDataKey key, BotStoreType botStoreType, CancellationToken cancellationToken) { BotData botData; switch (botStoreType) { case BotStoreType.BotConversationData: botData = await stateClient.BotState.GetConversationDataAsync(key.ChannelId, key.ConversationId, cancellationToken); break; case BotStoreType.BotUserData: botData = await stateClient.BotState.GetUserDataAsync(key.ChannelId, key.UserId, cancellationToken); break; case BotStoreType.BotPrivateConversationData: botData = await stateClient.BotState.GetPrivateConversationDataAsync(key.ChannelId, key.ConversationId, key.UserId, cancellationToken); break; default: throw new ArgumentException($"{botStoreType} is not a valid store type!"); } return(botData); }
async Task <object> IBotDataStore <object> .LoadAsync(BotDataKey key, BotStoreType botStoreType, CancellationToken cancellationToken) { DataEntry entry; object obj = null; if (!cache.TryGetValue(key, out entry)) { entry = new DataEntry(); cache[key] = entry; BotData value = await inner.LoadAsync(key, botStoreType, cancellationToken); if (value?.Data != null) { obj = value.Data; } SetValue(entry, botStoreType, obj); return(obj); } else { switch (botStoreType) { case BotStoreType.BotConversationData: if (entry.BotConversationData != null) { obj = entry.BotConversationData; } break; case BotStoreType.BotPrivateConversationData: if (entry.BotPrivateConversationData != null) { obj = entry.BotPrivateConversationData; } break; case BotStoreType.BotUserData: if (entry.BotUserData != null) { obj = entry.BotUserData; } break; } if (obj == null) { BotData value = await inner.LoadAsync(key, botStoreType, cancellationToken); if (value?.Data != null) { obj = value.Data; SetValue(entry, botStoreType, obj); } } return(obj); } }
public Task SaveAsync(BotDataKey key, BotStoreType botStoreType, object value) { try { if (!ValidateKey(key)) { throw new ArgumentException("Invalid bot data key!"); } switch (botStoreType) { case BotStoreType.BotConversationData: message.BotConversationData = value; break; case BotStoreType.BotPerUserInConversationData: message.BotPerUserInConversationData = value; break; case BotStoreType.BotUserData: message.BotUserData = value; break; default: throw new ArgumentException($"key {botStoreType} is not supported by this message store"); } return(Task.FromResult(Type.Missing)); } catch (Exception e) { return(Task.FromException(e)); } }
public async Task <T> LoadAsync <T>(BotDataKey key, BotStoreType botStoreType) { DataEntry entry; T obj = default(T); if (!cache.TryGetValue(key, out entry)) { entry = new DataEntry(); cache[key] = entry; string value; if (store.TryGetValue(GetKey(key, botStoreType), out value)) { obj = (T)JsonConvert.DeserializeObject(value); } SetValue(entry, botStoreType, obj); return(obj); } else { switch (botStoreType) { case BotStoreType.BotConversationData: if (entry.BotConversationData != null) { obj = (T)entry.BotConversationData; } break; case BotStoreType.BotPerUserInConversationData: if (entry.BotPerUserInConversationData != null) { obj = (T)entry.BotPerUserInConversationData; } break; case BotStoreType.BotUserData: if (entry.BotUserData != null) { obj = (T)entry.BotUserData; } break; } if (obj == null) { string value; if (store.TryGetValue(GetKey(key, botStoreType), out value)) { obj = (T)JsonConvert.DeserializeObject(value); SetValue(entry, botStoreType, obj); } } return(obj); } }
async Task <BotData> IBotDataStore <BotData> .LoadAsync(BotDataKey key, BotStoreType botStoreType, CancellationToken cancellationToken) { string serializedData; serializedData = store.GetOrAdd(GetKey(key, botStoreType), dictionaryKey => Serialize(new BotData { ETag = DateTime.UtcNow.ToString() })); return(Deserialize(serializedData)); }
public async Task SaveAsync(BotDataKey key, BotStoreType botStoreType, object value) { DataEntry entry; if (!cache.TryGetValue(key, out entry)) { entry = new DataEntry(); cache[key] = entry; } SetValue(entry, botStoreType, value); }
async Task IBotDataStore <object> .SaveAsync(BotDataKey key, BotStoreType botStoreType, object value, CancellationToken cancellationToken) { DataEntry entry; if (!cache.TryGetValue(key, out entry)) { entry = new DataEntry(); cache[key] = entry; } SetValue(entry, botStoreType, value); }
async Task IBotDataStore <BotData> .SaveAsync(BotDataKey key, BotStoreType botStoreType, BotData value, CancellationToken cancellationToken) { CacheEntry entry; if (!cache.TryGetValue(key, out entry)) { entry = new CacheEntry(); cache.Add(key, entry); } SetCachedValue(entry, botStoreType, value); }
public async Task <bool> FlushAsync(BotDataKey key) { DataEntry entry = default(DataEntry); if (cache.TryRemove(key, out entry)) { this.Save(key, entry); return(true); } else { return(false); } }
async Task IBotDataStore <BotData> .SaveAsync(BotDataKey key, BotStoreType botStoreType, BotData botData, CancellationToken cancellationToken) { lock (locks[botStoreType]) { store.AddOrUpdate(GetKey(key, botStoreType), JsonConvert.SerializeObject(botData), (dictionaryKey, value) => { if (botData.ETag != "*" && JsonConvert.DeserializeObject <BotData>(value).ETag != botData.ETag) { throw new HttpException((int)HttpStatusCode.PreconditionFailed, "Inconsistent SaveAsync based on Etag!"); } botData.ETag = DateTime.UtcNow.ToString(); return(Serialize(botData)); }); } }
async Task <bool> IBotDataStore <object> .FlushAsync(BotDataKey key, CancellationToken cancellationToken) { DataEntry entry = default(DataEntry); if (cache.TryGetValue(key, out entry)) { cache.Remove(key); await this.Save(key, entry, cancellationToken); return(true); } else { return(false); } }
private async Task Save(BotDataKey key, CacheEntry entry, CancellationToken cancellationToken) { var tasks = new List <Task>(); switch (this.dataConsistencyPolicy) { case CachingBotDataStoreConsistencyPolicy.LastWriteWins: if (entry?.BotConversationData != null) { entry.BotConversationData.ETag = "*"; } if (entry?.BotUserData != null) { entry.BotUserData.ETag = "*"; } if (entry?.BotPrivateConversationData != null) { entry.BotPrivateConversationData.ETag = "*"; } break; case CachingBotDataStoreConsistencyPolicy.ETagBasedConsistency: // no action needed, store relies on the ETags returned by inner store break; default: throw new ArgumentException($"{this.dataConsistencyPolicy} is not a valid consistency policy!"); } if (entry?.BotConversationData != null) { tasks.Add(inner.SaveAsync(key, BotStoreType.BotConversationData, entry.BotConversationData, cancellationToken)); } if (entry?.BotUserData != null) { tasks.Add(inner.SaveAsync(key, BotStoreType.BotUserData, entry.BotUserData, cancellationToken)); } if (entry?.BotPrivateConversationData != null) { tasks.Add(inner.SaveAsync(key, BotStoreType.BotPrivateConversationData, entry.BotPrivateConversationData, cancellationToken)); } await Task.WhenAll(tasks); }
private string GetKey(BotDataKey key, BotStoreType botStoreType) { switch (botStoreType) { case BotStoreType.BotConversationData: return($"conversation:{key.BotId}:{key.ConversationId}"); case BotStoreType.BotUserData: return($"user:{key.BotId}:{key.UserId}"); case BotStoreType.BotPerUserInConversationData: return($"perUserInConversation:{key.BotId}:{key.UserId}:{key.ConversationId}"); default: throw new ArgumentException("Unsupported bot store type!"); } }
private void Save(BotDataKey key, DataEntry entry) { if (entry?.BotConversationData != null) { store[GetKey(key, BotStoreType.BotConversationData)] = JsonConvert.SerializeObject(entry.BotConversationData); } if (entry?.BotUserData != null) { store[GetKey(key, BotStoreType.BotUserData)] = JsonConvert.SerializeObject(entry.BotUserData); } if (entry?.BotPerUserInConversationData != null) { store[GetKey(key, BotStoreType.BotPerUserInConversationData)] = JsonConvert.SerializeObject(entry.BotPerUserInConversationData); } }
private async Task Save(BotDataKey key, DataEntry entry, CancellationToken cancellationToken) { if (entry?.BotConversationData != null) { await inner.SaveAsync(key, BotStoreType.BotConversationData, new BotData { ETag = "*", Data = entry.BotConversationData }, cancellationToken); } if (entry?.BotUserData != null) { await inner.SaveAsync(key, BotStoreType.BotUserData, new BotData { ETag = "*", Data = entry.BotUserData }, cancellationToken); } if (entry?.BotPrivateConversationData != null) { await inner.SaveAsync(key, BotStoreType.BotPrivateConversationData, new BotData { ETag = "*", Data = entry.BotPrivateConversationData }, cancellationToken); } }
async Task <BotData> IBotDataStore <BotData> .LoadAsync(BotDataKey key, BotStoreType botStoreType, CancellationToken cancellationToken) { CacheEntry cacheEntry; BotData value = null; if (!cache.TryGetValue(key, out cacheEntry)) { cacheEntry = new CacheEntry(); cache.Add(key, cacheEntry); value = await LoadFromInnerAndCache(cacheEntry, botStoreType, key, cancellationToken); } else { switch (botStoreType) { case BotStoreType.BotConversationData: if (cacheEntry.BotConversationData != null) { value = cacheEntry.BotConversationData; } break; case BotStoreType.BotPrivateConversationData: if (cacheEntry.BotPrivateConversationData != null) { value = cacheEntry.BotPrivateConversationData; } break; case BotStoreType.BotUserData: if (cacheEntry.BotUserData != null) { value = cacheEntry.BotUserData; } break; } if (value == null) { value = await LoadFromInnerAndCache(cacheEntry, botStoreType, key, cancellationToken); } } return(value); }
async Task <bool> IBotDataStore <BotData> .FlushAsync(BotDataKey key, CancellationToken cancellationToken) { CacheEntry entry = default(CacheEntry); if (cache.TryGetValue(key, out entry)) { // Removing the cached entry to make sure that we are not leaking // flushed entries when CachingBotDataStore is registered as a singleton object. // Also since this store is not updating ETags on LoadAsync(...), there // will be a conflict if we reuse the cached entries after flush. cache.Remove(key); await this.Save(key, entry, cancellationToken); return(true); } else { return(false); } }
public Task <T> LoadAsync <T>(BotDataKey key, BotStoreType botStoreType) { try { if (!ValidateKey(key)) { throw new ArgumentException("Invalid bot data key!"); } var value = default(T); switch (botStoreType.ToString()) { case nameof(message.BotConversationData): value = (T)message.BotConversationData; break; case nameof(message.BotPerUserInConversationData): value = (T)message.BotPerUserInConversationData; break; case nameof(message.BotUserData): value = (T)message.BotUserData; break; default: value = default(T); break; } return(Task.FromResult(value)); } catch (Exception e) { return(Task.FromException <T>(e)); } }
public BotDataBase(Message message, IBotDataStore botDataStore) { SetField.NotNull(out this.botDataStore, nameof(BotDataBase <T> .botDataStore), botDataStore); SetField.CheckNull(nameof(message), message); this.botDataKey = message.GetBotDataKey(); }
private async Task <BotData> LoadFromInnerAndCache(CacheEntry cacheEntry, BotStoreType botStoreType, BotDataKey key, CancellationToken token) { var value = await inner.LoadAsync(key, botStoreType, token); if (value != null) { SetCachedValue(cacheEntry, botStoreType, value); } else { // inner store returned null, we create a new instance of BotData with ETag = "*" value = new BotData() { ETag = "*" }; SetCachedValue(cacheEntry, botStoreType, value); } return(value); }
Task <bool> IBotDataStore <BotData> .FlushAsync(BotDataKey key, CancellationToken cancellationToken) { // Everything is saved. Flush is no-op return(Task.FromResult(true)); }
public BotDataBase(IBotIdResolver botIdResolver, IMessageActivity message, IBotDataStore botDataStore) { SetField.NotNull(out this.botDataStore, nameof(BotDataBase <T> .botDataStore), botDataStore); SetField.CheckNull(nameof(message), message); this.botDataKey = message.GetBotDataKey(botIdResolver.BotId); }
private bool ValidateKey(BotDataKey key) { return(key.UserId == message.From?.Id && key.BotId == message.To?.Id && key.ConversationId == message.ConversationId); }
public Task <bool> FlushAsync(BotDataKey key) { // no-op for message backed store return(Task.FromResult(ValidateKey(key))); }