EntityFrameworkCore.Triggered
EntityFrameworkCore.Triggered copied to clipboard
Cannot trigger all AfterSaveCompleted() if 2 triggers all implement IAfterSaveCompletedTrigger
ATrigger as below.
public ATrigger : IAfterSaveTrigger<A>, IAfterSaveCompletedTrigger {
private List<string> _aNames = new List<string>();
public Task AfterSave(ITriggerContext<A> context, CancellationToken cancellationToken) {
if (context.ChangeType == ChangeType.Added) {
this._aNames.Add(context.Name);
return Task.CompletedTask;
}
}
public Task AfterSaveCompleted(CancellationToken cancellationToken) {
Console.WriteLine($"There are {_aNames.Count()} As to {_aNames.Distinct().Count()}" distinct A names");
return Task.CompletedTask;
}
}
BTrigger as below.
public BTrigger : IAfterSaveTrigger<B>, IAfterSaveCompletedTrigger {
private List<string> _bNames = new List<string>();
public Task AfterSave(ITriggerContext<B> context, CancellationToken cancellationToken) {
if (context.ChangeType == ChangeType.Added) {
this._bNames.Add(context.Name);
return Task.CompletedTask;
}
}
public Task AfterSaveCompleted(CancellationToken cancellationToken) {
Console.WriteLine($"There are {_bNames.Count()} Bs to {_bNames.Distinct().Count()}" distinct B names");
return Task.CompletedTask;
}
}
AfterSave in both ATrigger and BTrigger could be triggered by SaveChanges(), but only ATrigger's AfterSaveCompleted triggered in the end.
AfterSaveCompleted
would be raised for all triggers for as long as a Save completed successfully. Some questions:
- What version of this library are you using?
- How are you registering your triggers?
- Can you share a reproducible?
- Version is 2.3.2.
- Registering triggers using
services.AddAssemblyTriggers(typeof(ATrigger).Assembly, typeof(BTrigger).Assembly);
Can you help me complete the following repro?
I'm getting this output:
A only
AfterSave for entity A
AfterSaveCompleted for entity A
AfterSaveCompleted for entity B
B only
AfterSave for entity B
AfterSaveCompleted for entity A
AfterSaveCompleted for entity B
A and B
AfterSave for entity A
AfterSave for entity B
AfterSaveCompleted for entity A
AfterSaveCompleted for entity B
Given this code:
using EntityFrameworkCore.Triggered;
using EntityFrameworkCore.Triggered.Lifecycles;
using Microsoft.EntityFrameworkCore;
using var dbContext = new MyDbContext();
Console.WriteLine("A only");
dbContext.A.Add(new A(1));
dbContext.SaveChanges();
Console.WriteLine();
Console.WriteLine("B only");
dbContext.B.Add(new B(1));
dbContext.SaveChanges();
Console.WriteLine();
Console.WriteLine("A and B");
dbContext.A.Add(new A(2));
dbContext.B.Add(new B(2));
dbContext.SaveChanges();
Console.ReadLine();
record A(int Id);
record B(int Id);
class MyDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseInMemoryDatabase("test");
optionsBuilder.UseTriggers(triggerOptions => triggerOptions.AddAssemblyTriggers());
}
public DbSet<A> A => Set<A>();
public DbSet<B> B => Set<B>();
public static Task WriteLineAsync(string message)
{
Console.WriteLine(message);
return Task.CompletedTask;
}
}
record Trigger1 : IAfterSaveTrigger<A>, IAfterSaveCompletedTrigger
{
public Task AfterSave(ITriggerContext<A> context, CancellationToken cancellationToken)
=> MyDbContext.WriteLineAsync($"AfterSave for entity A");
public Task AfterSaveCompleted(CancellationToken cancellationToken)
=> MyDbContext.WriteLineAsync($"AfterSaveCompleted for entity A");
}
record Trigger2 : IAfterSaveTrigger<B>, IAfterSaveCompletedTrigger
{
public Task AfterSave(ITriggerContext<B> context, CancellationToken cancellationToken)
=> MyDbContext.WriteLineAsync($"AfterSave for entity B");
public Task AfterSaveCompleted(CancellationToken cancellationToken)
=> MyDbContext.WriteLineAsync($"AfterSaveCompleted for entity B");
}
I have changed the way to register triggers and it works now. optionsBuilder.UseTriggers(triggerOptions => triggerOptions.AddAssemblyTriggers());
There may be an issue here with registering triggers directly with the ServiceProvider. Reopened until this issue is confirmed/fixed
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
I think this should be re-opened. I previously had (using ASP.NET Core 6):
public void ConfigureServices(IServiceCollection services) { services.AddAssemblyTriggers(); }
and fixed the same issue by moving to above syntax. I guess that should be fixed no?
Sorry, but don't have time to setup a full repro repo.
My PR fixes the original issue. We've also had big issues with using above syntax in Azure linux apps (app service) with CPU immediately raising to 100% as soon as we start up/run our ASP.NET Core backend (hence why we decided to put in the effort to fix the issue described here). We can unfortunately not reproduce this locally (neither on Win nor on Linux). We haven't spent a lot of time to dig into it though.
Thanks for the fix! Note that registering triggers with the DI container instead of the DbContext
is something that I was considering deprecating. I'm interested in the CPU immediately raising to 100%
issue though. Can you open up a new issue for that?
Will do! Thanks for quick action - very much appreciated! 😊👍
Btw. Can you release a new version? @koenbeuk
@cjblomqvist Done!
Thanks! I've not forgotten the CPU-stuff, but a little bit complex since it's only happening in Azure (AFAICT anyway) - so it'll take me a little bit more time before I have an issue created. It's not forgotten anyway!