/// <summary> /// Inserts instance of an entity in the cache. Any exisiting entity will be overwritten in the cache. /// Entity should be a part of the database context or else the method will throw an exception. /// </summary> /// <param name="entity">Instance of the entity to be inserted.</param> /// <param name="cacheKey">cache key that was used to insert the entity.</param> /// <param name="options">Caching options to be used while storing the entity. Note that some of the options /// might be overridden such as StoreAs option will always be <see cref="StoreAs.SeperateEntities"/>.</param> public void Insert(object entity, out string cacheKey, CachingOptions options) { Logger.Log( "Inserting entity '" + entity + "' with options " + options.ToLog() + "", Microsoft.Extensions.Logging.LogLevel.Trace ); if (IsValidEntity(entity)) { NCacheWrapper nCacheWrapper = QueryCacheManager.Cache; // Generate key using the key generator from NCacheWrapper cacheKey = nCacheWrapper.DefaultKeyGen.GetKey(CurrentContext, entity); // // Items are stored as separate entities in this API because only separate APIs can make it // to this section of code. List or other similar data structures will fail on IsValidEntity // so only individual entities will make it to here. // nCacheWrapper.Set(cacheKey, entity, options, null, StoreAs.SeperateEntities); return; } else { throw new Exception("Entity type and context do not match"); } }
internal static bool IsSeperateStorageEligible <T>(IQueryable <T> query, CachingOptions options) { Logger.Log( "IsSeparateStorageEligible with " + query.ToString() + " and " + options.ToLog() + "?", Microsoft.Extensions.Logging.LogLevel.Trace ); IEntityType entityType = query.GetDbContext().Model.FindEntityType(query.ElementType.FullName); // Full Projection if (entityType != null) { // If User specified key based caching if (options.StoreAs == StoreAs.SeperateEntities) { if (entityType.FindPrimaryKey() == null) { return(false); } else { return(true); } } else { return(false); } } else { return(false); } }
internal static bool CanDirectPkFetch <T>(IQueryable <T> query, CachingOptions options, out string directKey) { Logger.Log( "CanDirectPkFetch with " + query.ToString() + " and " + options.ToLog(), Microsoft.Extensions.Logging.LogLevel.Trace ); directKey = null; IEntityType entityType = query.GetDbContext().Model.FindEntityType(query.ElementType.FullName); // Full Projection if (entityType != null) { IKey key = entityType.FindPrimaryKey(); List <IProperty> primaryKeys = key.Properties.ToList(); Dictionary <IProperty, object> pks = new Dictionary <IProperty, object>(); foreach (IProperty pk in primaryKeys) { pks.Add(pk, null); } List <Expression> queryCriteria = ExtensionMethods.GetQueryCriteriaExp(query.Expression); if (queryCriteria.Count == 0) { return(false); } // Verify Entity has PK and query criteria has PK with Equality only foreach (Expression exp in queryCriteria) { if (!IsExpressionPkEquality(exp, pks)) { return(false); } } // Create Instance then get key object instance = Activator.CreateInstance(query.ElementType); foreach (var pk in pks) { instance.GetType().GetProperty(pk.Key.Name).SetValue(instance, pk.Value); } // Finally get key directKey = QueryCacheManager.Cache.DefaultKeyGen.GetKey(query.GetDbContext(), instance); return(true); } else { return(false); } }
/// <summary>Static constructor.</summary> static QueryCacheManager() { #if EF5 || EF6 Cache = MemoryCache.Default; DefaultCacheItemPolicy = new CacheItemPolicy(); #elif EFCORE DefaultNCacheEntryOptions = new CachingOptions(); #endif CachePrefix = "NCache;"; CacheTags = new ConcurrentDictionary <string, List <string> >(); IncludeConnectionInCacheKey = false; IncludeUserNameAndDatabase = true; }
public NCacheEnumerator(string key, IQueryable <T> query, IEnumerable <T> enumerable, CachingOptions options, CacheDependency dbDependency) { _queryKey = key; _query = query; _innerEnumerable = enumerable; _options = options; _dbDependency = dbDependency; _currentContext = query.GetDbContext(); _innerEnumerator = _innerEnumerable.GetEnumerator(); _dataEnumerated = new Dictionary <string, T>(); if (_options.StoreAs == StoreAs.SeperateEntities) { _isSeperateStorageEligible = QueryHelper.IsSeperateStorageEligible(query, options); } }
internal static TItem SetAsCacheEntry <TItem>(this NCacheWrapper cache, object key, TItem value, CachingOptions options, CacheDependency dbDependency) { Logger.Log( "Setting CacheEntry '" + value + "' against key '" + key + "' with DbDependency '" + dbDependency + "'", Microsoft.Extensions.Logging.LogLevel.Trace ); CacheEntry entry = cache.CreateEntry(key); if (options != null) { entry.SetOptions(options); } if (dbDependency != null) { entry.Dependencies = dbDependency; } entry.Value = value; cache.Set(key, entry, options, dbDependency, options.StoreAs); return(value); }
internal static TItem SetAsCacheEntry <TItem>(this NCacheWrapper cache, object key, TItem value, CachingOptions options) { return(SetAsCacheEntry(cache, key, value, options, null)); }
// Main implementation private static IEnumerable<T> FromCacheImplementation<T>(CachingMethod cachingMethod, IQueryable<T> query, out string cacheKey, CachingOptions options) where T : class { Logger.Log( "Performing " + cachingMethod + " for " + query.ToString() + " with options " + options.ToLog() + ".", LogLevel.Trace ); // Create NCache entry options CachingOptions optionsCloned = (CachingOptions)options.Clone(); cacheKey = null; string queryStoreKey = null; if (cachingMethod != CachingMethod.LoadIntoCache) { // Verify if query can be fetched seperately string pkCacheKey; if (QueryHelper.CanDirectPkFetch(query, optionsCloned, out pkCacheKey)) { object pkItem; if (QueryCacheManager.Cache.TryGetValue(pkCacheKey, out pkItem)) { List<T> resultSetPk = new List<T>(); List<T> resultSetPkTracked = new List<T>(); var stateManagerPk = query.GetStateManager(); resultSetPk.Add((T)pkItem); foreach (var entity in resultSetPk) { resultSetPkTracked.Add(((StateManager)stateManagerPk).GetRefValue(entity)); } return resultSetPkTracked; } } } bool cacheHit = false; IDictionary cacheResult = null; queryStoreKey = QueryCacheManager.GetQueryCacheKey(query, optionsCloned.QueryIdentifier); if (optionsCloned.StoreAs == StoreAs.Collection || optionsCloned.QueryIdentifier == null) { if (optionsCloned.StoreAs == StoreAs.Collection) cacheKey = queryStoreKey; if (optionsCloned.QueryIdentifier == null) optionsCloned.QueryIdentifier = queryStoreKey; } // Check in cache if (cachingMethod != CachingMethod.LoadIntoCache) { cacheHit = QueryCacheManager.Cache.GetByKey(queryStoreKey, out cacheResult); } // If not found in cache go for db if (!cacheHit) { var enumerableSet = query.AsEnumerable<T>(); CacheDependency dbDependency = null; if (optionsCloned.CreateDbDependency) { RelationalQueryContext queryContext = null; IRelationalCommand command = query.CreateCommand(out queryContext); string connectionString = queryContext.Connection.ConnectionString; dbDependency = GetDependency(NCacheConfiguration.DatabaseType, command.CommandText, connectionString); } return new NCacheEnumerable<T>(queryStoreKey, query, enumerableSet, optionsCloned, dbDependency); } // data is found in cache return result set else { // Assume its a collection if (cacheResult.Count == 1) { foreach (var item in cacheResult.Values) { CacheEntry entry = item as CacheEntry; if (entry != null) { // Confirmed stored as collection just return the value after casting IEnumerable<T> resultSetC = (IEnumerable<T>)entry.Value; // [Umer] i know this tracking is costly but there is no other solution var resultSetCTracked = new List<T>(); var stateManagerC = query.GetStateManager(); foreach (var entity in resultSetC) { resultSetCTracked.Add(((StateManager)stateManagerC).GetRefValue(entity)); } return resultSetCTracked; } break; } } var resultSetSE = cacheResult.Values.Cast<T>(); // [Umer] i know this tracking is costly but there is no other solution var resultSetSETracked = new List<T>(); var stateManagerSE = query.GetStateManager(); foreach (var entity in resultSetSE) { resultSetSETracked.Add(((StateManager)stateManagerSE).GetRefValue(entity)); } return resultSetSETracked; } }
/// <summary> /// Checks if the result set is available in cache or not. If it is available it is fetched from the cache and returned /// however if it is not available the query is executed on the database and the result set is stored in cache as well /// as returned. /// </summary> /// <typeparam name="T">The generic type of the collection</typeparam> /// <param name="query">The query to be executed.</param> /// <param name="cacheKey">The key against which the result set will be cached is returned as out parameter.</param> /// <param name="options">The option that will be used to store the result set.</param> /// <returns>Returns the result set of the query from cache if available else from the database and stores it in /// the cache. /// </returns> public static IEnumerable<T> FromCache<T>(this IQueryable<T> query, out string cacheKey, CachingOptions options) where T : class { return FromCacheImplementation(CachingMethod.FromCache, query, out cacheKey, options); }
internal static TItem[] Set <TItem>(this NCacheWrapper cache, object[] keys, TItem[] values, CachingOptions options, Alachisoft.NCache.Runtime.Dependencies.CacheDependency dbDependency, StoreAs storingAs) { Logger.Log( "Setting items in bulk against respective keys with DbDependency '" + dbDependency + "'.", Microsoft.Extensions.Logging.LogLevel.Trace ); Alachisoft.NCache.Web.Caching.CacheItem[] cacheItems = new Alachisoft.NCache.Web.Caching.CacheItem[values.Count()]; for (int i = 0; i < values.Count(); i++) { cacheItems[i] = new Alachisoft.NCache.Web.Caching.CacheItem(values[i]); CachingOptionsUtil.CopyMetadata(ref cacheItems[i], options, dbDependency); } if (keys.Length > 0) { cache.InsertBulk(keys, cacheItems); } return(values); }
/// <summary> /// Executes the query on the database irrespective of the fact that the result set could have been in the cache /// and could have been served from there and updates the result set in the cache. /// </summary> /// <typeparam name="T">The generic type of the collection</typeparam> /// <param name="query">The query to be executed.</param> /// <param name="options">The option that will be used to store the result set.</param> /// <returns>Returns the result set of the query (encapsulated in a task) after executing it on the databse and storing it in the cache. /// </returns> public static async Task <IEnumerable <T> > LoadIntoCacheAsync <T>(this IQueryable <T> query, CachingOptions options) where T : class { Logger.Log( "Async operation requested.", Microsoft.Extensions.Logging.LogLevel.Trace ); Task <IEnumerable <T> > task = Task.Factory.StartNew( () => LoadIntoCache(query, options) ); return(await task); }
public NCacheEnumerable(string key, IQueryable <T> query, IEnumerable <T> enumerable, CachingOptions options, CacheDependency dbDependency) { _innerEnumerable = enumerable; _enumeratorWrapper = new NCacheEnumerator <T>(key, query, _innerEnumerable, options, dbDependency); }
// Main implementation private static T FromCacheImplementation <T>(CachingMethod cachingMethod, QueryDeferred <T> query, out string cacheKey, CachingOptions options) { Logger.Log( "Performing " + cachingMethod + " for " + query.ToString() + " with options " + options.ToLog() + ".", Microsoft.Extensions.Logging.LogLevel.Trace ); options = (CachingOptions)options.Clone(); // Always store as collection options.StoreAs = StoreAs.Collection; bool cacheHit = false; IDictionary cacheResult = default(Hashtable); cacheKey = QueryCacheManager.GetQueryCacheKey(query.Query, options.QueryIdentifier); // If user has specified tag, leave it as it is // Otherwise overwrite it with 'cacheKey' options.QueryIdentifier = options.QueryIdentifier ?? cacheKey; /* NOTE: If user stored result with a tag and is trying to query * it without the tag, it's a different query so don't * worry about that. */ // Get result into 'cacheResult' hashtable if it exists if (cachingMethod == CachingMethod.FromCache) { // Get by the tag (more reliable) cacheHit = QueryCacheManager.Cache.GetByKey(options.QueryIdentifier, out cacheResult); } // If result wasn't found OR result was meant to be stored fresh if (cachingMethod == CachingMethod.LoadIntoCache || !cacheHit) { CacheDependency dbDependency = null; if (options.CreateDbDependency) { IRelationalCommand command = query.Query.CreateCommand(out RelationalQueryContext queryContext); string connectionString = queryContext.Connection.ConnectionString; dbDependency = GetDependency(NCacheConfiguration.DatabaseType, command.CommandText, connectionString); } object item = query.Execute(); QueryCacheManager.Cache.SetAsCacheEntry(cacheKey, item ?? Null.Value, options, dbDependency); return(item == null ? default(T) : (T)item); } // If result was meant to be fetched instead of stored fresh AND it was found (somewhat) else { object returnVal = default(T); if (cacheResult != default(Hashtable)) { returnVal = cacheResult.Values.Cast <CacheEntry>().FirstOrDefault().Value; } return(returnVal != null ? (returnVal is Null ? default(T) : (T)returnVal) : default(T)); } }
/// <summary> /// Checks if the result is available in cache or not. If it is available it is fetched from the cache and returned /// however if it is not available the query is executed on the database and the result is stored in cache as well /// as returned. /// </summary> /// <typeparam name="T">The generic type of the result</typeparam> /// <param name="query">The query to be executed.</param> /// <param name="cacheKey">The key against which the result will be cached is returned as out parameter.</param> /// <param name="options">The option that will be used to store the result.</param> /// <returns>Returns the result of the query from cache if available else from the database and stores it in /// the cache. /// </returns> public static T FromCache <T>(this QueryDeferred <T> query, out string cacheKey, CachingOptions options) { return(FromCacheImplementation(CachingMethod.FromCache, query, out cacheKey, options)); }
/// <summary> /// Checks if the result is available in cache or not. If it is available it is fetched from the cache and returned /// however if it is not available the query is executed on the database and the result is stored in cache as well /// as returned. /// </summary> /// <typeparam name="T">The generic type of the collection</typeparam> /// <param name="query">The query to be executed.</param> /// <param name="options">The option that will be used to store the result set.</param> /// <returns>Returns the result set of the query from cache if available else from the database and stores it in /// the cache. /// </returns> public static T FromCache <T>(this QueryDeferred <T> query, CachingOptions options) { return(query.FromCache(out string cacheKey, options)); }
internal static List <TItem> Set <TItem>(this NCacheWrapper cache, object key, Dictionary <string, TItem> value, CachingOptions options, Alachisoft.NCache.Runtime.Dependencies.CacheDependency dbDependency, StoreAs storingAs) { Logger.Log( "About to set values with options " + options.ToLog() + ", DbDependency '" + dbDependency + "' and StoringAs '" + storingAs + "'.", Microsoft.Extensions.Logging.LogLevel.Trace ); // Add entities if stroing as seperateEntities if (storingAs == StoreAs.SeperateEntities) { Logger.Log("Values are about to be set as separate entities.", Microsoft.Extensions.Logging.LogLevel.Trace); cache.Set(value.Keys.ToArray(), value.Values.ToArray(), options, dbDependency, storingAs); } // from here onwards is the enumerator logic and now it is being done in "else" after we have moved to tags based result set regeneration else { Logger.Log("Values are about to be set as collection.", Microsoft.Extensions.Logging.LogLevel.Trace); // Add query enumerator CacheEntry entry = cache.CreateEntry(key); // Setting options if (options != null) { entry.SetOptions(options); } // Setting Value if (storingAs == StoreAs.Collection) { entry.Value = value.Values.ToList(); } // Mind that this is not the user specified option but the end storing methodology entry.StoredAs = storingAs; // Set dependencies in the entry var aggregateDependency = new AggregateCacheDependency(); if (dbDependency != null) { aggregateDependency.Add(dbDependency); } entry.Dependencies = aggregateDependency; cache.Set(key, entry, options, dbDependency, storingAs); } return(value.Values.ToList()); }
internal static TItem Set <TItem>(this NCacheWrapper cache, object key, TItem value, CachingOptions options, Alachisoft.NCache.Runtime.Dependencies.CacheDependency dbDependency, StoreAs storingAs) { Logger.Log( "Setting item '" + value + "' against key '" + key + "' with DbDependency '" + dbDependency + "'.", Microsoft.Extensions.Logging.LogLevel.Trace ); Alachisoft.NCache.Web.Caching.CacheItem cacheItem = new Alachisoft.NCache.Web.Caching.CacheItem(value); CachingOptionsUtil.CopyMetadata(ref cacheItem, options, dbDependency); cache.Insert(key, cacheItem); return(value); }
/// <summary> /// Executes the query on the database irrespective of the fact that the result could have been in the cache /// and could have been served from there and updates the result in the cache. /// </summary> /// <typeparam name="T">The generic type of the collection</typeparam> /// <param name="query">The query to be executed.</param> /// <param name="options">The option that will be used to store the result.</param> /// <returns>Returns the result of the query (encapsulated in a task) after executing it on the databse and storing it in the cache. /// </returns> public static async Task <T> LoadIntoCacheAsync <T>(this QueryDeferred <T> query, CachingOptions options) { Logger.Log( "Async operation requested.", Microsoft.Extensions.Logging.LogLevel.Trace ); Task <T> task = Task.Factory.StartNew( () => LoadIntoCache(query, options) ); return(await task); }
/// <summary> /// Checks if the result set is available in cache or not. If it is available it is fetched from the cache and returned /// however if it is not available the query is executed on the database and the result set is stored in cache as well /// as returned. /// </summary> /// <typeparam name="T">The generic type of the collection</typeparam> /// <param name="query">The query to be executed.</param> /// <param name="options">The option that will be used to store the result set.</param> /// <returns>Returns the result set of the query from cache if available else from the database and stores it in /// the cache. /// </returns> public static IEnumerable<T> FromCache<T>(this IQueryable<T> query, CachingOptions options) where T : class { string str; return query.FromCache(out str, options); }
/// <summary> /// Executes the query on the database irrespective of the fact that the result set could have been in the cache /// and could have been served from there and updates the result set in the cache. /// </summary> /// <typeparam name="T">The generic type of the collection</typeparam> /// <param name="query">The query to be executed.</param> /// <param name="options">The option that will be used to store the result set.</param> /// <returns>Returns the result set of the query after executing it on the databse and storing it in the cache. /// </returns> public static IEnumerable <T> LoadIntoCache <T>(this IQueryable <T> query, CachingOptions options) where T : class { string str; return(query.LoadIntoCache(out str, options)); }