コード例 #1
0
        /// <summary>
        /// Build a dynamic record containing properties depending on the area and the profile
        /// </summary>
        /// <param name="record"></param>
        /// <param name="area"></param>
        /// <param name="userId"></param>
        /// <param name="profile"></param>
        /// <param name="request"></param>
        /// <returns>true if the record is filtered</returns>
        public bool FilterRecord(DSRecord record, string area, int userId, UserProfile.EUserProfile profile, DSRequest request)
        {
            foreach (KeyValuePair <string, DSTable> table in Tables)
            {
                if (table.Value.Table != record.GetType())
                {
                    continue;
                }

                return(table.Value.FilterRecord(record, area, userId, profile, request));
            }

            return(false);
        }
コード例 #2
0
        /// <summary>
        /// Build a dynamic record containing properties depending on the area and the profile
        /// </summary>
        /// <param name="record"></param>
        /// <param name="area"></param>
        /// <param name="userId"></param>
        /// <param name="profile"></param>
        /// <returns></returns>
        public JObject FilterRecord(DSRecord record, string area, int userId, UserProfile.EUserProfile profile)
        {
            foreach (KeyValuePair <string, DSTable> table in Tables)
            {
                if (table.Value.Table != record.GetType())
                {
                    continue;
                }

                return(table.Value.FilterRecord(record, area, userId, profile));
            }

            return(null);
        }
コード例 #3
0
        /// <summary>
        /// Update the cache within the list of records updated by the transaction
        /// </summary>
        /// <param name="records"></param>
        /// <param name="customerId"></param>
        /// <param name="tick"></param>
        public void UpdateCache(List <Tuple <string, DSRecord, InformationRecord> > records, int customerId, int tick)
        {
            if (!_enable)
            {
                return;
            }

            Lock(customerId); // lock critical section

            // Check if the customer is currently loaded

            if (!_tick.ContainsKey(customerId))
            {
                Unlock(customerId); // unlock critical section
                return;
            }

            try
            {
                // Update the cache within the list of records from the transaction currently executed

                Dictionary <string, Dictionary <int, Tuple <DSRecord, InformationRecord> > > currentTable = _records[customerId];

                foreach (Tuple <string, DSRecord, InformationRecord> record in records)
                {
                    if (!currentTable.ContainsKey(record.Item1))
                    {
                        continue;
                    }

                    currentTable[record.Item1][record.Item2.Id] = Tuple.Create(DSRecord.Copy(record.Item2), InformationRecord.Copy(record.Item3));
                }

                _tick[customerId] = tick;

                if (IsDebug())
                {
                    Debug($"[{customerId}] Cache updated in tick {_tick[customerId]}");
                }
            }
            catch (System.Exception ex)
            {
                Exception($"[{customerId}] Unable to update the cache", ex);
            }

            Unlock(customerId); // unlock critical section
        }
コード例 #4
0
        /// <summary>
        /// Retrieve a list of tuple (DSRecord, Table) attached to a given record (table, id) for the given profile
        /// This function is used to retrieve a list of records attached to the current update
        /// Example :
        ///    The object A is not visible for the user Y
        ///    The user X updates the object A
        ///    The object A becomes visible for the user Y
        ///    In that case, object A must be added and notified to the user Y
        ///    This function builds this case
        /// </summary>
        /// <param name="cache"></param>
        /// <param name="table"></param>
        /// <param name="id"></param>
        /// <param name="customerId"></param>
        /// <param name="userId"></param>
        /// <param name="profile"></param>
        /// <param name="area"></param>
        /// <param name="deepUpdate"></param>
        /// <param name="recordAlreadyRead"></param>
        /// <param name="informationAlreadyRead"></param>
        /// <returns></returns>
        virtual public void GetListRecordsConcernedByUpdate(DSCache cache,
                                                            string table,
                                                            int id,
                                                            int customerId,
                                                            int userId,
                                                            UserProfile.EUserProfile profile,
                                                            string area,
                                                            bool deepUpdate,
                                                            DSRecord recordAlreadyRead,
                                                            InformationRecord informationAlreadyRead)
        {
            if (id < 0 || profile != UserProfile.EUserProfile.None || cache.Is(table, id) != null)
            {
                return;
            }

            cache.Set(table, id, null);
        }
