コード例 #1
0
        /// <summary>
        /// Notify clients of changed persistons. This should be called when any persiston changes, in case a client is subscribed to it.
        /// No action will be taken if the change didn't change the version, so it is performant to call this repeatedly, or if it might
        /// not have really changed.
        /// </summary>
        public void NotifyClientsOf(DataDictionary dbdef, Daton[] datons)
        {
            foreach (var cli in Sessions.Values)
            {
                bool thisClientChanged = false;
                foreach (var daton in datons)
                {
                    //skip if this user doesn't need this daton
                    if (!cli.Subscriptions.TryGetValue(daton.Key, out string version))
                    {
                        continue;
                    }
                    if (version == daton.Version)
                    {
                        continue;                           //don't send if client already has the latest version
                    }
                    //remember we sent it
                    cli.Subscriptions[daton.Key] = daton.Version;
                    thisClientChanged            = true;

                    //hide cols by permissions
                    var datondef     = dbdef.FindDef(daton);
                    var trimmedDaton = daton.Clone(datondef);
                    var guard        = new SecurityGuard(dbdef, cli.User);
                    guard.HidePrivateParts(trimmedDaton);

                    //queue it for sending
                    lock (cli.DatonsToPush)
                    {
                        cli.DatonsToPush.Add(trimmedDaton);
                    }
                }

                //send queued datons now
                if (thisClientChanged)
                {
                    var completer = cli.LongPollingCompleter; //another thread could change cli.LongPollingCompleter, so access only through local var
                    if (completer != null)
                    {
                        try { completer.SetResult(true); }
                        catch { } //see note in LongPollingCompleted
                    }
                }
            }
        }
コード例 #2
0
        /// <summary>
        /// Get a daton, from cache or load from database. The reutrn value is a shared instance so the caller may not modify it.
        /// For new unsaved persistons with -1 as the key, this will create the instance with default values.
        /// </summary>
        /// <param name="user">if null, the return value is a shared guaranteed complete daton; if user is provided,
        /// the return value may be a clone with some rows removed or columns set to null</param>
        /// <param name="forceCheckLatest">if true then checks database to ensure latest version even if it was cached</param>
        /// <returns>object with daton, or readable errors</returns>
        public async Task <RetroSql.LoadResult> GetDaton(DatonKey key, IUser user, bool forceCheckLatest = false)
        {
            //new persiston: return now
            if (key.IsNew)
            {
                var   datondef2 = DataDictionary.FindDef(key);
                Daton newDaton  = Utils.Construct(datondef2.Type) as Daton;
                Utils.FixTopLevelDefaultsInNewPersiston(datondef2, newDaton);
                newDaton.Key = key;
                if (datondef2.Initializer != null)
                {
                    await datondef2.Initializer(newDaton);
                }
                return(new RetroSql.LoadResult {
                    Daton = newDaton
                });
            }

            //get from cache if possible, and optionally ignore cached version if it is not the latest
            string verifiedVersion = null;
            Daton  daton           = DatonCache.Get(key);

            if (forceCheckLatest && daton != null)
            {
                //viewons: always ignore cache; persistons: use cached only if known to be latest
                if (daton is Persiston)
                {
                    verifiedVersion = LockManager.GetVersion(key);
                    if (verifiedVersion != daton.Version)
                    {
                        daton = null;
                    }
                }
                else
                {
                    daton = null;
                }
            }

            //get from database if needed (and cache it), or abort
            var datondef = DataDictionary.FindDef(key);

            if (typeof(Persiston).IsAssignableFrom(datondef.Type) && (key is ViewonKey))
            {
                throw new Exception("Persiston requested but key format is for viewon");
            }
            if (typeof(Viewon).IsAssignableFrom(datondef.Type) && (key is PersistonKey))
            {
                throw new Exception("Viewon requested but key format is for persiston");
            }
            if (daton == null)
            {
                var sql = GetSqlInstance(key);
                RetroSql.LoadResult loadResult;
                using (var db = GetDbConnection(datondef.DatabaseNumber))
                    loadResult = await sql.Load(db, DataDictionary, user, key, ViewonPageSize);
                if (loadResult.Daton == null)
                {
                    return(loadResult);
                }
                daton = loadResult.Daton;
                if (verifiedVersion == null && daton is Persiston)
                {
                    verifiedVersion = LockManager.GetVersion(key);
                }
                daton.Version = verifiedVersion;
                DatonCache.Put(daton);
                Diagnostics.IncrementLoadCount();
            }

            //enforce permissions on the user
            if (user != null)
            {
                daton = daton.Clone(datondef);
                var guard = new SecurityGuard(DataDictionary, user);
                guard.HidePrivateParts(daton);
            }

            return(new RetroSql.LoadResult {
                Daton = daton
            });
        }