NickStrupat / EntityFramework.Triggers

Adds events for entity inserting, inserted, updating, updated, deleting, and deleted
MIT License
373 stars 46 forks source link

Event not being triggered #56

Closed guilhermelionzo closed 3 years ago

guilhermelionzo commented 4 years ago

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
{
   ...
}
NickStrupat commented 4 years ago

Did you add triggers and context instances?

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<DataContext>();
    services.AddTriggers();
    services.AddTransient<IUpdateReportingVotes,UpdateReportingVotes>();
}
guilhermelionzo commented 4 years ago

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/
            }));
NickStrupat commented 4 years ago

Thank you for the kind words!

Are you calling services.AddTriggers(); in your ConfigureServices?

guilhermelionzo commented 4 years ago

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
{
    ...
}
NickStrupat commented 4 years ago

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!

guilhermelionzo commented 4 years ago

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.

chukkalasandeep commented 3 years ago

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?

NickStrupat commented 3 years ago

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.

ensemblebd commented 3 years ago

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.

NickStrupat commented 3 years ago

Yes, the DbContext needs to receive an IServiceProvider if it is needed in any of the triggers.