Пример #1
0
        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);
            }));
        }
Пример #2
0
        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;
            }
        }
Пример #3
0
        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()));
        }
Пример #4
0
        public void KeysWithDifferentPrefixShouldNotBeEqual()
        {
            var key1 = new WorkerQueryKey("prefix1", new[] { 1 });
            var key2 = new WorkerQueryKey("prefix2", new[] { 1 });

            Assert.False(key1.Equals(key2));
        }
Пример #5
0
            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()));
                }
            }
Пример #6
0
            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());
                }
            }
Пример #7
0
        /// <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);
        }
Пример #8
0
        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());
        }
Пример #9
0
            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()));
                }
            }
Пример #10
0
        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());
        }
Пример #11
0
        /// <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);
        }
Пример #12
0
        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));
        }
Пример #13
0
        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);
            }));
        }
Пример #14
0
            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());
                }
            }
Пример #15
0
        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;
            }
        }
Пример #16
0
        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;
            }
        }
Пример #17
0
        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));
        }
Пример #18
0
            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()));
                }
            }
Пример #19
0
        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);
            }));
        }
Пример #20
0
        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;
            }
        }
Пример #21
0
        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()));
        }