コード例 #5
0
        /// <summary>
        /// Retrieve the userId into the record
        /// </summary>
        /// <param name="record"></param>
        /// <returns></returns>
        public int?GetUserId(DSRecord record)
        {
            if (_field == null)
            {
                _field = record.GetType().GetProperty(FieldUserId);
            }

            if (_field == null)
            {
                return(null);
            }

            object value = _field.GetValue(record);

            if (value == null)
            {
                return(null);
            }

            return((int)value);
        }
コード例 #6
0
        /// <summary>
        /// This static function checks if the list of restrictions limits the access to the element
        /// on depends on the current area and the current profile
        /// </summary>
        /// <param name="allows"></param>
        /// <param name="area">Null, "Create", "Read", "Update" or "Delete"</param>
        /// <param name="profile"></param>
        /// <param name="action"></param>
        /// <param name="userId"></param>
        /// <param name="record"></param>
        public static bool IsAllowed(IEnumerable <DSAllowAttribute> allows, string area, UserProfile.EUserProfile profile, string action, int userId, DSRecord record)
        {
            // everything is allowed ?

            if (!allows.Any())
            {
                return(true);
            }

            if (area == null && profile == UserProfile.EUserProfile.None)
            {
                return(true);
            }

            // if the record is null, not allowed!

            if (record == null)
            {
                return(false);
            }

            foreach (DSAllowAttribute allow in allows)
            {
                // By default: Nobody can access to this table

                if (allow.Area == null && allow.Profile == UserProfile.EUserProfile.None && allow.Action == null)
                {
                    continue;
                }

                // Check area (if restriction.Area = "*" : allow all areas)

                if (allow.Area != null && !allow.Area.Equals("*") && area != null && !allow.Area.Equals(area))
                {
                    continue;
                }

                // Check profile

                if (allow.Profile != UserProfile.EUserProfile.None && profile != UserProfile.EUserProfile.None && !UserProfile.IsInRole(profile, allow.Profile))
                {
                    continue;
                }

                // Check action

                if (allow.Action != null && !allow.Action.Equals("*") && action != null && !allow.Action.Equals(action))
                {
                    continue;
                }

                // retrieve the field if it doesn't exist

                if (allow.FieldUserId != null)
                {
                    int?currentUserId = allow.GetUserId(record);

                    if (currentUserId == null || currentUserId.Value != userId)
                    {
                        continue;
                    }
                }

                return(true);
            }

            return(false);
        }
コード例 #7
0
 /// <summary>
 /// This function is called after deleting the record ... used to complete the deletion
 /// </summary>
 /// <param name="database"></param>
 /// <param name="tick"></param>
 /// <param name="table"></param>
 /// <param name="record"></param>
 public void OnAfterDeleteRecord(Common.Database.DatabaseContext database, int tick, string table, DSRecord record)
 {
 }
コード例 #8
0
        /// <summary>
        /// This function is called after updating the record ... used to complete the update
        /// </summary>
        /// <param name="database"></param>
        /// <param name="tick"></param>
        /// <param name="table"></param>
        /// <param name="record"></param>
        public void OnAfterUpdateRecord(Common.Database.DatabaseContext database, int tick, string table, DSRecord record)
        {
            if (table.Equals("Language"))
            {
                if (database is Module.Administration.DatabaseContext administration &&
                    record is DSRecordWithCustomerId language)
                {
                    string          tickKey   = $"Language.Tick.{language.CustomerId}";
                    ParameterRecord parameter = database._Parameter.SingleOrDefault(e => e.Key.Equals(tickKey));
                    if (parameter != null)
                    {
                        parameter.Value = tick.ToString();
                    }

                    // Unable to add a line into "_Parameter" due to the lock set into the table "_Parameter"!
                }
            }
        }
