public async Task SchedulePostAsync(Post post, IReadOnlyList <PostFile> postFiles, DateTime?scheduledPostDate, DateTime now)
        {
            post.AssertNotNull("post");

            if (post.QueueId.HasValue)
            {
                throw new ArgumentException("Queue ID should not exist", "post");
            }

            if (scheduledPostDate.HasValue)
            {
                now.AssertUtc("scheduledPostDate");
            }

            now.AssertUtc("now");

            post.LiveDate = this.scheduledDateClipping.Apply(now, scheduledPostDate);

            using (var transaction = TransactionScopeBuilder.CreateAsync())
            {
                // The order we access tables should match RevisePostDbStatement.
                using (var connection = this.connectionFactory.CreateConnection())
                {
                    await connection.InsertAsync(post);

                    await connection.InsertAsync(postFiles);
                }

                transaction.Complete();
            }
        }
        public async Task ExecuteAsync(
            UserId userId,
            ChannelId channelId,
            ValidChannelName name,
            ValidChannelPrice price,
            bool isVisibleToNonSubscribers,
            DateTime now)
        {
            using (var transaction = TransactionScopeBuilder.CreateAsync())
            {
                using (var connection = this.connectionFactory.CreateConnection())
                {
                    await connection.ExecuteAsync(
                        UpdateStatement,
                        new
                    {
                        Id = channelId.Value,
                        IsVisibleToNonSubscribers = isVisibleToNonSubscribers,
                        Name             = name.Value,
                        Price            = price.Value,
                        PriceLastSetDate = now
                    });
                }

                await this.requestSnapshot.ExecuteAsync(userId, SnapshotType.CreatorChannels);

                transaction.Complete();
            }
        }
Beispiel #3
0
        private async Task CreateQueueAsync(CreateQueueCommand command)
        {
            var queue = new Queue(
                command.NewQueueId.Value,
                command.BlogId.Value,
                null,
                command.Name.Value,
                DateTime.UtcNow);

            var releaseDate = new WeeklyReleaseTime(
                command.NewQueueId.Value,
                null,
                (byte)command.InitialWeeklyReleaseTime.Value);

            // Assuming no lock escalation, this transaction will hold X locks on the new rows and IX locks further up the hierarchy,
            // so no deadlocks are to be expected.
            using (var transaction = TransactionScopeBuilder.CreateAsync())
            {
                using (var connection = this.connectionFactory.CreateConnection())
                {
                    await connection.InsertAsync(queue);

                    await connection.InsertAsync(releaseDate);
                }

                transaction.Complete();
            }
        }
Beispiel #4
0
        private async Task CreateChannelAsync(UserId userId, CreateChannelCommand command)
        {
            var now     = DateTime.UtcNow;
            var channel = new Channel(
                command.NewChannelId.Value,
                command.BlogId.Value,
                null,
                command.Name.Value,
                command.Price.Value,
                command.IsVisibleToNonSubscribers,
                now,
                now,
                false);


            using (var transaction = TransactionScopeBuilder.CreateAsync())
            {
                using (var connection = this.connectionFactory.CreateConnection())
                {
                    await connection.InsertAsync(channel);
                }

                await this.requestSnapshot.ExecuteAsync(userId, SnapshotType.CreatorChannels);

                transaction.Complete();
            }
        }
        public async Task ExecuteAsync(QueueId queueId, WeeklyReleaseSchedule weeklyReleaseSchedule)
        {
            queueId.AssertNotNull("queueId");
            weeklyReleaseSchedule.AssertNotNull("weeklyReleaseSchedule");

            var newWeeklyReleaseTimes = weeklyReleaseSchedule.Value.Select(
                _ => new WeeklyReleaseTime(queueId.Value, null, (byte)_.Value));

            var deletionParameters = new
            {
                QueueId = queueId.Value
            };

            // Transaction required on the following, as database must always contain at least one weekly release time per
            // collection. The absence of weekly release times would cause a breaking inconsistency.
            using (var transaction = TransactionScopeBuilder.CreateAsync())
            {
                using (var connection = this.connectionFactory.CreateConnection())
                {
                    await connection.ExecuteAsync(DeleteWeeklyReleaseTimesSql, deletionParameters);

                    await connection.InsertAsync(newWeeklyReleaseTimes);
                }

                transaction.Complete();
            }
        }
