5

Tracking Changes in EntityFramework and notifying these changes to client apps u...

 2 years ago
source link: https://www.codeproject.com/Tips/5329997/Tracking-Changes-in-EntityFramework-and-notifying
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Introduction

When creating a client server based WPF applications which can have multiple clients connected to a single WebApi service, one primary problem we need to address is synchronizing data between the multiple client apps. We need a mechanism to update the thick client apps with the refreshed data when its changed by a connected app. SignalR becomes very useful in these scenario to push the notification. But another important aspect of it is to track the modifcations done so that these updtae notification can be sent to other connected apps.

Background

In my previous article on SignalR. https://www.codeproject.com/Tips/5300866/Exploring-Signal-R-to-make-WPF-Application-Interac

I had explained on how a WPF application can be made more interactive and deployment friendly by using SignalR to our advantage. Today is a step ahead where we are tracking DB changes using Entity Framework and pushing those change notification using SignalR to client applications.

Using the code

Entity Framework being the current industry standard for data access, I have created a sample application using EF 6 and SignalR to demo the tracking of changes and notification using SignalR. We start by identification of changes via the BaseContext (base DbContext) class. This is the class which should be implemented by other Databases/ context classes. We override the SaveChanges method. The changes are identified and tracked using the ProcessChnages method. For each addition or modification tracked a new Audit event type of entity is created added to audit transaction.

Shrink ▲   Copy Code
public void ProcessChanges()
        {
            foreach (var ent in context.ChangeTracker.Entries<IAuditable>()
                .Where(p => p.State == EntityState.Deleted || p.State == EntityState.Modified || p.State == EntityState.Added))
            {
                //Set the update user/time
                SetAuditTimestamps(ent);


                if (ent.State == EntityState.Added)
                {
                    //Save for processing later, when the ID is generated
                    AdditionEntities.Add(ent);
                }
                else
                {
                    //Process now
                    ModifyEvents.Add(CreateAuditEvent(ent));
                }

            }
        }

        public void TrackModifications()
        {
            if (ModifyEvents.Count == 0 && AdditionEntities.Count == 0)
                return;
            this.auditTxn = new AuditTxn()
            {
                Date = DateTime.Now,
                ServerName = Environment.MachineName,
                AuditEvents = new List<AuditEvent>()
            };
            auditTxn.AuditEvents.AddRange(ModifyEvents);
            context.Set<AuditTxn>().Add(auditTxn);
        }

        public void TrackAdditions()
        {
            if (ModifyEvents.Count == 0 && AdditionEntities.Count == 0)
                return;
            auditTxn.AuditEvents.AddRange(AdditionEntities.Select(ae => CreateAuditEvent(ae, true)));
        }

The SignalRExtensions classes contains the SignalR methods to broadcast these audit events to connected clients.  The class contains a ExcludeAuditEvents collection which can be used for scenarios when we do not want to send notification for modification made to certain domain classes.

Shrink ▲   Copy Code
public static void BroadcastEntityUpdates<T>(this T ctx) where T : IAuditableContext
        {
            var auditTxns = ctx.AuditTxns.Local;
            

            var auditTxn = auditTxns.FirstOrDefault();
            if (auditTxn == null)
            {
                return;
            }
            var context = GlobalHost.ConnectionManager.GetHubContext<BroadcastHub>();

            var notification = new EntityNotification()
            {
                AuditTxnId = auditTxn.AuditTxnId,
                Date = auditTxn.Date,
                ServerName = auditTxn.ServerName,
                EntityEvents = auditTxn.AuditEvents.Where(a => !ExcludedAuditEvents.Contains(a.ObjectType)).Select(e => new EntityEvent()
                {
                    AuditEventId = e.AuditEventId,
                    ObjectId = e.ObjectId,
                    ObjectType = e.ObjectType,
                    Payload = ctx.GetPayload(e),
                    EventType = e.EventType == AuditEventTypes.Added ? UpdateTypes.Added : e.EventType == AuditEventTypes.Deleted ? UpdateTypes.Deleted : UpdateTypes.Modified,
                }).ToList()
            };
            context.Clients.All.UpdateEntities(notification);
        }

Now the client application should work as explained in my previous article "https://www.codeproject.com/Tips/5300866/Exploring-Signal-R-to-make-WPF-" where the onmessage handler can be configured to reload the EF data using the ObjectId and ObjectType passed in the EntityNotification class.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK