NickStrupat / EntityFramework.Triggers

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

Triggers Twice #44

Closed VRoxa closed 5 years ago

VRoxa commented 5 years ago

I am using EFCore 2.1.8; AutoFac 4.9.2 w/ AutoFac.DependencyInjection 4.4.0 & AspNetCore.App 2.1.1.

I added your package EntityFrameworkCore.Triggers.AspNetCore 1.0.0 to use IApplicationBuilder#UseTriggers() and IServiceCollection#AddTriggers() and declare my own triggers.

Here's how I declare'em:

app.UseTriggers(builder =>
            {
                 #region Declare Triggers for Actors               
                Triggers<MercuryActor, MessageBrokerContext>.Inserted += entry =>
                {
                    MercuryActor actor= entry.Entity;
                    Logger.LogInformation("{0} Inserted! w/ Context", actor.Id);
                };
                #endregion

                Logger.LogInformation("Triggers declared");
            } );

            MessageBrokerContext ctx = app.ApplicationServices.GetRequiredService<MessageBrokerContext>();
            ctx.Actors.Add(new MercuryActor("Trigger Testing"));
            ctx.SaveChangesAsync();

I do have two issues on that. This is a microservice hosted working with an API which listens and inserts entities. When I run this code above, it triggers the event (when I insert that entity right after of the declaration), but when I use the API which is hosted independently of that service, it doesn't.

Next, I that case, when it triggers, it does twice.

Any idea? Thanks.

NickStrupat commented 5 years ago

Could you post a full example of this behavior?

NickStrupat commented 4 years ago

I realized what your issue was. This is more for future readers of this thread...

You are adding your handlers using the global triggers instead of the instance triggers.

To use the instance triggers, you need to use the builder object in your UseTriggers() call

app.UseTriggers(builder =>
{
    builder.Triggers<MercuryActor, MessageBrokerContext>.Inserted += entry =>
    {
        MercuryActor actor= entry.Entity;
        Logger.LogInformation("{0} Inserted! w/ Context", actor.Id);
    };
    Logger.LogInformation("Triggers declared");
});
VRoxa commented 4 years ago

Ok, that was useful.

Indeed, I realized time ago that the fact why I could not communicate through microservices was even though both services were using the same EF Library, they had owned instances of the context (DbContext), that is, they were declaring the triggers by their own and not being able to share them each other.

Would be nice that behavior, but quite impossible. Never thought 'bout that.

Anyway, I appreciate your response.

Your lib is truly useful as to keep the code clean, but I think any other approach to trigger some kind of event wrapped in the same service scope could conquer the job.

NickStrupat commented 4 years ago

So to achieve that behaviour, you need to use something like a message queue (aka message bus) which both services are connected to across network. Have your triggers write the appropriate messages into the message queue, and set up handlers on the message queue receivers to process those messages.

NickStrupat commented 4 years ago

I have thought many times about developing a library which handles that entire behaviour with some kind of serialization convention. Maybe on day...

Gilmar-Gomes commented 4 years ago

I realized what your issue was. This is more for future readers of this thread...

You are adding your handlers using the global triggers instead of the instance triggers.

To use the instance triggers, you need to use the builder object in your UseTriggers() call

app.UseTriggers(builder =>
{
  builder.Triggers<MercuryActor, MessageBrokerContext>.Inserted += entry =>
  {
      MercuryActor actor= entry.Entity;
      Logger.LogInformation("{0} Inserted! w/ Context", actor.Id);
  };
  Logger.LogInformation("Triggers declared");
});

Congratulations on your brilliant work ... In a project I'm working on, for design reasons I can't use instance triggers, just global triggers. Can you suggest any way around this problem with global triggers?

NickStrupat commented 4 years ago

Oh sure, you can use the global singleton triggers like this:

Triggers<MercuryActor, MessageBrokerContext>.Inserted += entry =>
{
    MercuryActor actor= entry.Entity;
    Logger.LogInformation("{0} Inserted! w/ Context", actor.Id);
};
Logger.LogInformation("Triggers declared");