Beispiel #6
0
        public async Task ExecuteAsync(PostId postId, DateTime now, Func <Task> potentialRemovalOperation)
        {
            postId.AssertNotNull("postId");
            now.AssertUtc("now");
            potentialRemovalOperation.AssertNotNull("potentialRemovalOperation");

            var queuedCollectionId = await this.tryGetPostQueueId.ExecuteAsync(postId, now);

            if (queuedCollectionId == null)
            {
                await potentialRemovalOperation();
            }
            else
            {
                var weeklyReleaseSchedule = await this.getWeeklyReleaseSchedule.ExecuteAsync(queuedCollectionId);

                using (var transaction = TransactionScopeBuilder.CreateAsync())
                {
                    await potentialRemovalOperation();

                    await this.defragmentQueue.ExecuteAsync(queuedCollectionId, weeklyReleaseSchedule, now);

                    transaction.Complete();
                }
            }
        }
Beispiel #7
0
        public async Task ExecuteAsync(
            UserId userId,
            Username username,
            Email email,
            string exampleWork,
            Password password,
            DateTime timeStamp)
        {
            userId.AssertNotNull("userId");
            username.AssertNotNull("username");
            email.AssertNotNull("email");
            password.AssertNotNull("password");

            var passwordHash = this.userManager.PasswordHasher.HashPassword(password.Value);

            var user = new FifthweekUser
            {
                Id                  = userId.Value,
                UserName            = username.Value,
                Email               = email.Value,
                ExampleWork         = exampleWork,
                RegistrationDate    = timeStamp,
                LastSignInDate      = SqlDateTime.MinValue.Value,
                LastAccessTokenDate = SqlDateTime.MinValue.Value,
                SecurityStamp       = Guid.NewGuid().ToString(),
                PasswordHash        = passwordHash,
            };

            var parameters = new SqlGenerationParameters <FifthweekUser, FifthweekUser.Fields>(user)
            {
                Conditions = new[]
                {
                    WhereUsernameNotTaken,
                    WhereEmailNotTaken
                }
            };

            using (var transaction = TransactionScopeBuilder.CreateAsync())
            {
                using (var connection = this.connectionFactory.CreateConnection())
                {
                    var result = await connection.InsertAsync(parameters);

                    switch (result)
                    {
                    case 0: throw new RecoverableException("The username '" + username.Value + "' is already taken.");

                    case 1: throw new RecoverableException("The email address '" + email.Value + "' is already taken.");
                    }
                }

                await this.requestSnapshot.ExecuteAsync(userId, SnapshotType.Subscriber);

                transaction.Complete();
            }
        }
Beispiel #8
0
        protected async Task DatabaseTestAsync(Func <TestDatabaseContext, Task <ExpectedSideEffects> > databaseTest)
        {
            var database = await TestDatabase.CreateNewAsync();

            var databaseSnapshot = new TestDatabaseSnapshot(database);
            var databaseContext  = new TestDatabaseContext(database, databaseSnapshot);

            using (TransactionScopeBuilder.CreateAsync())
            {
                var sideEffects = await databaseTest(databaseContext);

                await databaseSnapshot.AssertSideEffectsAsync(sideEffects);
            }
        }
Beispiel #9
0
        public async Task ExecuteAsync(UserId userId, BlogId blogId, IReadOnlyList <AcceptedChannelSubscription> channels, DateTime now)
        {
            userId.AssertNotNull("userId");
            blogId.AssertNotNull("blogId");

            if (channels == null || channels.Count == 0)
            {
                channels = new List <AcceptedChannelSubscription>();
            }

            using (var transaction = TransactionScopeBuilder.CreateAsync())
            {
                using (var connection = this.connectionFactory.CreateConnection())
                {
                    await connection.ExecuteAsync(
                        DeleteStatement,
                        new
                    {
                        BlogId         = blogId.Value,
                        SubscriberId   = userId.Value,
                        KeepChannelIds = channels.Select(v => v.ChannelId.Value).ToList()
                    });

                    foreach (var item in channels)
                    {
                        var channelSubscription = new ChannelSubscription(
                            item.ChannelId.Value,
                            null,
                            userId.Value,
                            null,
                            item.AcceptedPrice.Value,
                            now,
                            now);

                        const ChannelSubscription.Fields UpdateFields
                            = ChannelSubscription.Fields.AcceptedPrice
                              | ChannelSubscription.Fields.PriceLastAcceptedDate;

                        await connection.UpsertAsync(
                            channelSubscription,
                            UpdateFields);
                    }
                }

                await this.requestSnapshot.ExecuteAsync(userId, SnapshotType.SubscriberChannels);

                transaction.Complete();
            }
        }
