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(); } }
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(); } }
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(); } }
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(); } } }
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(); } }
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); } }
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(); } }
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(); } }
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(); } }
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(); } }
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(); } }
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(); } }
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)); }