public async Task <int> CountAsync() { // Commit any pending changes before doing a query (auto-flush) await _session.CommitAsync(); var transaction = await _session.DemandAsync(); var localBuilder = _queryState._sqlBuilder.Clone(); localBuilder.Selector("count(*)"); // Clear paging and order when counting localBuilder.ClearOrder(); localBuilder.Skip(null); localBuilder.Take(null); if (_compiledQuery != null && _queryState._parameterBindings != null) { foreach (var binding in _queryState._parameterBindings) { binding(_compiledQuery, localBuilder); } } var sql = localBuilder.ToSqlString(); var key = new WorkerQueryKey(sql, localBuilder.Parameters); return(await _session._store.ProduceAsync(key, () => { return transaction.Connection.ExecuteScalarAsync <int>(sql, localBuilder.Parameters, transaction); })); }
private async Task <Document> GetDocumentByIdAsync(int id, string collection) { await CreateConnectionAsync(); var documentTable = Store.Configuration.TableNameConvention.GetDocumentTable(collection); var command = "select * from " + _dialect.QuoteForTableName(_tablePrefix + documentTable) + " where " + _dialect.QuoteForColumnName("Id") + " = @Id"; var key = new WorkerQueryKey(nameof(GetDocumentByIdAsync), new[] { id }); try { var result = await _store.ProduceAsync(key, (state) => { var logger = state.Store.Configuration.Logger; if (logger.IsEnabled(LogLevel.Trace)) { logger.LogTrace(state.Command); } return(state.Connection.QueryAsync <Document>(state.Command, state.Parameters, state.Transaction)); }, new { Store = _store, Connection = _connection, Transaction = _transaction, Command = command, Parameters = new { Id = id } }); return(result.FirstOrDefault()); } catch { await CancelAsync(); throw; } }
public async Task <IEnumerable <T> > GetAsync <T>(int[] ids) where T : class { if (ids == null || !ids.Any()) { return(Enumerable.Empty <T>()); } CheckDisposed(); // Auto-flush await FlushAsync(); await DemandAsync(); var documentTable = CollectionHelper.Current.GetPrefixedName(YesSql.Store.DocumentTable); var command = "select * from " + _dialect.QuoteForTableName(_tablePrefix + documentTable) + " where " + _dialect.QuoteForColumnName("Id") + " " + _dialect.InOperator("@Ids"); var key = new WorkerQueryKey(nameof(GetAsync), ids); var documents = await _store.ProduceAsync(key, () => { return(_connection.QueryAsync <Document>(command, new { Ids = ids }, _transaction)); }); return(Get <T>(documents.OrderBy(d => Array.IndexOf(ids, d.Id)).ToArray())); }
public void KeysWithDifferentPrefixShouldNotBeEqual() { var key1 = new WorkerQueryKey("prefix1", new[] { 1 }); var key2 = new WorkerQueryKey("prefix2", new[] { 1 }); Assert.False(key1.Equals(key2)); }
public async Task <IEnumerable <T> > ListImpl() { // Commit any pending changes before doing a query (auto-flush) await _query._session.CommitAsync(); if (typeof(IIndex).IsAssignableFrom(typeof(T))) { _query._sqlBuilder.Selector("*"); var sql = _query._sqlBuilder.ToSqlString(_query._dialect); var key = new WorkerQueryKey(sql, _query._sqlBuilder.Parameters); return(await _query._session._store.ProduceAsync(key, () => { return _query._connection.QueryAsync <T>(sql, _query._sqlBuilder.Parameters, _query._transaction); })); } else { _query._sqlBuilder.Selector(_query._sqlBuilder.FormatColumn(_query._documentTable, "*")); var sql = _query._sqlBuilder.ToSqlString(_query._dialect); var key = new WorkerQueryKey(sql, _query._sqlBuilder.Parameters); var documents = await _query._session._store.ProduceAsync(key, () => { return(_query._connection.QueryAsync <Document>(sql, _query._sqlBuilder.Parameters, _query._transaction)); }); return(_query._session.Get <T>(documents.ToArray())); } }
protected async Task <T> FirstOrDefaultImpl() { // Commit any pending changes before doing a query (auto-flush) await _query._session.CommitAsync(); _query.Page(1, 0); if (typeof(IIndex).IsAssignableFrom(typeof(T))) { _query._sqlBuilder.Selector("*"); var sql = _query._sqlBuilder.ToSqlString(); var key = new WorkerQueryKey(sql, _query._sqlBuilder.Parameters); return((await _query._session._store.ProduceAsync(key, async() => { return await _query._connection.QueryAsync <T>(sql, _query._sqlBuilder.Parameters, _query._transaction); })).FirstOrDefault()); } else { _query._sqlBuilder.Selector(_query._documentTable, "*"); var sql = _query._sqlBuilder.ToSqlString(); var key = new WorkerQueryKey(sql, _query._sqlBuilder.Parameters); var documents = (await _query._session._store.ProduceAsync(key, async() => { return(await _query._connection.QueryAsync <Document>(sql, _query._sqlBuilder.Parameters, _query._transaction)); })).ToArray(); if (documents.Length == 0) { return(default(T)); } return(_query._session.Get <T>(documents).FirstOrDefault()); } }
/// <summary> /// Enlists some reusable logic such that not two threads run the same thing. /// </summary> /// <param name="key">A key identifying the running work.</param> /// <param name="work">A function containing the logic to execute.</param> /// <returns>The result of the work.</returns> internal async Task <T> ProduceAsync <T>(WorkerQueryKey key, Func <object[], Task <T> > work, params object[] args) { if (!Configuration.QueryGatingEnabled) { return(await work(args)); } object content = null; while (content == null) { // Is there any query already processing the ? if (!Workers.TryGetValue(key, out var result)) { // Multiple threads can potentially reach this point which is fine #if !NET451 // c.f. https://blogs.msdn.microsoft.com/seteplia/2018/10/01/the-danger-of-taskcompletionsourcet-class/ var tcs = new TaskCompletionSource <object>(TaskCreationOptions.RunContinuationsAsynchronously); #else var tcs = new TaskCompletionSource <object>(); #endif Workers.TryAdd(key, tcs.Task); try { // The current worker is processed content = await work(args); } catch { // An exception occured in the main worker, we broadcast the null value content = null; throw; } finally { // Remove the worker task before setting the result. // If the result is null, other threads would potentially // acquire it otherwise. Workers.TryRemove(key, out result); // Notify all other awaiters to return the result tcs.TrySetResult(content); } } else { // Another worker is already running, wait for it to finish and reuse the results. // This value can be null if the worker failed, in this case the loop will run again. content = await result; } } return((T)content); }
private async Task <Document> GetDocumentByIdAsync(int id) { Demand(); var command = "select * from " + _dialect.QuoteForTableName(_store.Configuration.TablePrefix + "Document") + " where " + _dialect.QuoteForColumnName("Id") + " = @Id"; var key = new WorkerQueryKey(nameof(GetDocumentByIdAsync), new [] { id }); var result = await _store.ProduceAsync(key, () => _connection.QueryAsync <Document>(command, new { Id = id }, _transaction)); return(result.FirstOrDefault()); }
public async Task <IEnumerable <T> > ListImpl() { // Flush any pending changes before doing a query (auto-flush) await _query._session.FlushAsync(); var transaction = await _query._session.DemandAsync(); if (_query._compiledQuery != null && _query._queryState._parameterBindings != null) { foreach (var binding in _query._queryState._parameterBindings) { binding(_query._compiledQuery, _query._queryState._sqlBuilder); } } if (typeof(IIndex).IsAssignableFrom(typeof(T))) { _query._queryState._sqlBuilder.Selector("*"); // If a page is requested without order add a default one if (!_query._queryState._sqlBuilder.HasOrder && _query._queryState._sqlBuilder.HasPaging) { _query._queryState._sqlBuilder.OrderBy(_query._queryState._sqlBuilder.FormatColumn(typeof(T).Name, "Id")); } var sql = _query._queryState._sqlBuilder.ToSqlString(); var key = new WorkerQueryKey(sql, _query._queryState._sqlBuilder.Parameters); return(await _query._session._store.ProduceAsync(key, () => { _query._session._store.Configuration.Logger.LogDebug(sql); return transaction.Connection.QueryAsync <T>(sql, _query._queryState._sqlBuilder.Parameters, transaction); })); } else { // If a page is requested without order add a default one if (!_query._queryState._sqlBuilder.HasOrder && _query._queryState._sqlBuilder.HasPaging) { _query._queryState._sqlBuilder.OrderBy(_query._queryState._sqlBuilder.FormatColumn(_query._queryState._documentTable, "Id")); } _query._queryState._sqlBuilder.Selector(_query._queryState._sqlBuilder.FormatColumn(_query._queryState._documentTable, "*")); var sql = _query._queryState._sqlBuilder.ToSqlString(); var key = new WorkerQueryKey(sql, _query._queryState._sqlBuilder.Parameters); var documents = await _query._session._store.ProduceAsync(key, () => { _query._session._store.Configuration.Logger.LogDebug(sql); return(transaction.Connection.QueryAsync <Document>(sql, _query._queryState._sqlBuilder.Parameters, transaction)); }); return(_query._session.Get <T>(documents.ToArray())); } }
private async Task <Document> GetDocumentByIdAsync(int id) { await DemandAsync(); var documentTable = CollectionHelper.Current.GetPrefixedName(YesSql.Store.DocumentTable); var command = "select * from " + _dialect.QuoteForTableName(_tablePrefix + documentTable) + " where " + _dialect.QuoteForColumnName("Id") + " = @Id"; var key = new WorkerQueryKey(nameof(GetDocumentByIdAsync), new [] { id }); var result = await _store.ProduceAsync(key, () => _connection.QueryAsync <Document>(command, new { Id = id }, _transaction)); return(result.FirstOrDefault()); }
/// <summary> /// Enlists some reusable logic such that not two threads run the same thing. /// </summary> /// <param name="key">A key identifying the running work.</param> /// <param name="work">A function containing the logic to execute.</param> /// <returns>The result of the work.</returns> public async Task <T> ProduceAsync <T>(WorkerQueryKey key, Func <Task <T> > work) { if (!Configuration.QueryGatingEnabled) { return(await work()); } object content = null; while (content == null) { // Is there any query already processing the ? if (!Workers.TryGetValue(key, out var result)) { // Multiple threads can potentially reach this point which is fine var tcs = new TaskCompletionSource <object>(); Workers.TryAdd(key, tcs.Task); try { // The current worker is processed content = await work(); } catch { // An exception occured in the main worker, we broadcast the null value content = null; throw; } finally { // Remove the worker task before setting the result. // If the result is null, other threads would potentially // acquire it otherwise. Workers.TryRemove(key, out result); // Notify all other awaiters to render the content tcs.TrySetResult(content); } } else { // Another worker is already running, wait for it to finish and reuse the results. // This value can be null if the worker failed, in this case the loop will run again. content = await result; } } return((T)content); }
public void KeysWithDifferentParameterNamesShouldNotBeEqual() { var params1 = new Dictionary <string, object> { ["a"] = 1, ["b"] = 1 }; var params2 = new Dictionary <string, object> { ["a"] = 1, ["c"] = 1 }; var key1 = new WorkerQueryKey("prefix1", params1); var key2 = new WorkerQueryKey("prefix1", params2); Assert.False(key1.Equals(key2)); }
public async Task <int> CountAsync() { // Commit any pending changes before doing a query (auto-flush) await _session.CommitAsync(); _sqlBuilder.Selector("count(*)"); var sql = _sqlBuilder.ToSqlString(_dialect, true); var key = new WorkerQueryKey(sql, _sqlBuilder.Parameters); return(await _session._store.ProduceAsync(key, () => { return _connection.ExecuteScalarAsync <int>(sql, _sqlBuilder.Parameters, _transaction); })); }
protected async Task <T> FirstOrDefaultImpl() { // Flush any pending changes before doing a query (auto-flush) await _query._session.FlushAsync(); var transaction = await _query._session.DemandAsync(); if (_query._compiledQuery != null && _query._queryState._parameterBindings != null) { foreach (var binding in _query._queryState._parameterBindings) { binding(_query._compiledQuery, _query._queryState._sqlBuilder); } } _query.Page(1, 0); if (typeof(IIndex).IsAssignableFrom(typeof(T))) { _query._queryState._sqlBuilder.Selector("*"); var sql = _query._queryState._sqlBuilder.ToSqlString(); var key = new WorkerQueryKey(sql, _query._queryState._sqlBuilder.Parameters); return((await _query._session._store.ProduceAsync(key, () => { _query._session._store.Configuration.Logger.LogDebug(sql); return transaction.Connection.QueryAsync <T>(sql, _query._queryState._sqlBuilder.Parameters, transaction); })).FirstOrDefault()); } else { _query._queryState._sqlBuilder.Selector(_query._queryState._documentTable, "*"); var sql = _query._queryState._sqlBuilder.ToSqlString(); var key = new WorkerQueryKey(sql, _query._queryState._sqlBuilder.Parameters); var documents = (await _query._session._store.ProduceAsync(key, () => { _query._session._store.Configuration.Logger.LogDebug(sql); return(transaction.Connection.QueryAsync <Document>(sql, _query._queryState._sqlBuilder.Parameters, transaction)); })).ToArray(); if (documents.Length == 0) { return(default(T)); } return(_query._session.Get <T>(documents).FirstOrDefault()); } }
public async Task <IEnumerable <T> > GetAsync <T>(int[] ids, string collection = null) where T : class { if (ids == null || !ids.Any()) { return(Enumerable.Empty <T>()); } CheckDisposed(); // Auto-flush await FlushAsync(); await DemandAsync(); var documentTable = Store.Configuration.TableNameConvention.GetDocumentTable(collection); var command = "select * from " + _dialect.QuoteForTableName(_tablePrefix + documentTable) + " where " + _dialect.QuoteForColumnName("Id") + " " + _dialect.InOperator("@Ids"); var key = new WorkerQueryKey(nameof(GetAsync), ids); try { var documents = await _store.ProduceAsync(key, (args) => { var localConnection = (DbConnection)args[0]; var localTransaction = (DbTransaction)args[1]; var localCommand = (string)args[2]; var localParamters = args[3]; return(localConnection.QueryAsync <Document>(localCommand, localParamters, localTransaction)); }, _connection, _transaction, command, new { Ids = ids }); return(Get <T>(documents.OrderBy(d => Array.IndexOf(ids, d.Id)).ToArray(), collection)); } catch { Cancel(); throw; } }
public async Task <IEnumerable <T> > GetAsync <T>(int[] ids, string collection = null) where T : class { if (ids == null || !ids.Any()) { return(Enumerable.Empty <T>()); } CheckDisposed(); // Auto-flush await FlushAsync(); await CreateConnectionAsync(); var documentTable = Store.Configuration.TableNameConvention.GetDocumentTable(collection); var command = "select * from " + _dialect.QuoteForTableName(_tablePrefix + documentTable) + " where " + _dialect.QuoteForColumnName("Id") + " " + _dialect.InOperator("@Ids"); var key = new WorkerQueryKey(nameof(GetAsync), ids); try { var documents = await _store.ProduceAsync(key, (state) => { var logger = state.Store.Configuration.Logger; if (logger.IsEnabled(LogLevel.Trace)) { logger.LogTrace(state.Command); } return(state.Connection.QueryAsync <Document>(state.Command, state.Parameters, state.Transaction)); }, new { Store = _store, Connection = _connection, Transaction = _transaction, Command = command, Parameters = new { Ids = ids } }); return(Get <T>(documents.OrderBy(d => Array.IndexOf(ids, d.Id)).ToArray(), collection)); } catch { await CancelAsync(); throw; } }
public void KeysWithSamePrefixShouldBeEqual() { var key1 = new WorkerQueryKey("prefix1", new[] { 1 }); var key2 = new WorkerQueryKey("prefix1", new[] { 1 }); Assert.True(key1.Equals(key2)); var params1 = new Dictionary <string, object> { ["a"] = 1, ["b"] = 1 }; var params2 = new Dictionary <string, object> { ["a"] = 1, ["b"] = 1 }; key1 = new WorkerQueryKey("prefix1", params1); key2 = new WorkerQueryKey("prefix1", params2); Assert.True(key1.Equals(key2)); }
public async Task <IEnumerable <T> > ListImpl() { // Commit any pending changes before doing a query (auto-flush) await _query._session.CommitAsync(); if (typeof(IIndex).IsAssignableFrom(typeof(T))) { _query._sqlBuilder.Selector("*"); // If a page is requested without order add a default one if (!_query._sqlBuilder.HasOrder && _query._sqlBuilder.HasPaging) { _query._sqlBuilder.OrderBy(_query._sqlBuilder.FormatColumn(typeof(T).Name, "Id")); } var sql = _query._sqlBuilder.ToSqlString(); var key = new WorkerQueryKey(sql, _query._sqlBuilder.Parameters); return(await _query._session._store.ProduceAsync(key, async() => { return await _query._connection.QueryAsync <T>(sql, _query._sqlBuilder.Parameters, _query._transaction); })); } else { // If a page is requested without order add a default one if (!_query._sqlBuilder.HasOrder && _query._sqlBuilder.HasPaging) { _query._sqlBuilder.OrderBy(_query._sqlBuilder.FormatColumn(_query._documentTable, "Id")); } _query._sqlBuilder.Selector(_query._sqlBuilder.FormatColumn(_query._documentTable, "*")); var sql = _query._sqlBuilder.ToSqlString(); var key = new WorkerQueryKey(sql, _query._sqlBuilder.Parameters); var documents = await _query._session._store.ProduceAsync(key, async() => { return(await _query._connection.QueryAsync <Document>(sql, _query._sqlBuilder.Parameters, _query._transaction)); }); return(_query._session.Get <T>(documents.ToArray())); } }
public async Task <int> CountAsync() { // Commit any pending changes before doing a query (auto-flush) await _session.CommitAsync(); _sqlBuilder.Selector("count(*)"); // Clear paging and order when counting _sqlBuilder.ClearOrder(); _sqlBuilder.Skip(null); _sqlBuilder.Take(null); var sql = _sqlBuilder.ToSqlString(); var key = new WorkerQueryKey(sql, _sqlBuilder.Parameters); return(await _session._store.ProduceAsync(key, async() => { return await _connection.ExecuteScalarAsync <int>(sql, _sqlBuilder.Parameters, _transaction); })); }
private async Task <Document> GetDocumentByIdAsync(int id) { await DemandAsync(); var documentTable = CollectionHelper.Current.GetPrefixedName(YesSql.Store.DocumentTable); var command = "select * from " + _dialect.QuoteForTableName(_tablePrefix + documentTable) + " where " + _dialect.QuoteForColumnName("Id") + " = @Id"; var key = new WorkerQueryKey(nameof(GetDocumentByIdAsync), new[] { id }); try { var result = await _store.ProduceAsync(key, (args) => { var localStore = (Store)args[0]; var localConnection = (DbConnection)args[1]; var localTransaction = (DbTransaction)args[2]; var localCommand = (string)args[3]; var localParameters = (object)args[4]; localStore.Configuration.Logger.LogTrace(localCommand); return(localConnection.QueryAsync <Document>(localCommand, localParameters, localTransaction)); }, _store, _connection, _transaction, command, new { Id = id }); return(result.FirstOrDefault()); } catch { Cancel(); throw; } }
public async Task <IEnumerable <T> > GetAsync <T>(int[] ids) where T : class { if (ids == null || !ids.Any()) { return(Enumerable.Empty <T>()); } CheckDisposed(); // Auto-flush await CommitAsync(); Demand(); var command = "select * from " + _dialect.QuoteForTableName(_store.Configuration.TablePrefix + "Document") + " where " + _dialect.QuoteForColumnName("Id") + " " + _dialect.InOperator("@Ids"); var key = new WorkerQueryKey(nameof(GetAsync), ids); var documents = await _store.ProduceAsync(key, () => { return(_connection.QueryAsync <Document>(command, new { Ids = ids }, _transaction)); }); return(Get <T>(documents.ToArray())); }