Event not being triggered
guilhermelionzo opened this issue · 10 comments
Hi there,
I'm trying to implement an action that going to be triggered after a entity insert on database. The code below is not triggering the action and I don't know what I'm doing wrong.
Could you guys help me?
If you need more information, please let me know.
Thanks in advance.
File[Startup.cs]
public void ConfigureServices(IServiceCollection services)
{
...
services.AddTransient<IUpdateReportingVotes,UpdateReportingVotes>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseTriggers(builder=>{
builder.Triggers<Vote,DataContext>().Inserting.Add(
e => serviceProvider.GetService<IUpdateReportingVotes>().Start(e.Entity.DocumentId,e.Entity.VoteType)
);
});
}
File[DatabaseContext.cs]
public class DataContext : DbContextWithTriggers
{
...
}
Did you add triggers and context instances?
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<DataContext>();
services.AddTriggers();
services.AddTransient<IUpdateReportingVotes,UpdateReportingVotes>();
}
Hi Nick,
To add the 'DataContext', I'm using AddDbContext, as shonw below. Is that a problem? I'm also adding the trigger(services.AddScoped <DataContext> ();
).
By the way, thank you for your quick response and for the amazing library :)
services.AddDbContext<DataContext>(
opt=>opt.UseMySql(Configuration.GetConnectionString("connectionString"),
mySqlOptionsAction: sqlOptions =>{
sqlOptions.EnableRetryOnFailure(); // enable Retry on failure : https://thecodebuzz.com/enabling-transient-error-resiliency-enableretryonfailure/
}));
Thank you for the kind words!
Are you calling services.AddTriggers();
in your ConfigureServices
?
Yes, I'm calling services.AddTriggers();
. It follows the piece of code that contains the DbContext, Trigger and UpdateReportingVotes service calling.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DataContext>(
opt=>opt.UseMySql(Configuration.GetConnectionString("connectionString"),
mySqlOptionsAction: sqlOptions =>{
sqlOptions.EnableRetryOnFailure(); // enable Retry on failure :
https://thecodebuzz.com/enabling-transient-error-resiliency-enableretryonfailure/
}));
..
//Use EntityFramework Triggers
//more information : https://github.com/NickStrupat/EntityFramework.Triggers
services.AddTriggers();
services.AddTransient<IUpdateReportingVotes,UpdateReportingVotes>();
}
Ps.: The DbContext class is inheriting DbContextWithTriggers.
public class DataContext : DbContextWithTriggers
{
...
}
If you set a breakpoint right at *BREAKPOINT*
in the following bit of code you posted...
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseTriggers(builder=>{
builder.Triggers<Vote,DataContext>().Inserting.Add(
e => *BREAKPOINT*serviceProvider.GetService<IUpdateReportingVotes>().Start(e.Entity.DocumentId,e.Entity.VoteType)
);
});
}
Does it hit the breakpoint?
Also, would be great if you could post an run-able solution which demonstrates the issue. Thanks!
Nick, unfortunately the breakpoint was not hit.
During this week, I`m going to be a bit busy. But next week I will send a run-able solution.
Thank you again.
Hi @NickStrupat ,
I am also trying the same code and the events are not triggering, but in my project the DBContext is dynamic as below
ConfigureServices
services.AddTransient<TrackingDBContext>(provider => {
TrackingDBContext trackingDBContext = null;
try
{
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("SQLHOST")) && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("SQLPORT")) && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("SQLPASSWORD")) && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("SQLUSER")) && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("SQLDATABASE")))
{
trackingDBContext = new TrackingDBSqlContext();
}
else if(!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("PGHOST")) && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("PGPORT")) && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("PGPASSWORD")) && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("PGUSER")) && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("PGDATABASE")))
{
trackingDBContext = new TrackingDBPostgresContext();
}
return trackingDBContext;
}
catch (Exception ex)
{
ILogger<Startup> _logger = (ILogger<Startup>)provider.GetService(typeof(ILogger<Startup>));
_logger.LogWarning("Failed to create DBContext", ex);
return trackingDBContext;
}
});
...........
services.AddTriggers();
Configure
app.UseTriggers(builder => {
builder.Triggers<Command, TrackingDBContext>().Inserting.Add(e =>
{
Console.WriteLine(e.Entity);
});
builder.Triggers<Command, TrackingDBContext>().Inserted.Add(e =>
{
Console.WriteLine(e.Entity);
});
builder.Triggers<Command, TrackingDBContext>().Updated.Add(e =>
{
Console.WriteLine(e.Entity);
});
builder.Triggers<Command, TrackingDBContext>().Deleted.Add(e =>
{
Console.WriteLine(e.Entity);
});
});
TrackingDBContext
public class TrackingDBContext : DbContextWithTriggers
{
public TrackingDBContext()
{
}
public TrackingDBContext(DbContextOptions<TrackingDBContext> options) : base(options)
{
}
public DbSet<Command> Commands { get; set; }
public override int SaveChanges()
{
return this.SaveChangesWithTriggers(base.SaveChanges, acceptAllChangesOnSuccess: true);
}
public override Int32 SaveChanges(Boolean acceptAllChangesOnSuccess)
{
return this.SaveChangesWithTriggers(base.SaveChanges, acceptAllChangesOnSuccess);
}
public override Task<Int32> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
{
return this.SaveChangesWithTriggersAsync(base.SaveChangesAsync, acceptAllChangesOnSuccess: true, cancellationToken: cancellationToken);
}
public override Task<Int32> SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken))
{
return this.SaveChangesWithTriggersAsync(base.SaveChangesAsync, acceptAllChangesOnSuccess, cancellationToken);
}
}
Can you please help on this where is it wrong?
Also another question, instead of handling the triggers in my API project can I write a separate console application to handle the database triggers?
Hi there, @chukkalasandeep. It would be easier if you posted a link to a complete code sample (a Program.cs
and a Project.csproj
).
From what I can tell from your pasted code snippets, you shouldn't have those SaveChanges...
overrides in your TrackingDBContext
class. The DbContextWithTriggers
class does this for you.
Hopefully that is your issue, but again it is much more difficult for me to help you if I only have a few snippets to read. I can only guess.
After pulling src into my project, I discovered the reason.
It properly calls the raiseAction()
which calls RaiseGlobalThenInstance()
.
However, because we are not using the base class for DbContextWithTriggers
, the IServiceProvider
instance is null, which means none of the DI based triggers are resolved therefore nor called (logically impossible, without the provider).
So the solution is simple, by adding IServiceProvider
to your constructor for DbContext
, and passing that directly into SaveChangesWithTriggers
as a parameter, it works.
This can best be resolved for future persons by updating the README.md documentation to instruct users using that approach, to do so.
Yes, the DbContext
needs to receive an IServiceProvider
if it is needed in any of the triggers.