using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; using Abp.Collections.Extensions; using Abp.Dependency; using Abp.Domain.Uow; using Abp.Runtime.Caching; using Abp.Runtime.Session; namespace Abp.Configuration { /// /// This class implements to manage setting values in the database. /// public class SettingManager : ISettingManager, ISingletonDependency { public const string ApplicationSettingsCacheKey = "ApplicationSettings"; /// /// Reference to the current Session. /// public IAbpSession AbpSession { get; set; } /// /// Reference to the setting store. /// public ISettingStore SettingStore { get; set; } private readonly ISettingDefinitionManager _settingDefinitionManager; private readonly ITypedCache> _applicationSettingCache; private readonly ITypedCache> _tenantSettingCache; private readonly ITypedCache> _userSettingCache; /// public SettingManager(ISettingDefinitionManager settingDefinitionManager, ICacheManager cacheManager) { _settingDefinitionManager = settingDefinitionManager; AbpSession = NullAbpSession.Instance; SettingStore = DefaultConfigSettingStore.Instance; _applicationSettingCache = cacheManager.GetApplicationSettingsCache(); _tenantSettingCache = cacheManager.GetTenantSettingsCache(); _userSettingCache = cacheManager.GetUserSettingsCache(); } #region Public methods /// public Task GetSettingValueAsync(string name) { return GetSettingValueInternalAsync(name, AbpSession.TenantId, AbpSession.UserId); } public Task GetSettingValueForApplicationAsync(string name) { return GetSettingValueInternalAsync(name); } public Task GetSettingValueForApplicationAsync(string name, bool fallbackToDefault) { return GetSettingValueInternalAsync(name, fallbackToDefault: fallbackToDefault); } public Task GetSettingValueForTenantAsync(string name, int tenantId) { return GetSettingValueInternalAsync(name, tenantId); } public Task GetSettingValueForTenantAsync(string name, int tenantId, bool fallbackToDefault) { return GetSettingValueInternalAsync(name, tenantId, fallbackToDefault: fallbackToDefault); } public Task GetSettingValueForUserAsync(string name, int? tenantId, long userId) { return GetSettingValueInternalAsync(name, tenantId, userId); } public Task GetSettingValueForUserAsync(string name, int? tenantId, long userId, bool fallbackToDefault) { return GetSettingValueInternalAsync(name, tenantId, userId, fallbackToDefault); } public async Task> GetAllSettingValuesAsync() { return await GetAllSettingValuesAsync(SettingScopes.Application | SettingScopes.Tenant | SettingScopes.User); } /// public async Task> GetAllSettingValuesAsync(SettingScopes scopes) { var settingDefinitions = new Dictionary(); var settingValues = new Dictionary(); //Fill all setting with default values. foreach (var setting in _settingDefinitionManager.GetAllSettingDefinitions()) { settingDefinitions[setting.Name] = setting; settingValues[setting.Name] = new SettingValueObject(setting.Name, setting.DefaultValue); } //Overwrite application settings if (scopes.HasFlag(SettingScopes.Application)) { foreach (var settingValue in await GetAllSettingValuesForApplicationAsync()) { var setting = settingDefinitions.GetOrDefault(settingValue.Name); //TODO: Conditions get complicated, try to simplify it if (setting == null || !setting.Scopes.HasFlag(SettingScopes.Application)) { continue; } if (!setting.IsInherited && ((setting.Scopes.HasFlag(SettingScopes.Tenant) && AbpSession.TenantId.HasValue) || (setting.Scopes.HasFlag(SettingScopes.User) && AbpSession.UserId.HasValue))) { continue; } settingValues[settingValue.Name] = new SettingValueObject(settingValue.Name, settingValue.Value); } } //Overwrite tenant settings if (scopes.HasFlag(SettingScopes.Tenant) && AbpSession.TenantId.HasValue) { foreach (var settingValue in await GetAllSettingValuesForTenantAsync(AbpSession.TenantId.Value)) { var setting = settingDefinitions.GetOrDefault(settingValue.Name); //TODO: Conditions get complicated, try to simplify it if (setting == null || !setting.Scopes.HasFlag(SettingScopes.Tenant)) { continue; } if (!setting.IsInherited && (setting.Scopes.HasFlag(SettingScopes.User) && AbpSession.UserId.HasValue)) { continue; } settingValues[settingValue.Name] = new SettingValueObject(settingValue.Name, settingValue.Value); } } //Overwrite user settings if (scopes.HasFlag(SettingScopes.User) && AbpSession.UserId.HasValue) { foreach (var settingValue in await GetAllSettingValuesForUserAsync(AbpSession.ToUserIdentifier())) { var setting = settingDefinitions.GetOrDefault(settingValue.Name); if (setting != null && setting.Scopes.HasFlag(SettingScopes.User)) { settingValues[settingValue.Name] = new SettingValueObject(settingValue.Name, settingValue.Value); } } } return settingValues.Values.ToImmutableList(); } /// public async Task> GetAllSettingValuesForApplicationAsync() { return (await GetApplicationSettingsAsync()).Values .Select(setting => new SettingValueObject(setting.Name, setting.Value)) .ToImmutableList(); } /// public async Task> GetAllSettingValuesForTenantAsync(int tenantId) { return (await GetReadOnlyTenantSettings(tenantId)).Values .Select(setting => new SettingValueObject(setting.Name, setting.Value)) .ToImmutableList(); } /// public Task> GetAllSettingValuesForUserAsync(long userId) { return GetAllSettingValuesForUserAsync(new UserIdentifier(AbpSession.TenantId, userId)); } public async Task> GetAllSettingValuesForUserAsync(UserIdentifier user) { return (await GetReadOnlyUserSettings(user)).Values .Select(setting => new SettingValueObject(setting.Name, setting.Value)) .ToImmutableList(); } /// [UnitOfWork] public virtual async Task ChangeSettingForApplicationAsync(string name, string value) { await InsertOrUpdateOrDeleteSettingValueAsync(name, value, null, null); await _applicationSettingCache.RemoveAsync(ApplicationSettingsCacheKey); } /// [UnitOfWork] public virtual async Task ChangeSettingForTenantAsync(int tenantId, string name, string value) { await InsertOrUpdateOrDeleteSettingValueAsync(name, value, tenantId, null); await _tenantSettingCache.RemoveAsync(tenantId); } /// [UnitOfWork] public virtual Task ChangeSettingForUserAsync(long userId, string name, string value) { return ChangeSettingForUserAsync(new UserIdentifier(AbpSession.TenantId, userId), name, value); } public async Task ChangeSettingForUserAsync(UserIdentifier user, string name, string value) { await InsertOrUpdateOrDeleteSettingValueAsync(name, value, user.TenantId, user.UserId); await _userSettingCache.RemoveAsync(user.ToUserIdentifierString()); } #endregion #region Private methods private async Task GetSettingValueInternalAsync(string name, int? tenantId = null, long? userId = null, bool fallbackToDefault = true) { var settingDefinition = _settingDefinitionManager.GetSettingDefinition(name); //Get for user if defined if (settingDefinition.Scopes.HasFlag(SettingScopes.User) && userId.HasValue) { var settingValue = await GetSettingValueForUserOrNullAsync(new UserIdentifier(tenantId, userId.Value), name); if (settingValue != null) { return settingValue.Value; } if (!fallbackToDefault) { return null; } if (!settingDefinition.IsInherited) { return settingDefinition.DefaultValue; } } //Get for tenant if defined if (settingDefinition.Scopes.HasFlag(SettingScopes.Tenant) && tenantId.HasValue) { var settingValue = await GetSettingValueForTenantOrNullAsync(tenantId.Value, name); if (settingValue != null) { return settingValue.Value; } if (!fallbackToDefault) { return null; } if (!settingDefinition.IsInherited) { return settingDefinition.DefaultValue; } } //Get for application if defined if (settingDefinition.Scopes.HasFlag(SettingScopes.Application)) { var settingValue = await GetSettingValueForApplicationOrNullAsync(name); if (settingValue != null) { return settingValue.Value; } if (!fallbackToDefault) { return null; } } //Not defined, get default value return settingDefinition.DefaultValue; } private async Task InsertOrUpdateOrDeleteSettingValueAsync(string name, string value, int? tenantId, long? userId) { var settingDefinition = _settingDefinitionManager.GetSettingDefinition(name); var settingValue = await SettingStore.GetSettingOrNullAsync(tenantId, userId, name); //Determine defaultValue var defaultValue = settingDefinition.DefaultValue; if (settingDefinition.IsInherited) { //For Tenant and User, Application's value overrides Setting Definition's default value. if (tenantId.HasValue || userId.HasValue) { var applicationValue = await GetSettingValueForApplicationOrNullAsync(name); if (applicationValue != null) { defaultValue = applicationValue.Value; } } //For User, Tenants's value overrides Application's default value. if (userId.HasValue && tenantId.HasValue) { var tenantValue = await GetSettingValueForTenantOrNullAsync(tenantId.Value, name); if (tenantValue != null) { defaultValue = tenantValue.Value; } } } //No need to store on database if the value is the default value if (value == defaultValue) { if (settingValue != null) { await SettingStore.DeleteAsync(settingValue); } return null; } //If it's not default value and not stored on database, then insert it if (settingValue == null) { settingValue = new SettingInfo { TenantId = tenantId, UserId = userId, Name = name, Value = value }; await SettingStore.CreateAsync(settingValue); return settingValue; } //It's same value in database, no need to update if (settingValue.Value == value) { return settingValue; } //Update the setting on database. settingValue.Value = value; await SettingStore.UpdateAsync(settingValue); return settingValue; } private async Task GetSettingValueForApplicationOrNullAsync(string name) { return (await GetApplicationSettingsAsync()).GetOrDefault(name); } private async Task GetSettingValueForTenantOrNullAsync(int tenantId, string name) { return (await GetReadOnlyTenantSettings(tenantId)).GetOrDefault(name); } private async Task GetSettingValueForUserOrNullAsync(UserIdentifier user, string name) { return (await GetReadOnlyUserSettings(user)).GetOrDefault(name); } private async Task> GetApplicationSettingsAsync() { return await _applicationSettingCache.GetAsync(ApplicationSettingsCacheKey, async () => { var dictionary = new Dictionary(); var settingValues = await SettingStore.GetAllListAsync(null, null); foreach (var settingValue in settingValues) { dictionary[settingValue.Name] = settingValue; } return dictionary; }); } private async Task> GetReadOnlyTenantSettings(int tenantId) { var cachedDictionary = await GetTenantSettingsFromCache(tenantId); lock (cachedDictionary) { return cachedDictionary.ToImmutableDictionary(); } } private async Task> GetReadOnlyUserSettings(UserIdentifier user) { var cachedDictionary = await GetUserSettingsFromCache(user); lock (cachedDictionary) { return cachedDictionary.ToImmutableDictionary(); } } private async Task> GetTenantSettingsFromCache(int tenantId) { return await _tenantSettingCache.GetAsync( tenantId, async () => { var dictionary = new Dictionary(); var settingValues = await SettingStore.GetAllListAsync(tenantId, null); foreach (var settingValue in settingValues) { dictionary[settingValue.Name] = settingValue; } return dictionary; }); } private async Task> GetUserSettingsFromCache(UserIdentifier user) { return await _userSettingCache.GetAsync( user.ToUserIdentifierString(), async () => { var dictionary = new Dictionary(); var settingValues = await SettingStore.GetAllListAsync(user.TenantId, user.UserId); foreach (var settingValue in settingValues) { dictionary[settingValue.Name] = settingValue; } return dictionary; }); } public Task GetSettingValueForUserAsync(string name, UserIdentifier user) { Check.NotNull(name, nameof(name)); Check.NotNull(user, nameof(user)); return GetSettingValueForUserAsync(name, user.TenantId, user.UserId); } #endregion #region Nested classes private class SettingValueObject : ISettingValue { public string Name { get; private set; } public string Value { get; private set; } public SettingValueObject(string name, string value) { Value = value; Name = name; } } #endregion } }