/// <summary>
        /// Raised AFTER MSAL added the new token in its in-memory copy of the cache.
        /// This notification is called every time MSAL accessed the cache, not just when a write took place:
        /// If MSAL's current operation resulted in a cache change, the property TokenCacheNotificationArgs.HasStateChanged will be set to true.
        /// If that is the case, we call the TokenCache.Serialize() to get a binary blob representing the latest cache content – and persist it.
        /// </summary>
        /// <param name="args">Contains parameters used by the MSAL call accessing the cache.</param>
        private void AppTokenCacheAfterAccessNotification(TokenCacheNotificationArgs args)
        {
            // if state changed, i.e. new token obtained
            if (args.HasStateChanged && !string.IsNullOrWhiteSpace(this.ActiveClientId))
            {
                if (this.InMemoryCache == null)
                {
                    this.InMemoryCache = new AppTokenCache
                    {
                        ClientID = this.ActiveClientId
                    };
                }

                this.InMemoryCache.CacheBits = this.DataProtector.Protect(this.ApptokenCache.SerializeMsalV3());
                this.InMemoryCache.LastWrite = DateTime.Now;

                try
                {
                    // Update the DB and the lastwrite
                    this.TokenCacheDb.Entry(InMemoryCache).State = InMemoryCache.AppTokenCacheId == 0 ? EntityState.Added : EntityState.Modified;
                    this.TokenCacheDb.SaveChanges();
                }
                catch (DbUpdateConcurrencyException)
                {
                    // Record already updated on a different thread, so just read the updated record
                    this.ReadCacheForSignedInApp();
                }
            }
        }
        /// <summary>
        /// Reads the cache data from the backend database.
        /// </summary>
        private void ReadCacheForSignedInApp()
        {
            if (this.InMemoryCache == null) // first time access
            {
                this.InMemoryCache = GetLatestAppRecordQuery().FirstOrDefault();
            }
            else
            {
                // retrieve last written record from the DB
                var lastwriteInDb = GetLatestAppRecordQuery().Select(n => n.LastWrite).FirstOrDefault();

                // if the persisted copy is newer than the in-memory copy
                if (lastwriteInDb > this.InMemoryCache.LastWrite)
                {
                    // read from from storage, update in-memory copy
                    this.InMemoryCache = GetLatestAppRecordQuery().FirstOrDefault();
                }
            }

            // Send data to the TokenCache instance
            this.ApptokenCache.DeserializeMsalV3((this.InMemoryCache == null) ? null : this.DataProtector.Unprotect(this.InMemoryCache.CacheBits));
        }
        /// <summary>
        /// Reads the cache data from the backend database.
        /// </summary>
        private void ReadCacheForSignedInApp(TokenCacheNotificationArgs args)
        {
            // first time access
            if (this.inMemoryCache == null)
            {
                this.inMemoryCache = this.GetLatestAppRecordQuery().FirstOrDefault();
            }
            else
            {
                // retrieve last written record from the DB
                var lastwriteInDb = this.GetLatestAppRecordQuery().Select(n => n.LastWrite).FirstOrDefault();

                // if the persisted copy is newer than the in-memory copy
                if (lastwriteInDb > this.inMemoryCache.LastWrite)
                {
                    // read from from storage, update in-memory copy
                    this.inMemoryCache = this.GetLatestAppRecordQuery().FirstOrDefault();
                }
            }

            // Send data to the TokenCache instance
            args.TokenCache.DeserializeMsalV3((this.inMemoryCache == null) ? null : this.dataProtector.Unprotect(this.inMemoryCache.CacheBits), shouldClearExistingCache: true);
        }