コード例 #9
0
        /// <summary>
        /// This function is called after creating the record ... used to complete the creation
        /// </summary>
        /// <param name="database"></param>
        /// <param name="tick"></param>
        /// <param name="table"></param>
        /// <param name="record"></param>
        public void OnAfterCreateRecord(Common.Database.DatabaseContext database, int tick, string table, DSRecord record)
        {
            if (table.Equals("User"))
            {
                if (record is DSRecordWithCustomerId user &&
                    database is Module.Administration.DatabaseContext administration)
                {
                    // Create the first notification or reports ...

                    administration.Notification.Add(new NotificationRecord()
                    {
                        UserId = user.Id, CustomerId = user.CustomerId, LastTick = tick, Date = DateTime.Now, Report = NotificationRecord.NOTIFICATION
                    });
                    administration.SaveChanges();
                }
            }

            /* TODO : Handle AttachedFile
             * else if (table.Equals("AttachedFile"))
             * {
             *  if (record is AttachedFileRecord attachment &&
             *      database is Module.Stock.DatabaseContext stock)
             *  {
             *      // Load the file, save it into the table and delete it
             *
             *      string filename = System.Web.HttpContext.Current.Server.MapPath($"~/App_Data/{attachment.FileId:D6}_{attachment.Filename}");
             *      Debug($"Loading file {filename} into the database {attachment.ToString()} ...");
             *
             *      AttachedFileContentRecord content = null;
             *      if (System.IO.File.Exists(filename))
             *      {
             *          try
             *          {
             *              content = new AttachedFileContentRecord() { Content = System.IO.File.ReadAllBytes(filename), CustomerId = attachment.CustomerId };
             *              stock.AttachedFileContent.Add(content);
             *              stock.SaveChanges();
             *
             *              // update the fileId
             *
             *              attachment.FileId = content.Id;
             *              stock.SaveChanges();
             *          }
             *          catch (System.Exception ex)
             *          {
             *              Exception($"Unable to load the file {filename}", ex);
             *              return;
             *          }
             *          attachment.Size = content == null || content.Content == null ? 0 : content.Content.Length;
             *      }
             *      else
             *      {
             *          Warn($"The file {filename} doesn't exist ... The file may be already saved into the database!");
             *          attachment.Date = DateTime.Now;
             *      }
             *
             *      try
             *      {
             *          System.IO.File.Delete(filename);
             *      }
             *      catch (System.Exception)
             *      {
             *          Warn($"Unable to delete the file {filename}");
             *      }
             *
             *      Info($"File {filename} loaded into the database");
             *  }
             * } */
        }