Beispiel #10
0
        public async Task ExecuteAsync(
            UserId subscriberId,
            UserId creatorId,
            IReadOnlyList <AppendOnlyLedgerRecord> ledgerRecords,
            UncommittedSubscriptionPayment uncommittedRecord)
        {
            subscriberId.AssertNotNull("subscriberId");
            creatorId.AssertNotNull("creatorId");
            ledgerRecords.AssertNotNull("ledgerRecords");

            using (PaymentsPerformanceLogger.Instance.Log(typeof(PersistCommittedAndUncommittedRecordsDbStatement)))
                using (var transaction = TransactionScopeBuilder.CreateAsync())
                {
                    using (var connection = this.connectionFactory.CreateConnection())
                    {
                        if (ledgerRecords.Any())
                        {
                            await connection.InsertAsync(ledgerRecords, false);
                        }

                        if (uncommittedRecord != null)
                        {
                            var uncommittedFields = UncommittedSubscriptionPayment.Fields.StartTimestampInclusive
                                                    | UncommittedSubscriptionPayment.Fields.EndTimestampExclusive
                                                    | UncommittedSubscriptionPayment.Fields.Amount
                                                    | UncommittedSubscriptionPayment.Fields.InputDataReference;

                            await connection.UpsertAsync(uncommittedRecord, uncommittedFields);
                        }
                        else
                        {
                            // Remove the existing uncommitted record.
                            await connection.ExecuteAsync(
                                string.Format(
                                    @"DELETE FROM {0} WHERE {1}=@SubscriberId AND {2}=@CreatorId",
                                    UncommittedSubscriptionPayment.Table,
                                    UncommittedSubscriptionPayment.Fields.SubscriberId,
                                    UncommittedSubscriptionPayment.Fields.CreatorId),
                                new
                            {
                                SubscriberId = subscriberId.Value,
                                CreatorId    = creatorId.Value
                            });
                        }
                    }

                    transaction.Complete();
                }
        }
        public async Task ExecuteAsync(QueueId queueId, WeeklyReleaseSchedule weeklyReleaseSchedule, DateTime now)
        {
            queueId.AssertNotNull("queueId");
            weeklyReleaseSchedule.AssertNotNull("weeklyReleaseSchedule");
            now.AssertUtc("now");

            // Transaction required on the following, as we don't want user to see a queue that does not match the collection's release times.
            using (var transaction = TransactionScopeBuilder.CreateAsync())
            {
                await this.replaceWeeklyReleaseTimes.ExecuteAsync(queueId, weeklyReleaseSchedule);

                await this.defragmentQueue.ExecuteAsync(queueId, weeklyReleaseSchedule, now);

                transaction.Complete();
            }
        }
Beispiel #12
0
        public async Task ExecuteAsync(UserId userId, ChannelId channelId)
        {
            userId.AssertNotNull("userId");
            channelId.AssertNotNull("channelId");

            using (var transaction = TransactionScopeBuilder.CreateAsync())
            {
                using (var connection = this.connectionFactory.CreateConnection())
                {
                    await connection.ExecuteAsync(DeleteStatement, new { ChannelId = channelId.Value, UserId = userId.Value });
                }

                await this.requestSnapshot.ExecuteAsync(userId, SnapshotType.SubscriberChannels);

                transaction.Complete();
            }
        }
