public async Task ExecuteAsync(HookType type, IDomainEntityContext <Invoice> context, CancellationToken cancellationToken) { var isStatusPropertyDirty = context.EditMode == EditMode.Create || context.EditMode == EditMode.Update && context.IsPropertyDirty(x => x.Status); if (isStatusPropertyDirty) { switch (context.Entity.Status) { case InvoiceStatus.Sent: await context.AddEventAsync(InvoiceSentEvent.Create(_correlationIdProvider.Id, context.Entity.Id), cancellationToken); break; case InvoiceStatus.Paid: await context.AddEventAsync(InvoicePaidEvent.Create(_correlationIdProvider.Id, context.Entity.Id), cancellationToken); break; case InvoiceStatus.Cancelled: await context.AddEventAsync(InvoiceCancelledEvent.Create(_correlationIdProvider.Id, context.Entity.Id), cancellationToken); break; } } }
public Task <IEnumerable <ValidationMessage> > ValidateAsync(IDomainEntityContext <Invoice> context, CancellationToken cancellationToken = default) { if (context.EditMode != EditMode.Update) { return(ValidationResultTask.Ok()); } var statussesAllowedToModifyInvoiceContent = new[] { InvoiceStatus.Draft }; if (!statussesAllowedToModifyInvoiceContent.Contains(context.Pristine.Status)) { try { context.Entity.InvoiceDate.Should().Be(context.Pristine.InvoiceDate); context.Entity.CustomerId.Should().Be(context.Pristine.CustomerId); context.Entity.InvoiceLines.Should().BeEquivalentTo(context.Pristine.InvoiceLines, options => options .Excluding(x => x.Invoice) //.Excluding(x => x.Item) .Excluding(x => x.Timestamp) ); } catch (System.Exception ex) { _logger.LogWarning(ex, "Not allowed to change invoice content in current status."); return(ValidationResultTask.Invalid("Not allowed to change invoice content in current status.")); } } return(ValidationResultTask.Ok()); }
public Task ExecuteAsync(HookType type, IDomainEntityContext <User> context, CancellationToken cancellationToken) { context.Entity.Fullname = $"{context.Entity.GivenName} {context.Entity.MiddleName} {context.Entity.FamilyName}" .Replace(" ", " ") .Trim(); return(Task.CompletedTask); }
public Task ExecuteAsync(HookType type, IDomainEntityContext <Invoice> context, CancellationToken cancellationToken) { context.Entity.Customer = null; // context.Entity.InvoiceLines?.ForEach(invoiceLine => invoiceLine.Item = null); return(Task.CompletedTask); }
public Task ExecuteAsync(HookType type, IDomainEntityContext <FeatureFlagSettings> context, CancellationToken cancellationToken = default) { foreach (var featureFlag in context.Entity.Settings.FeatureFlags.Where(x => x.Id == default)) { featureFlag.Id = Guid.NewGuid(); } return(Task.CompletedTask); }
public async Task <IEnumerable <ValidationMessage> > ValidateAsync(IDomainEntityContext <Role> context, CancellationToken cancellationToken = default) { RuleFor(user => user.Name).NotEmpty(); var validationResult = await ValidateAsync(context.Entity, cancellationToken); return(validationResult.ToValidationMessage()); }
public async Task <IEnumerable <ValidationMessage> > ValidateAsync(IDomainEntityContext <OutboxEvent> context, CancellationToken cancellationToken = default) { RuleFor(customer => customer.Type).NotEmpty(); RuleFor(customer => customer.Event).NotEmpty(); var validationResult = await ValidateAsync(context.Entity, cancellationToken); return(validationResult.ToValidationMessage()); }
public Task ExecuteAsync(HookType type, IDomainEntityContext <UserPreferences> context, CancellationToken cancellationToken) { if (context.Entity.Id == Guid.Empty) { context.Entity.Id = _currentUser.Id; } return(Task.CompletedTask); }
public Task ExecuteAsync(HookType type, IDomainEntityContext <User> context, CancellationToken cancellationToken) { context.Entity.UserRoles?.ForEach(x => { x.User = null; x.Role = null; }); return(Task.CompletedTask); }
public Task ExecuteAsync(HookType type, IDomainEntityContext <Invoice> context, CancellationToken cancellationToken) { int i = 0; foreach (var invoiceLine in context.Entity.InvoiceLines) { invoiceLine.LineNumber = ++i; } return(Task.CompletedTask); }
public Task ExecuteAsync(HookType type, IDomainEntityContext <Customer> context, CancellationToken cancellationToken) { // Example: Retrieve variable set in application layer. // var exampleBoolean = context.State.Get<bool>(CustomerStateKeys.ExampleBoolean); // or var hasExampleBoolean = context.State.TryGet(CustomerStateKeys.ExampleBoolean, out bool exampleBoolean2); // Example: Set variable accesible in application layer after create, update or delete has been called. context.State.Set(CustomerStateKeys.ExampleGuid, Guid.NewGuid()); return(Task.CompletedTask); }
internal static bool IsPropertyDirty <TEntity, TProperty>(this IDomainEntityContext <TEntity> context, Expression <Func <TEntity, TProperty> > property) where TEntity : IEntity where TProperty : IComparable { var currentValue = context.Entity != null?property.Compile()(context.Entity) : default; var previousValue = context.Pristine != null?property.Compile()(context.Pristine) : default; var comparer = EqualityComparer <TProperty> .Default; return(!comparer.Equals(currentValue, previousValue)); }
public async Task <IEnumerable <ValidationMessage> > ValidateAsync(IDomainEntityContext <Customer> context, CancellationToken cancellationToken = default) { RuleFor(customer => customer.Name).NotEmpty(); RuleFor(customer => customer.Name).MaximumLength(200); RuleFor(customer => customer.InvoiceEmailAddress).MaximumLength(320); // How to: Scope validation rules to specific edit mode //When(x => context.EditMode == EditMode.Update, () => //{ //}); var validationResult = await ValidateAsync(context.Entity, cancellationToken); return(validationResult.ToValidationMessage()); }
public async Task ExecuteAsync(HookType type, IDomainEntityContext <UserPreferences> context, CancellationToken cancellationToken) { switch (context.EditMode) { case EditMode.Create: case EditMode.Update: await context.AddEventAsync(UserPreferencesUpdatedEvent.Create(_correlationIdProvider.Id, context.Entity.Id, _currentUser.Id), cancellationToken); break; case EditMode.Delete: await context.AddEventAsync(UserPreferencesDeletedEvent.Create(_correlationIdProvider.Id, context.Entity.Id, _currentUser.Id), cancellationToken); break; } }
public Task <IEnumerable <ValidationMessage> > ValidateAsync(IDomainEntityContext <Invoice> context, CancellationToken cancellationToken = default) { if (context.EditMode != EditMode.Delete) { return(ValidationResultTask.Ok()); } var statussesAllowedToDelete = new[] { InvoiceStatus.Draft, InvoiceStatus.Cancelled }; if (!statussesAllowedToDelete.Contains(context.Pristine.Status)) { return(ValidationResultTask.Invalid("Not allowed to delete invoice in current status.")); } return(ValidationResultTask.Ok()); }
public async Task <IEnumerable <ValidationMessage> > ValidateAsync(IDomainEntityContext <Invoice> context, CancellationToken cancellationToken = default) { RuleFor(invoice => invoice.CustomerId).NotEmpty(); RuleFor(invoice => invoice.InvoiceDate).GreaterThan(DateTime.MinValue); RuleFor(invoice => invoice.OrderReference).NotEmpty(); RuleFor(invoice => invoice.InvoiceLines).NotEmpty(); RuleForEach(invoice => invoice.InvoiceLines).ChildRules(invoiceLine => { invoiceLine.RuleFor(x => x.LineNumber).NotEmpty(); invoiceLine.RuleFor(x => x.Quantity).NotEmpty(); invoiceLine.RuleFor(x => x.Description).NotEmpty(); }); var validationResult = await ValidateAsync(context.Entity, cancellationToken); return(validationResult.ToValidationMessage()); }
public Task ExecuteAsync(HookType type, IDomainEntityContext <User> context, CancellationToken cancellationToken) { switch (context.EditMode) { case EditMode.Create: context.AddEventAsync(UserCreatedEvent.Create(_correlationIdProvider.Id, context.Entity.Id, _currentUser.Id), cancellationToken); break; case EditMode.Update: context.AddEventAsync(UserUpdatedEvent.Create(_correlationIdProvider.Id, context.Entity.Id, _currentUser.Id), cancellationToken); break; case EditMode.Delete: context.AddEventAsync(UserDeletedEvent.Create(_correlationIdProvider.Id, context.Entity.Id, _currentUser.Id), cancellationToken); break; } return(Task.CompletedTask); }
public async Task <IEnumerable <ValidationMessage> > ValidateAsync(IDomainEntityContext <User> context, CancellationToken cancellationToken = default) { RuleFor(user => user.Email).NotEmpty(); RuleFor(user => user.Email).EmailAddress(); RuleFor(user => user.FamilyName).NotEmpty(); RuleFor(user => user.UserRoles).NotEmpty(); RuleForEach(user => user.UserRoles).ChildRules(userRole => { userRole.RuleFor(x => x.RoleId).NotEmpty(); }); RuleFor(user => user.UserRoles) .Must((userRoles) => HasUniqueRoles(userRoles)) .WithMessage(x => "UserRoles cannot contain duplicate roles"); var validationResult = await ValidateAsync(context.Entity, cancellationToken); return(validationResult.ToValidationMessage()); }
public async Task ExecuteAsync(HookType type, IDomainEntityContext <Invoice> context, CancellationToken cancellationToken) { if (type == HookType.BeforeCreate) { // An invoice always has an accompanying PDF document. We can create this by requesting synchronization via a domain event. context.Entity.PdfIsSynced = false; await context.AddMessageAsync(SynchronizeInvoicePdfMessage.Create(_correlationIdProvider.Id, context.Entity.Id), cancellationToken); } else if (type == HookType.BeforeUpdate && context.Pristine.Status == InvoiceStatus.Draft) { var invoiceToPdfModel = await _invoiceToPdfModelMapper.MapAsync(context.Entity, cancellationToken); var checksum = invoiceToPdfModel.GetChecksum(); // Compare calculated checksum against checksum when PDF was last generated if (!string.Equals(checksum, context.Entity.PdfChecksum)) { context.State.TryGet(InvoiceStateKeys.ThrowIfPdfIsNotSynced, out bool throwIfPdfIsNotSynced); if (throwIfPdfIsNotSynced) { // Prevent a possible infinite loop by not allowing synchronization when in the context of // updating the invoice directly after PDF synchronization has occured. In this scenario the // checksums should have been equal. throw new DomainException("Expected invoice PDF to have been synced"); } // Changes made to entity DO affect PDF content. Requesting synchronization. context.Entity.PdfIsSynced = false; await context.AddMessageAsync(SynchronizeInvoicePdfMessage.Create(_correlationIdProvider.Id, context.Entity.Id), cancellationToken); } else { // Changes made to entity DIDNT affect PDF content. Not requesting synchronization. context.Entity.PdfIsSynced = true; } } if (context.Entity.PdfIsSynced && context.IsPropertyDirty(x => x.PdfIsSynced)) { await context.AddEventAsync(InvoicePdfSynchronizedEvent.Create(_correlationIdProvider.Id, context.Entity.Id), cancellationToken); } }
public async Task <IEnumerable <ValidationMessage> > ValidateAsync(IDomainEntityContext <Customer> context, CancellationToken cancellationToken = default) { if (context.EditMode != EditMode.Delete) { return(ValidationResult.Ok()); } var invoiceStatussesWhichAllowDeletionOfCustomer = new[] { InvoiceStatus.Cancelled }; var hasInvoices = await _invoiceQuery.AsQueryable() .Where(x => x.CustomerId == context.Entity.Id) .Where(x => !invoiceStatussesWhichAllowDeletionOfCustomer.Contains(x.Status)) .AnyAsync(cancellationToken); if (hasInvoices) { return(ValidationResult.Invalid("Cannot delete customer, because one or more invoices are linked to this customer.")); } return(ValidationResult.Ok()); }
public async Task <IEnumerable <ValidationMessage> > ValidateAsync(IDomainEntityContext <Role> context, CancellationToken cancellationToken = default) { if (context.EditMode == EditMode.Delete) { return(ValidationResult.Ok()); } if (context.IsPropertyDirty(x => x.Name)) { var alreadyExists = await _roleQuery.AsQueryable() .Where(x => x.Id != context.Entity.Id) .Where(x => x.Name == context.Entity.Name) .AnyAsync(cancellationToken); if (alreadyExists) { return(ValidationResult.Invalid($"A role with name '{context.Entity.Name}' already exists.")); } } return(ValidationResult.Ok()); }
public Task ExecuteAsync(HookType type, IDomainEntityContext <User> context, CancellationToken cancellationToken) { if (context.IsPropertyDirty(x => x.Email)) { context.AddMessageAsync(SyncEmailToAuth0UserMessage.Create(_correlationIdProvider.Id, context.Entity.Id), cancellationToken); } if (context.IsPropertyDirty(x => x.Fullname)) { context.AddMessageAsync(SyncNameToAuth0UserMessage.Create(_correlationIdProvider.Id, context.Entity.Id), cancellationToken); } if (!Enumerable.SequenceEqual( context.Entity.UserRoles.Select(x => x.RoleId), context.Pristine.UserRoles.Select(x => x.RoleId) )) { context.AddMessageAsync(SyncRolesToAuth0UserMessage.Create(_correlationIdProvider.Id, context.Entity.Id), cancellationToken); } return(Task.CompletedTask); }
public DomainEntity( ILogger logger, ICurrentUser currentUser, IDateTime dateTime, IDbCommand <T> dbCommand, Lazy <IEnumerable <IDefaultValuesSetter <T> > > defaultValuesSetters, Lazy <IEnumerable <IValidator <T> > > validators, Lazy <IEnumerable <IBeforeCreate <T> > > beforeCreateHooks, Lazy <IEnumerable <IAfterCreate <T> > > afterCreateHooks, Lazy <IEnumerable <IBeforeUpdate <T> > > beforeUpdateHooks, Lazy <IEnumerable <IAfterUpdate <T> > > afterUpdateHooks, Lazy <IEnumerable <IBeforeDelete <T> > > beforeDeleteHooks, Lazy <IEnumerable <IAfterDelete <T> > > afterDeleteHooks, Lazy <IOutboxEventCreator> outboxEventCreator, Lazy <IOutboxMessageCreator> outboxMessageCreator, Lazy <IJsonService <T> > jsonService, Lazy <IAuditlogger <T> > auditlogger ) { Context = new DomainEntityContext <T>(logger, outboxEventCreator, outboxMessageCreator, jsonService); Logger = logger; CurrentUser = currentUser; DateTime = dateTime; DbCommand = dbCommand; _defaultValuesSetters = defaultValuesSetters; _validators = validators; _beforeCreateHooks = beforeCreateHooks; _afterCreateHooks = afterCreateHooks; _beforeUpdateHooks = beforeUpdateHooks; _afterUpdateHooks = afterUpdateHooks; _beforeDeleteHooks = beforeDeleteHooks; _afterDeleteHooks = afterDeleteHooks; _auditlogger = auditlogger; _options = new DomainEntityOptions(); }
public Task ExecuteAsync(HookType type, IDomainEntityContext <Customer> context, CancellationToken cancellationToken) { context.Entity.Invoices = null; return(Task.CompletedTask); }
public Task ExecuteAsync(HookType type, IDomainEntityContext <Role> context, CancellationToken cancellationToken) { context.Entity.UserRoles = null; return(Task.CompletedTask); }
public Task ExecuteAsync(HookType type, IDomainEntityContext <T> context, CancellationToken cancellationToken) { _logger.LogInformation($"{type}: {typeof(T).Name}"); return(Task.CompletedTask); }
public Task ExecuteAsync(HookType type, IDomainEntityContext <User> context, CancellationToken cancellationToken) { context.AddMessageAsync(DeleteAuth0UserMessage.Create(_correlationIdProvider.Id, context.Entity.Id), cancellationToken); return(Task.CompletedTask); }
public async Task ExecuteAsync(HookType type, IDomainEntityContext <FeatureFlagSettings> context, CancellationToken cancellationToken) { await context.AddEventAsync(FeatureFlagSettingsUpdatedEvent.Create(_correlationIdProvider.Id, context.Entity.Id, _currentUser.Id), cancellationToken); }
public Task ExecuteAsync(HookType type, IDomainEntityContext <OutboxEvent> context, CancellationToken cancellationToken) { _outboxEventCreatedEvents.Add(OutboxEventCreatedEvent.Create(_correlationIdProvider.Id, context.Entity.Id, _currentUser.Id)); return(Task.CompletedTask); }