コード例 #10
0
        /// <summary>
        /// Retrieve a list of tuple (DSRecord, Table) attached to a given record (table, id) for the given profile
        /// </summary>
        /// <param name="cache"></param>
        /// <param name="table"></param>
        /// <param name="id"></param>
        /// <param name="customerId"></param>
        /// <param name="userId"></param>
        /// <param name="profile"></param>
        /// <param name="area"></param>
        /// <param name="deepUpdate"></param>
        /// <param name="recordAlreadyRead"></param>
        /// <param name="informationAlreadyRead"></param>
        /// <returns></returns>
        public override void GetListRecordsConcernedByUpdate(DSCache cache,
                                                             string table,
                                                             int id,
                                                             int customerId,
                                                             int userId,
                                                             UserProfile.EUserProfile profile,
                                                             string area,
                                                             bool deepUpdate,
                                                             DSRecord recordAlreadyRead,
                                                             InformationRecord informationAlreadyRead)
        {
            if (id < 0 || profile == UserProfile.EUserProfile.None || cache.Is(table, id) != null)
            {
                return;
            }

            // Retrieve the current record

            DSRecord currentRecord = null;
            Tuple <DSRecord, InformationRecord> currentRecordFromCache = cache.GetRecord(this, table, id);

            if (currentRecordFromCache == null && recordAlreadyRead == null)
            {
                switch (table)
                {
                case "Customer":
                    currentRecord = Customer.Find(id);
                    break;

                case "Language":
                    currentRecord = Language.Find(id);
                    break;

                case "User":
                    currentRecord = User.Find(id);
                    break;

                case "Module":
                    currentRecord = Module.Find(id);
                    break;

                case "UserModule":
                    currentRecord = UserModule.Find(id);
                    break;

                default:
                    base.GetListRecordsConcernedByUpdate(cache, table, id, customerId, userId, profile, area, deepUpdate, recordAlreadyRead, informationAlreadyRead);
                    return;
                }

                if (currentRecord == null)
                {
                    cache.Set(table, id, null);
                    return;
                }

                currentRecordFromCache = cache.SetRecord(this, table, id, currentRecord, null);
            }
            else if (currentRecordFromCache == null && recordAlreadyRead != null)
            {
                currentRecordFromCache = cache.SetRecord(this, table, id, recordAlreadyRead, informationAlreadyRead);
            }

            if (currentRecordFromCache != null)
            {
                currentRecord = currentRecordFromCache.Item1;
            }

            // Check if the record is concerned by the current update

            if (currentRecord is DSRecordWithCustomerId currentCustomerRecord)
            {
                if (currentCustomerRecord.CustomerId != customerId)
                {
                    cache.Set(table, id, null);
                    return;
                }
            }

            if (currentRecord is CustomerRecord customer)
            {
                cache.Set(table, id, customer.Id != customerId ? null : currentRecord);
                return;
            }

            if (currentRecord as LanguageRecord != null ||
                currentRecord as ModuleRecord != null ||
                currentRecord as UserModuleRecord != null ||
                currentRecord as UserRecord != null)
            {
                cache.Set(table, id, currentRecord);
                return;
            }

            base.GetListRecordsConcernedByUpdate(cache, table, id, customerId, userId, profile, area, deepUpdate, recordAlreadyRead, informationAlreadyRead);
        }
コード例 #11
0
        /// <summary>
        /// This function is called after deleting the record ... used to complete the deletion
        /// </summary>
        /// <param name="database"></param>
        /// <param name="tick"></param>
        /// <param name="table"></param>
        /// <param name="record"></param>
        public void OnAfterDeleteRecord(DatabaseContext database, int tick, string table, DSRecord record)
        {
            if (_request == null)
            {
                return;
            }

            _request.OnAfterDeleteRecord(database, tick, table, record);
        }
コード例 #12
0
        /// <summary>
        /// Execute a request on the database schema (Create, Update or Delete a record for an existing table)
        /// on depends on the restriction view for the area and the profile
        /// </summary>
        /// <param name="database"></param>
        /// <param name="tick"></param>
        /// <param name="customerId"></param>
        /// <param name="userId"></param>
        /// <param name="area"></param>
        /// <param name="profile"></param>
        /// <param name="table"></param>
        /// <param name="action"></param>
        /// <param name="id"></param>
        /// <param name="record"></param>
        /// <returns></returns>
        public void OnAfterExecuteRequest(DatabaseContext database, int tick, int customerId, int userId, string area, UserProfile.EUserProfile profile, string table, string action, int id, DSRecord record)
        {
            if (!Tables.ContainsKey(table))
            {
                throw new ExceptionDefinitionRecord("ERR_REQUEST_UNKNOWN");
            }

            Tables[table].OnAfterExecuteRequest(database, tick, customerId, userId, area, profile, action, id, record);
        }
