Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does an entity model fire an event when its data changes?

I want to be notified when data changes within a particular table, represented by an entity model. Is the model aware of underlying data changes? If it does actually fire an event, how can I subscribe to it?

like image 575
Morgan Herlocker Avatar asked Apr 26 '11 14:04

Morgan Herlocker


4 Answers

Your question is actually very unclear.

I want to be notified when data changes within a particular table, represented by an entity model.

What does it mean?

Do you want to be notified about changes in database not done by your application? Then the answer is: No EF doesn't provide any such notification system. You must build your own.

Do you want to be notified about changes you did to your entities? The the answer is INotifyPropertyChanged interface and ObservableCollection for navigation properties. use these constructs in your entities to be able to fire events when entity changes.

Do you want to know what changes will be executed in the database? Override SaveChanges or handle SavingChanges and use ObjectStateManager to get list of changed entities. Here is some example how to get list of added entities of given type.

like image 143
Ladislav Mrnka Avatar answered Oct 20 '22 19:10

Ladislav Mrnka


The NuGet package EntityFramework.Triggers nicely wraps up functionality of subscribing to an Entity for inserts, updates, and deletions.

Just wrap your context with the DbContextWithTriggers;

public class MyContext : DbContextWithTriggers {
    public DbSet<Person> People { get; set; }
}

Then subscribe to trigger events

var mycontext  = new MyContext() { TriggersEnabled = true };
Triggers<Person>.Inserting += entry =>
{
    Console.WriteLine($"Person: {entry.Entity}");
};
like image 31
wonea Avatar answered Oct 20 '22 19:10

wonea


No, there is no events fired on Change, only when you do SaveChanges you could catch what you need ...

for that, try looking at this question / answers

like image 1
balexandre Avatar answered Oct 20 '22 21:10

balexandre


you can use Interceptors.

just inherit SaveChangesInterceptor and override SavingChangesAsync then listen SavedChanges event.

you should override SavingChanges ,because in SavedChanges all entry.State is EntityState.Unchanged

My code is just like this:

public override InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result)
{
    try
    {
        foreach (var entry in eventData.Context.ChangeTracker.Entries())
        {
            //when target entity is modified ,subscribe event
            if (entry.Entity is Device de && (entry.State == EntityState.Added || entry.State == EntityState.Deleted || entry.State == EntityState.Modified))
            {
                if (entry.State == EntityState.Modified)
                {
                    if (de.UseStatus.GetValueOrDefault(0) != (int)UseStatusConstType.IsCancel)
                    {
                        continue;
                    }
                }
                ModifiedDeviceId = de.Id;
                eventData.Context.SavedChanges -= Context_SavedChangesAsync;
                eventData.Context.SavedChanges += Context_SavedChangesAsync;
            }
        }
        return result;
    }
    catch (Exception ex)
    {
        _log.LogError(ex, "when entryState changing,try auto run function faild");
        return result;
    }
}

private async void Context_SavedChangesAsync(object sender, SavedChangesEventArgs e)
{
    //_log.LogInformation("current ModifiedDeviceId is  {ModifiedDeviceId} ", ModifiedDeviceId);

    try
    {               
        _log.LogInformation("in logger:{logger} for device:{deviceId},the state changed,now refresh device cache ", nameof(AutoRefreshDeviceCacheInterceptor), ModifiedDeviceId);

        //just do you want 

    }
    catch (Exception ex)
    {
        _log.LogError(ex, "in logger:{logger} after entryState changed,try auto run function faild", nameof(AutoRefreshDeviceCacheInterceptor));
    }
}

public override async ValueTask<InterceptionResult<int>> SavingChangesAsync(DbContextEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default)
{
    try
    {
        eventData.Context.ChangeTracker.DetectChanges();
        foreach (var entry in eventData.Context.ChangeTracker.Entries())
        {
            if (entry.Entity is Device de && (entry.State == EntityState.Added || entry.State == EntityState.Deleted || entry.State == EntityState.Modified))
            {
                if (entry.State == EntityState.Modified)
                {
                    if (de.UseStatus.GetValueOrDefault(0) != (int)UseStatusConstType.IsCancel)
                    {
                        continue;
                    }
                }
                ModifiedDeviceId = de.Id;
                eventData.Context.SavedChanges -= Context_SavedChangesAsync;
                eventData.Context.SavedChanges += Context_SavedChangesAsync;
            }
        }
        return result;
    }
    catch (Exception ex)
    {
        _log.LogError(ex, "when entryState changing,try auto run function faild");
        return result;
    }
}
like image 1
我零0七 Avatar answered Oct 20 '22 19:10

我零0七