question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

The trigger executed twice

See original GitHub issue

Whenever I do insert an entity with its related entity, the trigger is executed twice. I don’t think there’s a problem in my code though.

CreateStockInputWindow.xaml.cs

...
                    dbContext.StockInputLogs.Add(
                        new StockInputLog()
                        {
                            UserId = Session.Instance.User!.Id,
                            Amount = inputAmount,
                            Stock = new Stock()
                            {
                                ExpirationDate = expirationDate,
                                ItemId = Item!.Id,
                                Amount = inputAmount,
                            }
                        }
                    );
...

StockInputLog.cs

namespace WarehouseApp.Models;

public class StockInputLog : BaseModel
{
    public int StockId { get; set; }

    public int UserId { get; set; }

    public int Amount { get; set; }

    public Stock? Stock { get; set; }

    public User? User { get; set; }
}

Stock.cs

using System;
using System.Collections.Generic;

namespace WarehouseApp.Models;

public class Stock : BaseModel
{
    public DateTime ExpirationDate { get; set; }

    public int ItemId { get; set; }

    public int Amount { get; set; }

    public Item? Item { get; set; }

    public List<StockInputLog>? StockInputLogs { get; set; }

    public List<StockOutputLog>? StockOutputLogs { get; set; }
}

The current workaround is to insert an entity, then call dbContext.SaveChanges(), and finally insert the related entity by selecting the previous entity’s id and do dbContext.SaveChanges()

Example.cs

        dbContext.AddRange(
            new User(username: "admin", name: "Admin", password: "Password", isAdmin: true),
            new User(username: "user", name: "User", password: "Password", isAdmin: false)
        );

        dbContext.AddRange(
            new Item(barcode: "8991038775416", name: "Lorem ipsum ", quantifier: "dus"),
            new Item(
                barcode: "8991038775417",
                name: "Dolor sit amet",
                quantifier: "kg",
                totalAmount: 0
            ),
            new Item(
                barcode: "8991038775418",
                name: "Consectetur adipiscing",
                quantifier: "m",
                totalAmount: 0
            )
        );

        dbContext.SaveChanges();

        dbContext.Add(new Stock() { ItemId = 1, ExpirationDate = DateTime.Now.AddDays(10) });

        dbContext.SaveChanges();

        dbContext.AddRange(
            new StockInputLog() { Amount = 10, UserId = 1, StockId = 1 },
            new StockInputLog() { Amount = 20, UserId = 1, StockId = 1 },
            new StockInputLog() { Amount = 15, UserId = 1, StockId = 1 }
        );

        dbContext.SaveChanges();

But it looks ugly. Can anyone help to fix this problem? Thank you in advance

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
kangzoelcommented, Aug 14, 2022

My bad, the problem is in my code, not the library. I shouldn’t have initialize the stock value, and let the StockInputLog trigger handle the initial value. Thus the code should be

CreateStockInputWindow.xaml.cs

...
                    dbContext.StockInputLogs.Add(
                        new StockInputLog()
                        {
                            UserId = Session.Instance.User!.Id,
                            Amount = inputAmount,
                            Stock = new Stock()
                            {
                                ExpirationDate = expirationDate,
                                ItemId = Item!.Id,
                                // Removed: Amount = inputAmount,
                            }
                        }
                    );
...
0reactions
kangzoelcommented, Aug 8, 2022

I don’t use dependency injection. I modified my code but it has not effect. The trigger still executed twice. These are my new codes:

DatabaseContext.cs

using System.Diagnostics;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using WarehouseApp.Models;

namespace WarehouseApp.Database;

public class DatabaseContext : DbContext
{
    public DbSet<Item> Items => Set<Item>();
    public DbSet<Stock> Stocks => Set<Stock>();
    public DbSet<StockInputLog> StockInputLogs => Set<StockInputLog>();
    public DbSet<StockOutputLog> StockOutputLogs => Set<StockOutputLog>();
    public DbSet<User> Users => Set<User>();

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder
            .UseSqlite("Data Source=database.sqlite")
            .EnableSensitiveDataLogging()
            .LogTo((e) => Trace.WriteLine(e))
            .UseTriggers(triggerOptions =>
            {
                triggerOptions.AddTrigger<Triggers.StockInputLogTrigger>(ServiceLifetime.Scoped);
            });
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Stock>().HasAlternateKey(s => new { s.ItemId, s.ExpirationDate });
    }
}

StockInputLogTrigger.cs

using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using EntityFrameworkCore.Triggered;
using EntityFrameworkCore.Triggered.Lifecycles;
using WarehouseApp.Models;

namespace WarehouseApp.Triggers;

internal class StockInputLogTrigger : IBeforeSaveTrigger<StockInputLog>, IBeforeSaveCompletedTrigger
{
    readonly HashSet<StockInputLog> _processedEntities = new();

    public Task BeforeSave(
        ITriggerContext<StockInputLog> context,
        CancellationToken cancellationToken
    )
    {
        if (!_processedEntities.Contains(context.Entity))
        {
            _processedEntities.Add(context.Entity);

            var stock = context.Entity.Stock;

            switch (context.ChangeType)
            {
                case ChangeType.Added:
                    stock!.Amount += context.Entity.Amount;
                    break;
                case ChangeType.Deleted:
                    stock!.Amount -= context.Entity.Amount;
                    break;
            }
        }

        return Task.CompletedTask;
    }

    public Task BeforeSaveCompleted(CancellationToken _)
    {
        _processedEntities.Clear();

        return Task.CompletedTask;
    }
}

I think I’ll use my workaround instead, for the time being, by inserting entities independently (no nesting entity), until the problem is fixed

Read more comments on GitHub >

github_iconTop Results From Across the Web

Avoid Triggers from firing twice in a transaction
Triggers can fire twice, once before workflows and once after workflows. Review step 12 in Trigger and Order of Execution.
Read more >
Can anyone explain me why my trigger execute twice?
Hey, I'm struggling to create a trigger that updates the parent whenever the child is updated. I'm not sure why my trigger is...
Read more >
What would cause a trigger to execute twice?
The trigger works fine, except for when an Opportunity goes to 'Closed Won' it creates duplicate log entries in the custom object. It...
Read more >
StateMachine understanding, weird behavior of trigger ...
StateMachine understanding, weird behavior of trigger executed twice · Entry actions are executed · Outgoing triggers are fired (the 3 differents ...
Read more >
sql - PLSQL Statement Trigger Firing Twice
The trigger doesn't fire twice. I'd guess the duplicated ORA-06512 message in the error stack is due to the way the compiler structures...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found