コード例 #13
0
        /// <summary>
        /// Execute a list of requests from a client
        /// </summary>
        /// <param name="transaction"></param>
        /// <returns>RequestId, Error, Record</returns>
        public List <Tuple <DSRecord, InformationRecord> > ExecuteTransaction(DSTransaction transaction)
        {
            Info($"Executing the transaction [{transaction.RequestId}] containing {transaction.Requests.Count} requests ...");

            List <Tuple <DSRecord, InformationRecord> >         recordsTreated  = new List <Tuple <DSRecord, InformationRecord> >();
            List <Tuple <string, DSRecord, InformationRecord> > recordsToUpdate = new List <Tuple <string, DSRecord, InformationRecord> >();

            // Retrieve the database schema

            DSDatabase schema = ConfigurationManager.Schemas[transaction.Area];

            if (schema == null)
            {
                Error("No schema available!");
                throw new ExceptionDefinitionRecord("ERR_SCHEMA");
            }

            // Lock database during the execution of the request

            using (DatabaseLock lockDatabase = Database.Lock(transaction.CustomerId))
            {
                try
                {
                    if (IsVerbose())
                    {
                        Verbose("Getting the first tick of the transaction ...");
                    }

                    // Get the tick

                    string          tickKey    = $"Database.Tick.{transaction.CustomerId}";
                    int             tick       = 0;
                    ParameterRecord tickRecord = Database._Parameter.FirstOrDefault(p => p.Key.Equals(tickKey));
                    if (tickRecord == null)
                    {
                        tickRecord = new ParameterRecord {
                            Key = tickKey, Value = transaction.Requests.Count.ToString()
                        };
                        tickRecord = Database._Parameter.Add(tickRecord);
                    }
                    else
                    {
                        tick             = int.Parse(tickRecord.Value);
                        tickRecord.Value = (tick + transaction.Requests.Count).ToString();
                    }
                    Database.SaveChanges();

                    if (IsDebug())
                    {
                        Debug($"First tick is {tick}");
                    }

                    // Execute the OnBefore trigger

                    if (IsVerbose())
                    {
                        Verbose("Executing the pre-request of the transaction ...");
                    }

                    transaction.SetNewTick(tick);

                    foreach (DSRequest request in transaction.Requests)
                    {
                        // Execute the request

                        if (IsVerboseAll())
                        {
                            Verbose($"Executing the pre-request[{request.Id}] with tick[{request.NewTick}]: {request} ...");
                        }

                        // Execute the trigger before requesting

                        schema.OnBeforeExecuteRequest(Database, request.NewTick,
                                                      transaction.CustomerId, transaction.UserId, transaction.Area, transaction.Profile,
                                                      request.Table, request.Action, request.RecordId, request.Record, request.Identity);
                    }

                    if (IsDebug())
                    {
                        Debug($"Pre-request executed for {transaction.Requests.Count} requests");
                    }

                    // Execute each request

                    if (IsVerbose())
                    {
                        Verbose("Executing the transaction ...");
                    }

                    // Execution by lot

                    List <RequestTableRecord> actions = new List <RequestTableRecord>();

                    int index = 0;
                    foreach (List <DSRequest> lot in transaction.LotRequests)
                    {
                        // Execute the lot of requests

                        if (IsVerbose())
                        {
                            Verbose($"Executing the lot[{index}] with {lot.Count} requests ...");
                        }

                        // Execute the lot of requests

                        recordsTreated.AddRange(schema.ExecuteRequest(Database, transaction, lot));

                        // Saving data

                        Database.SaveChanges();

                        index++;
                    }

                    if (IsVerbose())
                    {
                        Verbose("Building the list of actions ...");
                    }

                    index = 0;
                    foreach (DSRequest request in transaction.Requests)
                    {
                        Tuple <DSRecord, InformationRecord> recordTreated = recordsTreated[index];

                        // Keep in memory all records executed

                        recordsToUpdate.Add(Tuple.Create(request.Table, recordTreated.Item1, recordTreated.Item2));

                        // The request is correctly executed ... Store a new request

                        actions.Add(new RequestTableRecord
                        {
                            Tick       = request.NewTick,
                            CustomerId = transaction.CustomerId,
                            UserId     = transaction.UserId,
                            RequestId  = transaction.RequestId,
                            Table      = request.Table,
                            Action     = request.Action,
                            Id         = (recordTreated == null || recordTreated.Item1 == null ? -1 : recordTreated.Item1.Id)
                        });

                        Info($"The request[{request.Id}] has correctly been executed : {(recordTreated == null ? "null" : recordTreated.ToString())}");

                        index++;
                    }

                    if (IsDebug())
                    {
                        Debug($"Transaction executed with {transaction.Requests.Count} requests");
                    }

                    // Write actions into the RequestTable

                    if (IsVerbose())
                    {
                        Verbose($"Writing {actions.Count} actions into the RequestTable ...");
                    }

                    Database._RequestTable.AddRange(actions);
                    Database.SaveChanges();

                    if (IsDebug())
                    {
                        Debug($"{actions.Count} actions written into the RequestTable");
                    }

                    // Execute the OnAfter trigger

                    if (IsVerbose())
                    {
                        Verbose("Executing the post-request of the transaction ...");
                    }

                    foreach (DSRequest request in transaction.Requests)
                    {
                        // Execute the request

                        if (IsVerboseAll())
                        {
                            Verbose($"Executing the post-request[{request.Id}] with tick[{request.NewTick}]: {request} ...");
                        }

                        // Execute the trigger before requesting

                        DSRecord record = recordsTreated[request.Id].Item1;
                        if (record != null)
                        {
                            schema.OnAfterExecuteRequest(Database, request.NewTick,
                                                         transaction.CustomerId, transaction.UserId, transaction.Area, transaction.Profile,
                                                         request.Table, request.Action, record.Id, record);
                        }
                    }

                    if (IsDebug())
                    {
                        Debug($"Post-request executed for {transaction.Requests.Count} requests");
                    }

                    // Unlock the database and commit all changes

                    if (IsVerbose())
                    {
                        Verbose("Committing changes ...");
                    }

                    lockDatabase.Commit();

                    if (IsVerbose())
                    {
                        Verbose("Changes committed");
                    }

                    // Update the cache manager

                    if (IsVerbose())
                    {
                        Verbose("Updating cache ...");
                    }

                    DatabaseCacheManager.Instance.UpdateCache(recordsToUpdate, transaction.CustomerId, tick + transaction.Requests.Count);

                    if (IsVerbose())
                    {
                        Verbose("Cache updated");
                    }
                }
                catch (System.Exception ex)
                {
                    Exception("An exception occurs on executing the transaction", ex);

                    bool saveFailed = false;

                    do
                    {
                        saveFailed = false;
                        try
                        {
                            Database.SaveChanges();
                        }
                        catch (DbUpdateException ex2)
                        {
                            saveFailed = true;
                            ex2.Entries.Single().Reload();
                        }
                    }while (saveFailed);

                    // Rollback all request already executed

                    throw ex;
                }
            }

            Info("Transaction done");

            return(recordsTreated);
        }
