/// <summary> /// Updates settings for a store. /// </summary> /// <param name="settings">Settings class instance.</param> /// <param name="form">Form value collection.</param> /// <param name="storeId">Store identifier.</param> /// <param name="settingService">Setting service.</param> /// <param name="propertyNameMapper">Function to map property names. Return <c>null</c> to skip a property.</param> public async Task UpdateSettingsAsync( object settings, IFormCollection form, int storeId, Func <string, string> propertyNameMapper = null) { var settingType = settings.GetType(); var settingName = settingType.Name; var settingProperties = FastProperty.GetProperties(settingType).Values; foreach (var prop in settingProperties) { var name = propertyNameMapper?.Invoke(prop.Name) ?? prop.Name; if (!name.HasValue()) { continue; } var key = string.Concat(settingName, ".", name); if (storeId == 0 || IsOverrideChecked(key, form)) { dynamic value = prop.GetValue(settings); await _settingService.ApplySettingAsync(key, value ?? string.Empty, storeId); } else if (storeId > 0) { await _settingService.RemoveSettingAsync(key, storeId); } } await _db.SaveChangesAsync(); }
public ApplySettingResult ApplyUserSetting <T>(ProviderMetadata metadata, Expression <Func <ProviderMetadata, T> > propertyAccessor) { Guard.NotNull(metadata, nameof(metadata)); Guard.NotNull(propertyAccessor, nameof(propertyAccessor)); var settingKey = metadata.SettingKeyPattern.FormatInvariant(metadata.SystemName, propertyAccessor.ExtractPropertyInfo().Name); var value = propertyAccessor.Compile().Invoke(metadata); return(_settingService.ApplySettingAsync(settingKey, value).Await()); }
/// <summary> /// Applies setting value. The caller is responsible for database commit. /// </summary> /// <typeparam name="T">Entity type</typeparam> /// <typeparam name="TPropType">Property type</typeparam> /// <param name="settings">Settings</param> /// <param name="keySelector">Key selector</param> /// <param name="storeId">Store ID</param> public static async Task <ApplySettingResult> ApplySettingAsync <T, TPropType>(this ISettingService service, T settings, Expression <Func <T, TPropType> > keySelector, int storeId = 0) where T : ISettings, new() { var propInfo = keySelector.ExtractPropertyInfo(); var key = string.Concat(typeof(T).Name, ".", propInfo.Name); // Duck typing is not supported in C#. That's why we're using dynamic type. var fastProp = FastProperty.GetProperty(propInfo, PropertyCachingStrategy.EagerCached); dynamic currentValue = fastProp.GetValue(settings); return(await service.ApplySettingAsync(key, currentValue, storeId)); }
/// <summary> /// Applies or removes a setting property. The caller is responsible for database commit. /// </summary> /// <typeparam name="T">Entity type</typeparam> /// <typeparam name="TPropType">Property type</typeparam> /// <param name="settings">Settings</param> /// <param name="keySelector">Key selector</param> /// <param name="storeId">Store ID</param> /// <returns><c>true</c> when the setting property has been modified.</returns> public static async Task <ApplySettingResult> UpdateSettingAsync <T, TPropType>(this ISettingService service, T settings, Expression <Func <T, TPropType> > keySelector, bool overrideForStore, int storeId = 0) where T : ISettings, new() { if (overrideForStore || storeId == 0) { return(await service.ApplySettingAsync(settings, keySelector, storeId)); } else if (storeId > 0 && await service.RemoveSettingAsync(settings, keySelector, storeId)) { return(ApplySettingResult.Deleted); } return(ApplySettingResult.Unchanged); }
public async Task Run(TaskExecutionContext ctx, CancellationToken cancelToken = default) { if (!_currencySettings.AutoUpdateEnabled) { return; } long lastUpdateTimeTicks = _currencySettings.LastUpdateTime; DateTime lastUpdateTime = DateTime.FromBinary(lastUpdateTimeTicks); lastUpdateTime = DateTime.SpecifyKind(lastUpdateTime, DateTimeKind.Utc); // Don't update currencies if last execution time is less then an hour in the past. if (lastUpdateTime.AddHours(1) < DateTime.UtcNow) { var exchangeRates = await _currencyService .GetCurrencyLiveRatesAsync(_storeContext.CurrentStore.PrimaryExchangeRateCurrency.CurrencyCode); var currencyCodes = exchangeRates.Select(x => x.CurrencyCode).Distinct().ToArray(); var currencies = await _db.Currencies .Where(x => currencyCodes.Contains(x.CurrencyCode)) .ToDictionaryAsync(x => x.CurrencyCode, StringComparer.OrdinalIgnoreCase); foreach (var exchageRate in exchangeRates) { if (currencies.TryGetValue(exchageRate.CurrencyCode, out var currency) && currency.Rate != exchageRate.Rate) { currency.Rate = exchageRate.Rate; } } // Save new current date as last execution time. _currencySettings.LastUpdateTime = DateTime.UtcNow.ToBinary(); await _settingService.ApplySettingAsync(_currencySettings, x => x.LastUpdateTime); await _db.SaveChangesAsync(cancelToken); } }
public virtual async Task <bool> MoveAsync( Provider <IMediaStorageProvider> sourceProvider, Provider <IMediaStorageProvider> targetProvider, CancellationToken cancelToken = default) { Guard.NotNull(sourceProvider, nameof(sourceProvider)); Guard.NotNull(targetProvider, nameof(targetProvider)); // Source must support sending if (sourceProvider.Value is not IMediaSender sender) { throw new ArgumentException(T("Admin.Media.StorageMovingNotSupported", sourceProvider.Metadata.SystemName)); } // Target must support receiving if (targetProvider.Value is not IMediaReceiver receiver) { throw new ArgumentException(T("Admin.Media.StorageMovingNotSupported", targetProvider.Metadata.SystemName)); } // Source and target provider must not be equal if (sender == receiver) { throw new ArgumentException(T("Admin.Media.CannotMoveToSameProvider")); } var success = false; var utcNow = DateTime.UtcNow; var context = new MediaMoverContext(sender, receiver); // We are about to process data in chunks but want to commit ALL at once after ALL chunks have been processed successfully. // AutoDetectChanges true required for newly inserted binary data. using (var scope = new DbContextScope(ctx: _db, autoDetectChanges: true, retainConnection: true)) { using (var transaction = await _db.Database.BeginTransactionAsync(cancelToken)) { try { var pager = new FastPager <MediaFile>(_db.MediaFiles, PAGE_SIZE); while ((await pager.ReadNextPageAsync <MediaFile>()).Out(out var files)) { if (cancelToken.IsCancellationRequested) { break; } foreach (var file in files) { // Move item from source to target await sender.MoveToAsync(receiver, context, file); file.UpdatedOnUtc = utcNow; ++context.MovedItems; } if (!cancelToken.IsCancellationRequested) { await _db.SaveChangesAsync(cancelToken); // Detach all entities from previous page to save memory scope.DbContext.DetachEntities(files, deep: true); } } if (!cancelToken.IsCancellationRequested) { await transaction.CommitAsync(cancelToken); success = true; } else { success = false; await transaction.RollbackAsync(CancellationToken.None); } } catch (Exception exception) { success = false; await transaction.RollbackAsync(cancelToken); _notifier.Error(exception); Logger.Error(exception); } } } if (success) { await _settingService.ApplySettingAsync("Media.Storage.Provider", targetProvider.Metadata.SystemName); await _db.SaveChangesAsync(cancelToken); } // Inform both provider about ending await sender.OnCompletedAsync(context, success, cancelToken); await receiver.OnCompletedAsync(context, success, cancelToken); return(success); }