/// <summary> /// Tries to read a value from local cache. If it is not found there, tries the distributed cache. /// If neither contains the specified key, produces value by calling a loader function and adds the /// value to local and distributed cache for a given expiration time. By using a group key, /// all items on both cache types that are members of this group can be expired at once. </summary> /// <remarks> /// To not check group generation every time an item is requested, generation number itself is also /// cached in local cache. Thus, when a generation number changes, local cached items might expire /// after about 5 seconds. This means that, if you use this strategy in a web farm setup, when a change /// occurs in one server, other servers might continue to use old local cached data for 5 seconds more. /// If this is a problem for your configuration, use DistributedCache directly. /// </remarks> /// <typeparam name="TItem">Data type</typeparam> /// <param name="cache">Two level cache</param> /// <param name="cacheKey">The item key for local and distributed cache</param> /// <param name="localExpiration">Local expiration</param> /// <param name="remoteExpiration">Distributed cache expiration (is usually same with local expiration)</param> /// <param name="groupKey">Group key that will hold generation (version). Can be used to expire all items /// that depend on it. This can be a table name. When a table changes, you change its version, and all /// cached data that depends on that table is expired.</param> /// <param name="loader">The delegate that will be called to generate value, if not found in local cache, /// or distributed cache, or all found items are expired.</param> public static TItem Get <TItem>(this ITwoLevelCache cache, string cacheKey, TimeSpan localExpiration, TimeSpan remoteExpiration, string groupKey, Func <TItem> loader) where TItem : class { return(GetInternal(cache, cacheKey, localExpiration, remoteExpiration, groupKey, loader, localOnly: false)); }
/// <summary> /// Tries to read a value from local cache. If it is not found there produces value by calling a loader /// function and adds the value to local cache for a given expiration time. By using a generation /// (item version) key, all items on local cache that are members of this group can be expired /// at once. </summary> /// <remarks> /// The difference between this and Get method is that this one only caches items in local cache, but /// uses distributed cache for versioning. To not check group generation every time an item is requested, /// generation number itself is also cached in local cache. Thus, when a generation number changes, local /// cached items might expire after about 5 seconds. This means that, if you use this strategy in a web farm /// setup, when a change occurs in one server, other servers might continue to use old local cached data for /// 5 seconds more. If this is a problem for your configuration, use DistributedCache directly. /// </remarks> /// <typeparam name="TItem">Data type</typeparam> /// <param name="cache">Two level cache</param> /// <param name="cacheKey">The item key for local and distributed cache</param> /// <param name="localExpiration">Local expiration</param> /// <param name="groupKey">Group key that will hold generation (version). Can be used to expire all items /// that depend on it. This can be a table name. When a table changes, you change its version, and all /// cached data that depends on that table is expired.</param> /// <param name="loader">The delegate that will be called to generate value, if not found in local cache, /// or distributed cache, or all found items are expired.</param> public static TItem GetLocalStoreOnly <TItem>(this ITwoLevelCache cache, string cacheKey, TimeSpan localExpiration, string groupKey, Func <TItem> loader) where TItem : class { return(GetInternal(cache, cacheKey, localExpiration, TimeSpan.FromSeconds(0), groupKey, loader, localOnly: true)); }
public ActionResult Index( [FromServices] ITwoLevelCache cache, [FromServices] ISqlConnections sqlConnections ) { if (cache is null) { throw new ArgumentNullException(nameof(cache)); } if (sqlConnections is null) { throw new ArgumentNullException(nameof(sqlConnections)); } var cachedModel = cache.GetLocalStoreOnly("DashboardPageModel", TimeSpan.FromMinutes(5), OrderRow.Fields.GenerationKey, () => { var model = new DashboardPageModel(); var o = OrderRow.Fields; using (var connection = sqlConnections.NewFor <OrderRow>()) { model.OpenOrders = connection.Count <OrderRow>(o.ShippingState == (int)OrderShippingState.NotShipped); var closedOrders = connection.Count <OrderRow>(o.ShippingState == (int)OrderShippingState.Shipped); var totalOrders = model.OpenOrders + closedOrders; model.ClosedOrderPercent = (int)Math.Round(totalOrders == 0 ? 100 : ((double)closedOrders / (double)totalOrders * 100)); model.CustomerCount = connection.Count <CustomerRow>(); model.ProductCount = connection.Count <ProductRow>(); } return(model); }); return(View(MVC.Views.Common.Dashboard.DashboardIndex, cachedModel)); }
/// <summary> /// Creates or overrides a specified entry in the local cache. /// </summary> /// <typeparam name="TItem">Data type</typeparam> /// <param name="cache">Two level cache</param> /// <param name="cacheKey">The item key for local and distributed cache</param> /// <param name="localExpiration">Local expiration</param> /// <param name="groupKey">Group key that will hold generation (version). Can be used to expire all items /// that depend on it. This can be a table name. When a table changes, you change its version, and all /// cached data that depends on that table is expired.</param> /// <param name="value">Value to set.</param> public static TItem SetLocalStoreOnly <TItem>(this ITwoLevelCache cache, string cacheKey, TimeSpan localExpiration, string groupKey, TItem value) where TItem : class { return(GetInternal(cache, cacheKey, localExpiration, TimeSpan.FromSeconds(0), groupKey, () => value, localOnly: true, forceReload: true)); }
/// <summary> /// Creates or overrides a specified entry in the local and distributed cache. /// </summary> /// <typeparam name="TItem">Data type</typeparam> /// <param name="cache">Two level cache</param> /// <param name="cacheKey">The item key for local and distributed cache</param> /// <param name="localExpiration">Local expiration</param> /// <param name="remoteExpiration">Distributed cache expiration (is usually same with local expiration)</param> /// <param name="groupKey">Group key that will hold generation (version). Can be used to expire all items /// that depend on it. This can be a table name. When a table changes, you change its version, and all /// cached data that depends on that table is expired.</param> /// <param name="value">Value to set.</param> public static TItem Set <TItem>(this ITwoLevelCache cache, string cacheKey, TimeSpan localExpiration, TimeSpan remoteExpiration, string groupKey, TItem value) where TItem : class { return(GetInternal(cache, cacheKey, localExpiration, remoteExpiration, groupKey, () => value, localOnly: false, forceReload: true)); }
private static void ProcessTwoLevelCachedAttribute(ITwoLevelCache cache, IUnitOfWork uow, Type type) { if (type == null) { return; } var attr = type.GetCustomAttribute <TwoLevelCachedAttribute>(true); if (attr == null) { return; } if (attr.GenerationKeys != null) { foreach (var key in attr.GenerationKeys) { InvalidateOnCommit(cache, uow, key); } } if (attr.LinkedRows != null) { foreach (var rowType in attr.LinkedRows) { var rowInstance = (IRow)Activator.CreateInstance(rowType); InvalidateOnCommit(cache, uow, rowInstance.GetFields().GenerationKey); } } }
public PermissionService(ITwoLevelCache cache, ISqlConnections sqlConnections, ITypeSource typeSource, IUserAccessor userAccessor) { Cache = cache ?? throw new ArgumentNullException(nameof(cache)); SqlConnections = sqlConnections ?? throw new ArgumentNullException(nameof(sqlConnections)); TypeSource = typeSource ?? throw new ArgumentNullException(nameof(typeSource)); UserAccessor = userAccessor ?? throw new ArgumentNullException(nameof(userAccessor)); }
/// <summary> /// Removes a key from local, distributed caches, and removes their generation version information. /// </summary> /// <param name="cache">Two level cache</param> /// <param name="cacheKey">Cache key</param> public static void Remove(this ITwoLevelCache cache, string cacheKey) { string itemGenerationKey = cacheKey + TwoLevelCache.GenerationSuffix; cache.Memory.Remove(cacheKey); cache.Memory.Remove(itemGenerationKey); cache.Distributed.Remove(cacheKey); cache.Distributed.Remove(itemGenerationKey); }
public UserPasswordValidator(ITwoLevelCache cache, ISqlConnections sqlConnections, IUserRetrieveService userRetriever, ILogger <UserPasswordValidator> log = null, IDirectoryService directoryService = null) { Cache = cache ?? throw new ArgumentNullException(nameof(cache)); SqlConnections = sqlConnections ?? throw new ArgumentNullException(nameof(sqlConnections)); UserRetriever = userRetriever ?? throw new ArgumentNullException(nameof(userRetriever)); DirectoryService = directoryService; Log = log; }
public DefaultRequestContext(IBehaviorProvider behaviors, ITwoLevelCache cache, ITextLocalizer localizer, IPermissionService permissions, IUserAccessor userAccessor) { Behaviors = behaviors ?? throw new ArgumentNullException(nameof(behaviors)); Cache = cache ?? throw new ArgumentNullException(nameof(cache)); Localizer = localizer ?? throw new ArgumentNullException(nameof(localizer)); Permissions = permissions ?? throw new ArgumentNullException(nameof(permissions)); this.userAccessor = userAccessor ?? throw new ArgumentNullException(nameof(userAccessor)); }
public UserDataScript(ITwoLevelCache cache, IPermissionService permissions, ITypeSource typeSource, IUserAccessor userAccessor, IUserRetrieveService userRetrieveService) { Cache = cache ?? throw new ArgumentNullException(nameof(cache)); Permissions = permissions ?? throw new ArgumentNullException(nameof(permissions)); TypeSource = typeSource ?? throw new ArgumentNullException(nameof(typeSource)); UserAccessor = userAccessor ?? throw new ArgumentNullException(nameof(userAccessor)); UserRetriever = userRetrieveService ?? throw new ArgumentNullException(nameof(userRetrieveService)); }
public DynamicScriptManager(ITwoLevelCache cache, IPermissionService permissions, ITextLocalizer localizer) { this.cache = cache ?? throw new ArgumentNullException(nameof(cache)); this.permissions = permissions ?? throw new ArgumentNullException(nameof(permissions)); this.localizer = localizer; registeredScripts = new ConcurrentDictionary <string, IDynamicScript>(StringComparer.OrdinalIgnoreCase); scriptLastChange = new ConcurrentDictionary <string, DateTime>(); Register(new RegisteredScripts(this)); }
public static void InvalidateOnCommit(this ITwoLevelCache cache, IUnitOfWork uow, IRow row) { if (row is null) { throw new ArgumentNullException(nameof(row)); } InvalidateOnCommit(cache, uow, row.GetFields().GenerationKey); ProcessTwoLevelCachedAttribute(cache, uow, row.GetType()); }
public static void RemoveCachedUser(ITwoLevelCache cache, int?userId, string username) { if (userId != null) { cache.Remove("UserByID_" + userId); } if (username != null) { cache.Remove("UserByName_" + username.ToLowerInvariant()); } }
public NullRequestContext(IBehaviorProvider behaviors = null, ITwoLevelCache cache = null, ITextLocalizer localizer = null, IPermissionService permissions = null, IUserAccessor userAccessor = null) { Behaviors = behaviors ?? new NullBehaviorProvider(); Cache = cache ?? new NullTwoLevelCache(); Localizer = localizer ?? NullTextLocalizer.Instance; Permissions = permissions ?? new NullPermissions(); UserAccessor = userAccessor ?? new NullUserAccessor(); }
public static void InvalidateOnCommit(this ITwoLevelCache cache, IUnitOfWork uow, string groupKey) { if (groupKey.IsNullOrEmpty()) { throw new ArgumentNullException("generationKey"); } var updater = cache.Memory.Get("BatchGenerationUpdater:UpdaterInstance", TimeSpan.Zero, () => new GenerationUpdater(cache)); updater.Add(groupKey); uow.OnCommit -= updater.Update; uow.OnCommit += updater.Update; }
private UserDefinition GetUserByName(ITwoLevelCache cache, string username, ref int loadCount) { var l = loadCount; var result = cache.Get("UserByName_" + username.ToLowerInvariant(), TimeSpan.Zero, TimeSpan.FromDays(1), "UserGenerationKey", () => { l++; return(new UserDefinition { UserId = 1, Username = username }); }); loadCount = l; return(result); }
public static void InvalidateOnCommit(this ITwoLevelCache cache, IUnitOfWork uow, RowFieldsBase fields) { if (fields is null) { throw new ArgumentNullException(nameof(fields)); } InvalidateOnCommit(cache, uow, fields.GenerationKey); var fieldsType = fields.GetType(); if (fieldsType.IsNested && fieldsType.DeclaringType != null) { ProcessTwoLevelCachedAttribute(cache, uow, fieldsType.DeclaringType); } }
public NavigationModelFactory( ITwoLevelCache cache, IHttpContextAccessor httpContextAccessor, ITextLocalizer localizer, IPermissionService permissions, IServiceProvider serviceProvider, ITypeSource typeSource, IUserAccessor userAccessor) { this.httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor)); this.localizer = localizer ?? throw new ArgumentNullException(nameof(localizer)); this.cache = cache ?? throw new ArgumentNullException(nameof(cache)); this.permissions = permissions ?? throw new ArgumentNullException(nameof(permissions)); this.serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); this.typeSource = typeSource ?? throw new ArgumentNullException(nameof(typeSource)); this.userAccessor = userAccessor ?? throw new ArgumentNullException(nameof(userAccessor)); }
public NavigationModel(ITwoLevelCache cache, IPermissionService permissions, ITypeSource typeSource, IServiceProvider services, ClaimsPrincipal user, string requestUrl, PathString pathBase) { if (cache is null) { throw new ArgumentNullException(nameof(cache)); } Items = cache.GetLocalStoreOnly("LeftNavigationModel:NavigationItems:" + (user?.GetIdentifier() ?? "-1"), TimeSpan.Zero, UserPermissionRow.Fields.GenerationKey, () => NavigationHelper.GetNavigationItems(permissions, typeSource, services, x => x != null && x.StartsWith("~/") ? VirtualPathUtility.ToAbsolute(PathBase, x) : x)); RequestUrl = requestUrl; PathBase = pathBase; SetActivePath(); }
public ActionResult Index( [FromServices] ITwoLevelCache cache, [FromServices] ISqlConnections sqlConnections ) { if (cache is null) { throw new ArgumentNullException(nameof(cache)); } if (sqlConnections is null) { throw new ArgumentNullException(nameof(sqlConnections)); } var cachedModel = new DashboardPageModel() { }; return(View(MVC.Views.Common.Dashboard.DashboardIndex, cachedModel)); }
public EmployeeListDecorator(ITwoLevelCache cache, ISqlConnections sqlConnections) { Cache = cache ?? throw new ArgumentNullException(nameof(cache)); SqlConnections = sqlConnections ?? throw new ArgumentNullException(nameof(sqlConnections)); }
private static TItem GetInternal <TItem>(ITwoLevelCache cache, string cacheKey, TimeSpan localExpiration, TimeSpan remoteExpiration, string groupKey, Func <TItem> loader, bool localOnly) where TItem : class { ulong?groupGeneration = null; ulong?groupGenerationCache = null; string itemGenerationKey = cacheKey + TwoLevelCache.GenerationSuffix; var localCache = cache.Memory; var distributedCache = cache.Distributed; // retrieves distributed cache group generation number lazily ulong getGroupGenerationValue() { if (groupGeneration != null) { return(groupGeneration.Value); } var bytes = distributedCache.Get(groupKey); groupGeneration = (bytes == null || bytes.Length != 8) ? (ulong?)null : BitConverter.ToUInt64(bytes); if (groupGeneration == null || groupGeneration == 0) { groupGeneration = RandomGeneration(); distributedCache.Set(groupKey, BitConverter.GetBytes(groupGeneration.Value)); } groupGenerationCache = groupGeneration.Value; // add to local cache, use 5 seconds from there localCache.Add(groupKey, groupGenerationCache, TwoLevelCache.GenerationCacheExpiration); return(groupGeneration.Value); } // retrieves local cache group generation number lazily ulong getGroupGenerationCacheValue() { if (groupGenerationCache != null) { return(groupGenerationCache.Value); } // check cached local value of group key // it expires in 5 seconds and read from server again groupGenerationCache = localCache.Get <object>(groupKey) as ulong?; // if its in local cache, return it if (groupGenerationCache != null) { return(groupGenerationCache.Value); } return(getGroupGenerationValue()); } // first check local cache, if item exists and not expired (group version = item version) return it var cachedObj = localCache.Get <object>(cacheKey); if (cachedObj != null) { // check local cache, if exists, compare version with group one var itemGenerationCache = localCache.Get <object>(itemGenerationKey) as ulong?; if (itemGenerationCache != null && itemGenerationCache == getGroupGenerationCacheValue()) { // local cached item is not expired yet if (cachedObj == DBNull.Value) { return(null); } return((TItem)cachedObj); } // local cached item is expired, remove all information if (itemGenerationCache != null) { localCache.Remove(itemGenerationKey); } localCache.Remove(cacheKey); } if (!localOnly) { // no item in local cache or expired, now check distributed cache var bytes = distributedCache.Get(itemGenerationKey); var itemGeneration = (bytes == null || bytes.Length != 8) ? (ulong?)null : BitConverter.ToUInt64(bytes); // if item has version number in distributed cache and this is equal to group version if (itemGeneration != null && itemGeneration.Value == getGroupGenerationValue()) { // get item from distributed cache cachedObj = distributedCache.GetAutoJson <TItem>(cacheKey); // if item exists in distributed cache if (cachedObj != null) { localCache.Add(cacheKey, cachedObj, localExpiration); localCache.Add(itemGenerationKey, getGroupGenerationValue(), localExpiration); return((TItem)cachedObj); } } } if (loader == null) { return(null); } // couldn't find valid item in local or distributed cache, produce value by calling loader var item = loader(); // add item and its version to cache localCache.Add(cacheKey, (object)item ?? DBNull.Value, localExpiration); localCache.Add(itemGenerationKey, getGroupGenerationValue(), localExpiration); if (!localOnly) { // add item and generation to distributed cache if (remoteExpiration == TimeSpan.Zero) { distributedCache.SetAutoJson(cacheKey, item); distributedCache.Set(itemGenerationKey, BitConverter.GetBytes(getGroupGenerationValue())); } else if (remoteExpiration < TimeSpan.Zero) { distributedCache.Remove(cacheKey); distributedCache.Remove(itemGenerationKey); } else { distributedCache.SetAutoJson(cacheKey, item, remoteExpiration); distributedCache.Set(itemGenerationKey, BitConverter.GetBytes(getGroupGenerationValue()), new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = remoteExpiration }); } } return(item); }
/// <summary>Changes a group generation value, so that all items that depend on it are expired.</summary> /// <param name="cache">Two level cache</param> /// <param name="groupKey">Group key</param> public static void ExpireGroupItems(this ITwoLevelCache cache, string groupKey) { cache.Memory.Remove(groupKey); cache.Distributed.Remove(groupKey); }
public GenerationUpdater(ITwoLevelCache cache) { groupKeys = new HashSet <string>(); this.cache = cache ?? throw new ArgumentNullException(nameof(cache)); }
public AccountController(ITwoLevelCache cache, ITextLocalizer localizer) { Cache = cache ?? throw new ArgumentNullException(nameof(cache)); Localizer = localizer ?? throw new ArgumentNullException(nameof(localizer)); }
public UserRetrieveService(ITwoLevelCache cache, ISqlConnections sqlConnections) { Cache = cache; SqlConnections = sqlConnections; }