private async Task SendMailAsync(WritableDataContext dbContext, Mail mail, CancellationToken cancellationToken) { try { await _smtpClient.SendAsync(mail.Message !, cancellationToken).ConfigureAwait(false); } catch (InvalidOperationException ex) { _logger.LogError(ex.InnerException, "Sending MIME message of mail type '{TYPE}' failed. Queue item id: {ID}.", mail.QueueItem.MailType, mail.QueueItem.Id); await RegisterItemFailureAsync(dbContext, mail.QueueItem, canRetry : false, cancellationToken).ConfigureAwait(false); return; } catch (CommandException ex) { _logger.LogError(ex.InnerException, "Sending MIME message of mail type '{TYPE}' failed. Queue item id: {ID}.", mail.QueueItem.MailType, mail.QueueItem.Id); await RegisterItemFailureAsync(dbContext, mail.QueueItem, canRetry : true, cancellationToken).ConfigureAwait(false); return; } // cancellation token is not passed since mails should be sent and deleted without interruption, // otherwise they could be sent multiple times await RemoveItemAsync(dbContext, mail, default).ConfigureAwait(false); }
private IAsyncEnumerable <MailQueueItem> PeekItems(WritableDataContext dbContext) { var utcNow = _clock.UtcNow; IQueryable <MailQueueItem> queueItems = from item in dbContext.MailQueue where item.DueDate != null && item.DueDate.Value <= utcNow orderby item.DueDate select item; if (_batchSize > 0) { queueItems = queueItems.Take(_batchSize); } return(queueItems.AsAsyncEnumerable()); }
public DbInitializer(WritableDataContext context, IOptions <DbInitializerOptions> options, IClock clock, ILogger <DbInitializer>?logger) { if (options?.Value == null) { throw new ArgumentNullException(nameof(options)); } _context = context ?? throw new ArgumentNullException(nameof(context)); _clock = clock ?? throw new ArgumentNullException(nameof(clock)); _dbProperties = _context.GetDbProperties(); _caseSensitiveComparer = _dbProperties.CaseSensitiveComparer; _caseInsensitiveComparer = _dbProperties.CaseInsensitiveComparer; _logger = logger ?? (ILogger)NullLogger.Instance; _dbEnsureCreated = options.Value.EnsureCreated; _dbSeedObjects = options.Value.Seed; }
public async Task SeedAsync(WritableDataContext dbContext, CancellationToken cancellationToken = default) { foreach (var file in _files) { using (var reader = new StreamReader(file.FilePath)) using (var csv = new CsvReader(reader, new CsvConfiguration(s_defaultCsvCulture))) { // applies global configuration _configureReader(csv.Configuration); // applies optional, file level configuration file.ConfigureReader?.Invoke(csv.Configuration); var entities = csv.GetRecords(file.EntityType); dbContext.AddRange(entities); } } await dbContext.SaveChangesAsync(cancellationToken); }
private async Task HandleMailErrorAsync(WritableDataContext dbContext, Mail mail, CancellationToken cancellationToken) { switch (mail.Error) { case MailTypeNotSupportedException ex: _logger.LogError(ex.InnerException, "Mail type '{TYPE}' is not supported. Queue item id: {ID}.", mail.QueueItem.MailType, mail.QueueItem.Id); await RegisterItemFailureAsync(dbContext, mail.QueueItem, canRetry : false, cancellationToken).ConfigureAwait(false); return; case MailModelSerializationException ex: _logger.LogError(ex.InnerException, "Model of mail type '{TYPE}' could not be deserialized. Queue item id: {ID}.", mail.QueueItem.MailType, mail.QueueItem.Id); await RegisterItemFailureAsync(dbContext, mail.QueueItem, canRetry : false, cancellationToken).ConfigureAwait(false); return; default: _logger.LogError(mail.Error, "Producing MIME message of mail type '{TYPE}' failed. Queue item id: {ID}.", mail.QueueItem.MailType, mail.QueueItem.Id); await RegisterItemFailureAsync(dbContext, mail.QueueItem, canRetry : false, cancellationToken).ConfigureAwait(false); return; } }
public async Task EnqueueItemAsync(MailModel model, WritableDataContext dbContext, CancellationToken cancellationToken) { var mailTypeDefinition = _mailTypeCatalog.GetDefinition(model.MailType, throwIfNotFound: true) !; var serializedMailModel = mailTypeDefinition.SerializeModel(model); var utcNow = _clock.UtcNow; dbContext.MailQueue.Add(new MailQueueItem { CreationDate = utcNow, DueDate = utcNow, MailType = model.MailType, MailModel = serializedMailModel, }); await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); if (!dbContext.Database.TryRegisterForPendingTransactionCommit(WakeProcessor)) { WakeProcessor(); } }
private async Task SendMailsAsync(IReadOnlyList <Task <Mail> > produceMailTasks, WritableDataContext dbContext, CancellationToken cancellationToken) { try { for (int i = 0, n = produceMailTasks.Count; i < n; i++) { var mail = await produceMailTasks[i].ConfigureAwait(false); if (mail.Message != null) { if (!_smtpClient.IsConnected) { try { await _smtpClient.ConnectAsync(_smtpHost, _smtpPort, _smtpSecurity, cancellationToken).ConfigureAwait(false); } catch (Exception ex) when(!(ex is OperationCanceledException)) { var smtpClient = _smtpClient; _smtpClient = _smtpClientFactory(); smtpClient.Dispose(); throw; } if (_smtpCredentials != null) { await _smtpClient.AuthenticateAsync(_smtpCredentials, cancellationToken).ConfigureAwait(false); } } await SendMailAsync(dbContext, mail, cancellationToken).ConfigureAwait(false); } else if (mail.Error != null) { await HandleMailErrorAsync(dbContext, mail, cancellationToken).ConfigureAwait(false); } else { // we should never get here throw new InvalidOperationException(); } } } finally { if (_smtpClient.IsConnected) { await _smtpClient.DisconnectAsync(quit : true, cancellationToken).ConfigureAwait(false); } } }
private async Task RegisterItemFailureAsync(WritableDataContext dbContext, MailQueueItem queueItem, bool canRetry, CancellationToken cancellationToken) { queueItem.DueDate = canRetry ? GetRetryDueDate(queueItem.CreationDate, queueItem.DueDate !.Value) : (DateTime?)null; await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); }
private async Task RemoveItemAsync(WritableDataContext dbContext, Mail mail, CancellationToken cancellationToken) { dbContext.MailQueue.Remove(mail.QueueItem); await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); }
public Task SeedAsync(WritableDataContext dbContext, CancellationToken cancellationToken = default) => _seeder(dbContext, cancellationToken);