/// <summary> /// Ivalidates app localizations cache /// </summary> /// <param name="appName"></param> public static void InvalidateAppLocalizationsCache(string appName, string className) { //app localizations cache first if (AppLocalizationsCache.ContainsKey(appName) && string.IsNullOrEmpty(className)) { AppLocalizationsCache.Remove(appName); } else if (AppLocalizationsCache.ContainsKey(appName)) { AppLocalizationsCache.Remove(appName); //FIXME - this requires better reading - need to read from db to 'top up' the cache //var localizations = AppLocalizationsCache[appName].ToList(); //var toBeRemoved = localizations.FirstOrDefault(l => l.ClassName == className); //if(toBeRemoved != null) // localizations.Remove(toBeRemoved); //AppLocalizationsCache[appName] = localizations; } //clients cache too... var keysToInvalidate = ClientLocalizationsCache.Keys.Where(k => k.IndexOf(appName) > -1).ToList(); foreach (var key in keysToInvalidate) { ClientLocalizationsCache.Remove(key); } }
/// <summary> /// Gets localizations for specified lang codes and apps /// </summary> /// <typeparam name="TDbCtx"></typeparam> /// <param name="dbCtx"></param> /// <param name="langCodes"></param> /// <param name="appNames"></param> /// <returns></returns> public static async Task <Dictionary <string, Dictionary <string, Dictionary <string, string> > > > GetAppLocalizationsAsync <TDbCtx>(TDbCtx dbCtx, IEnumerable <string> langCodes, IEnumerable <string> appNames) where TDbCtx : DbContext, ILocalizedDbContext { var ret = new Dictionary <string, Dictionary <string, Dictionary <string, string> > >(); if (appNames == null || !appNames.Any()) { return(ret); } //grab the default lng var defaultLang = await Lang.GetDefaultLangAsync(dbCtx); if (langCodes == null || !langCodes.Any()) { if (defaultLang == null) { return(ret); } //no langs provided, so the calling client may not be aware of the lang yet. //in this scenario just lookup the default lang and get the localization fot the default lng langCodes = new[] { defaultLang.LangCode }; } //see if there is cache for the current combination already var cacheKey = GetClientLocalizationsCacheKey(langCodes, appNames); if (ClientLocalizationsCache.ContainsKey(cacheKey)) { return((Dictionary <string, Dictionary <string, Dictionary <string, string> > >)ClientLocalizationsCache[cacheKey]); } //fetch localizations if needed foreach (var appName in appNames) { if (!AppLocalizationsCache.ContainsKey(appName) || AppLocalizationsCache[appName] == null) { //read all the Localization classes for the given AppName var localizationClasses = await dbCtx.LocalizationClasses.Where(lc => lc.ApplicationName == appName).ToListAsync(); //now grab the identifiers - need them in order to request translation keys. when using IQueryable, the range MUST BE static, simple types //so even though compiler will not complain if a range is passes as localizationClasses.Select(lc => lc.Uuid) it will fail in the runtime //saving this to a variable solves the issue! var localizationClassesIdentifiers = localizationClasses.Select(lc => lc.Uuid); var translationKeys = await dbCtx.TranslationKeys .Where(tk => localizationClassesIdentifiers.Contains(tk.LocalizationClassUuid) && (tk.Inherited != true || tk.Inherited == true && tk.Overwrites == true) //only output not inherited or inherited overwrites! ) .ToListAsync(); AppLocalizationsCache[appName] = localizationClasses.GroupJoin( translationKeys, lc => lc.Uuid, tk => tk.LocalizationClassUuid, (lc, tk) => new LocalizationClass() { ApplicationName = lc.ApplicationName, ClassName = lc.ClassName, InheritedClassName = lc.InheritedClassName, TranslationKeys = tk } ); } var appLocalizations = AppLocalizationsCache[appName]; foreach (var appL in appLocalizations) { var key = $"{appL.ApplicationName}.{appL.ClassName}"; if (!ret.ContainsKey(key)) { ret[key] = new Dictionary <string, Dictionary <string, string> >(); } var classTranslations = ret[key]; foreach (var tk in appL.TranslationKeys) { if (!classTranslations.ContainsKey(tk.Key)) { classTranslations[tk.Key] = new Dictionary <string, string>(); } foreach (var translation in (tk.Translations ?? new Translations()).Where(t => t.Key == defaultLang.LangCode || langCodes.Contains(t.Key))) { classTranslations[tk.Key].Add(translation.Key, translation.Value); } } } } //cahce the output ClientLocalizationsCache[cacheKey] = ret; return(ret); }