Beispiel #13
0
        private async Task CreateEntitiesAsync(CreateBlogCommand command, UserId authenticatedUserId)
        {
            var now  = DateTime.UtcNow;
            var blog = new Blog(
                command.NewBlogId.Value,
                authenticatedUserId.Value,
                null,
                command.BlogName.Value,
                null,
                null,
                null,
                null,
                null,
                now);

            var channel = new Channel(
                command.FirstChannelId.Value,
                command.NewBlogId.Value,
                null,
                command.BlogName.Value,
                command.BasePrice.Value,
                true,
                now,
                now,
                false);

            // Assuming no lock escalation, this transaction will hold X locks on the new rows and IX locks further up the hierarchy,
            // so no deadlocks are to be expected.
            using (var transaction = TransactionScopeBuilder.CreateAsync())
            {
                using (var connection = this.connectionFactory.CreateConnection())
                {
                    await connection.InsertAsync(blog);

                    await connection.InsertAsync(channel);
                }

                await this.requestSnapshot.ExecuteAsync(authenticatedUserId, SnapshotType.CreatorChannels);

                transaction.Complete();
            }
        }
Beispiel #14
0
        public async Task ExecuteAsync(UserId userId, BlogId blogId, IReadOnlyList <ValidEmail> emails)
        {
            userId.AssertNotNull("userId");
            blogId.AssertNotNull("blogId");

            if (emails == null)
            {
                emails = new List <ValidEmail>();
            }

            using (var transaction = TransactionScopeBuilder.CreateAsync())
            {
                using (var connection = this.connectionFactory.CreateConnection())
                {
                    if (emails.Count == 0)
                    {
                        await connection.ExecuteAsync(
                            DeleteQuery,
                            new { BlogId = blogId.Value });
                    }
                    else
                    {
                        await connection.ExecuteAsync(
                            DeleteQuery,
                            new { BlogId = blogId.Value });

                        await connection.ExecuteAsync(
                            AddQuery,
                            emails.Select(v => new { BlogId = blogId.Value, Email = v.Value }).ToList());
                    }
                }

                await this.requestSnapshot.ExecuteAsync(userId, SnapshotType.CreatorFreeAccessUsers);

                transaction.Complete();
            }
        }
Beispiel #15
0
        public async Task ExecuteAsync(
            UserId userId,
            ChannelId channelId,
            ValidAcceptedChannelPrice acceptedPrice,
            DateTime now)
        {
            userId.AssertNotNull("userId");
            channelId.AssertNotNull("channelId");
            acceptedPrice.AssertNotNull("acceptedPrice");

            using (var transaction = TransactionScopeBuilder.CreateAsync())
            {
                using (var connection = this.connectionFactory.CreateConnection())
                {
                    var channelSubscription = new ChannelSubscription(
                        channelId.Value,
                        null,
                        userId.Value,
                        null,
                        acceptedPrice.Value,
                        now,
                        now);

                    const ChannelSubscription.Fields UpdateFields
                        = ChannelSubscription.Fields.AcceptedPrice
                          | ChannelSubscription.Fields.PriceLastAcceptedDate;

                    await connection.UpsertAsync(
                        channelSubscription,
                        UpdateFields);
                }

                await this.requestSnapshot.ExecuteAsync(userId, SnapshotType.SubscriberChannels);

                transaction.Complete();
            }
        }
        public async Task QueuePostAsync(Post post, IReadOnlyList <PostFile> postFiles)
        {
            post.AssertNotNull("post");

            if (!post.QueueId.HasValue)
            {
                throw new ArgumentException("Queue ID required", "post");
            }

            var queueId      = new QueueId(post.QueueId.Value);
            var nextLiveDate = await this.getLiveDateOfNewQueuedPost.ExecuteAsync(queueId);

            post.LiveDate = nextLiveDate;

            var parameters = new SqlGenerationParameters <Post, Post.Fields>(post)
            {
                Conditions = new[] { WherePostLiveDateUniqueToQueue }
            };

            using (var transaction = TransactionScopeBuilder.CreateAsync())
            {
                // The order we access tables should match RevisePostDbStatement.
                using (var connection = this.connectionFactory.CreateConnection())
                {
                    var success = -1 == await connection.InsertAsync(parameters);

                    if (!success)
                    {
                        throw new OptimisticConcurrencyException(string.Format("Failed to optimistically queue post to channel {0}", post.ChannelId));
                    }

                    await connection.InsertAsync(postFiles);
                }

                transaction.Complete();
            }
        }