コード例 #14
0
        /// <summary>
        /// Load all labels if it's necessary
        /// </summary>
        /// <param name="database"></param>
        /// <param name="customerId"></param>
        private void Load(DatabaseContext database, int?customerId)
        {
            int lastUpdate = 0;

            _mutex.Wait(); // lock critical section

            if (customerId == null)
            {
                // Load all tickId for each customer or create it if it doesn't exist

                foreach (CustomerRecord customer in database.Customer.ToList())
                {
                    int tick = 0;

                    // Retrieve the tick of the database

                    string          tickKey   = $"Database.Tick.{customer.Id}";
                    ParameterRecord parameter = database._Parameter.FirstOrDefault(e => e.Key.Equals(tickKey));
                    if (parameter != null)
                    {
                        int.TryParse(parameter.Value, out tick);
                    }

                    // Update or retrieve the tick of the language

                    tickKey   = $"Language.Tick.{customer.Id}";
                    parameter = database._Parameter.FirstOrDefault(e => e.Key.Equals(tickKey));
                    if (parameter == null)
                    {
                        database._Parameter.Add(new ParameterRecord()
                        {
                            Key = tickKey, Value = tick.ToString()
                        });
                    }
                    else
                    {
                        parameter.Value = tick.ToString();
                    }

                    _lastUpdate[customer.Id] = tick;
                }

                database.SaveChanges();
            }
            else
            {
                string          tickKey   = $"Language.Tick.{customerId.Value}";
                ParameterRecord parameter = database._Parameter.SingleOrDefault(e => e.Key.Equals(tickKey));

                if (parameter == null)
                {
                    Warn($"Please, create the parameter '{tickKey}' into the database to avoid loading labels every time!");
                }

                if (parameter != null && !String.IsNullOrWhiteSpace(parameter.Value) && !int.TryParse(parameter.Value, out lastUpdate))
                {
                    lastUpdate = 0;
                }

                if (parameter != null && _lastUpdate.ContainsKey(customerId.Value) && lastUpdate == _lastUpdate[customerId.Value] && lastUpdate >= 0)
                {
                    // no changes ...
                    _mutex.Release(); // unlock critical section
                    return;
                }

                // Update the language tick

                if (parameter != null && lastUpdate < 0)
                {
                    string          dbTickKey     = $"Database.Tick.{customerId.Value}";
                    ParameterRecord parameterTick = database._Parameter.FirstOrDefault(e => e.Key.Equals(dbTickKey));
                    if (parameterTick != null)
                    {
                        parameter.Value = parameterTick.Value;
                        database.SaveChanges();
                    }
                }
            }

            if (customerId == null)
            {
                Info("Loading all labels ...");

                try
                {
                    int i = 0;
                    Dictionary <int, List <LanguageRecord> > newLabels = new Dictionary <int, List <LanguageRecord> >();

                    foreach (LanguageRecord label in database.Language.ToList())
                    {
                        if (!newLabels.ContainsKey(label.CustomerId))
                        {
                            newLabels[label.CustomerId] = new List <LanguageRecord>();
                        }

                        newLabels[label.CustomerId].Add(DSRecord.Copy(label) as LanguageRecord);

                        if (IsDebug() && customerId == null && label.CustomerId == 1)
                        {
                            Debug($"{label.Key.Trim()} = {label}");
                        }

                        i++;
                    }

                    Info($"{i} labels loaded");

                    _labels = newLabels;
                }
                catch (Exception ex)
                {
                    Exception("Unable to load all labels", ex);
                }
            }
            else
            {
                Info($"Loading labels because they recently change ('{lastUpdate}') for the customer '{customerId.Value}' ...");

                try
                {
                    int i = 0;
                    List <LanguageRecord> newLabels = new List <LanguageRecord>();

                    foreach (LanguageRecord label in database.Language.Where(l => l.CustomerId == customerId.Value).ToList())
                    {
                        newLabels.Add(DSRecord.Copy(label) as LanguageRecord);
                        i++;
                    }

                    Info($"{i} labels loaded");

                    _labels[customerId.Value]     = newLabels;
                    _lastUpdate[customerId.Value] = lastUpdate;
                }
                catch (Exception ex)
                {
                    Exception($"Unable to load labels for the customer '{customerId.Value}'", ex);
                }
            }

            _mutex.Release(); // unlock critical section

            if (customerId != null)
            {
                DatabaseCacheManager.Instance.Reload(database, customerId.Value, "Language");
            }

            return;
        }