Beispiel #17
0
        public async Task ExecuteAsync(
            PostId postId,
            ValidComment content,
            ValidPreviewText previewText,
            FileId previewImageId,
            IReadOnlyList <FileId> fileIds,
            int previewWordCount,
            int wordCount,
            int imageCount,
            int fileCount,
            int videoCount)
        {
            postId.AssertNotNull("postId");
            fileIds.AssertNotNull("fileIds");

            var post = new Post(postId.Value)
            {
                // QueueId = command.QueueId.Value, - Removed as this would require a queue defragmentation if post is already queued. Unnecessary complexity for MVP.
                PreviewText      = previewText == null ? null : previewText.Value,
                PreviewImageId   = previewImageId == null ? (Guid?)null : previewImageId.Value,
                Content          = content == null ? null : content.Value,
                PreviewWordCount = previewWordCount,
                WordCount        = wordCount,
                ImageCount       = imageCount,
                FileCount        = fileCount,
                VideoCount       = videoCount
            };

            var postFiles = fileIds.Select(v => new PostFile(postId.Value, v.Value)).ToList();

            using (var transaction = TransactionScopeBuilder.CreateAsync())
            {
                using (var connection = this.connectionFactory.CreateConnection())
                {
                    // The order we access tables should match PostToChannelDbStatement.
                    var rowsUpdated = await connection.UpdateAsync(
                        post,
                        Post.Fields.PreviewText
                        | Post.Fields.PreviewImageId
                        | Post.Fields.Content
                        | Post.Fields.PreviewWordCount
                        | Post.Fields.WordCount
                        | Post.Fields.ImageCount
                        | Post.Fields.FileCount
                        | Post.Fields.VideoCount);

                    if (rowsUpdated > 0)
                    {
                        await connection.ExecuteAsync(
                            DeleteQuery,
                            new { PostId = postId.Value });

                        if (fileIds.Count > 0)
                        {
                            await connection.InsertAsync(postFiles);
                        }
                    }
                }

                transaction.Complete();
            }
        }
        public async Task <UpdateAccountSettingsResult> ExecuteAsync(
            UserId userId,
            ValidUsername newUsername,
            ValidEmail newEmail,
            ValidPassword newPassword,
            FileId newProfileImageFileId,
            string newSecurityStamp)
        {
            userId.AssertNotNull("userId");
            newUsername.AssertNotNull("newUsername");
            newEmail.AssertNotNull("newEmail");

            string passwordHash = null;

            if (newPassword != null)
            {
                passwordHash = this.userManager.PasswordHasher.HashPassword(newPassword.Value);
            }

            var query = new StringBuilder();

            query.AppendLine(@"DECLARE @emailConfirmed bit");

            query.Append(@"
                UPDATE dbo.AspNetUsers 
                SET 
                    Email = @Email, 
                    UserName = @Username, 
                    ProfileImageFileId = @ProfileImageFileId,
                    SecurityStamp = @SecurityStamp,
                    @emailConfirmed =
                    (
                        CASE
                            WHEN 
                                ((EmailConfirmed = 0) OR (Email != @Email))
                            THEN
                                0
                            ELSE
                                1
                        END
                    ),
                    EmailConfirmed = @emailConfirmed");

            if (passwordHash != null)
            {
                query.Append(@", PasswordHash=@PasswordHash");
            }

            query.AppendLine().Append(@"WHERE Id=@UserId").AppendLine();

            query.Append(@"select @emailConfirmed");

            bool emailConfirmed;

            using (var transaction = TransactionScopeBuilder.CreateAsync())
            {
                using (var connection = this.connectionFactory.CreateConnection())
                {
                    emailConfirmed = await connection.ExecuteScalarAsync <bool>(
                        query.ToString(),
                        new
                    {
                        UserId             = userId.Value,
                        Username           = newUsername.Value,
                        Email              = newEmail.Value,
                        PasswordHash       = passwordHash,
                        SecurityStamp      = newSecurityStamp,
                        ProfileImageFileId = newProfileImageFileId == null ? (Guid?)null : newProfileImageFileId.Value
                    });
                }

                await this.requestSnapshot.ExecuteAsync(userId, SnapshotType.Subscriber);

                transaction.Complete();
            }

            return(new UpdateAccountSettingsResult(emailConfirmed));
        }