private async Task HandleCachedAttribute(
            CachedAttribute attribute, MethodInfo actionMethod,
            ActionExecutingContext context, ActionExecutionDelegate next)
        {
            if (UserAgentSpecifiedNoCache(context.HttpContext.Request))
            {
                await next();

                return;
            }

            var scopeValue        = _cacheParametersResolver.ResolveScopeValue(attribute.ScopeName, context);
            var outputContentType = actionMethod.GetCustomAttribute <ProducesAttribute>()?.ContentTypes.First()
                                    ?? RawActionResult.JsonContentType;
            var methodParameters = new CacheMethodParameters(
                actionMethod.GetUniqueIdentifier(),
                attribute.ScopeName,
                scopeValue,
                context.ActionArguments,
                outputContentType,
                attribute.ExpireAfterTimeSpan ?? _cacheParametersResolver.DefaultExpiration,
                attribute.SlidingExpiration);
            var cacheGetResult = await _cacheAdapter.GetOrUpdate(methodParameters, () => OnCacheMiss(next));

            if (cacheGetResult.Hit)
            {
                context.Result = cacheGetResult.ValueFromCache;
            }
        }
 public CacheQueryHandlerDecorator(IQueryCacheHandler <TQuery, TResult> cacheHandler,
                                   IQueryHandler <TQuery, TResult> decorated)
 {
     this.cacheHandler    = cacheHandler;
     this.decorated       = decorated;
     this.cachedAttribute = decorated.GetType().GetCustomAttribute <CachedAttribute>();
 }
예제 #3
0
        public void TimespanSet(int days, int hours, int minutes)
        {
            var attribute = new CachedAttribute(days, hours, minutes);
            var expected  = new TimeSpan(days, hours, minutes, 0);

            Assert.True(attribute.Duration.HasValue);
            Assert.Equal(expected, attribute.Duration.Value);
        }
예제 #4
0
        public CacheAttributeTest()
        {
            HttpContext = new DefaultHttpContext();
            Context     = new ActionExecutingContext(
                new ActionContext(HttpContext, new RouteData(), new ActionDescriptor()),
                new List <IFilterMetadata>(),
                new Dictionary <string, object>(),
                new Mock <ControllerBase>().Object);

            ResponseCacheServiceMock = new ResponseCacheServiceMock();
            CachedAttribute          = new CachedAttribute(ResponseCacheServiceMock.Object);

            ActionExecutionDelegateMock = new Mock <ActionExecutionDelegate>();
        }
 //Because CachedAttribute Enabled property is not in use and throws we
 //Create MethodCachingPolicyConfig using CachedAttribute with Enabled
 //Value 'null', so it wont effect the configuration merge
 public MethodCachingPolicyConfig(CachedAttribute attr)
 {
     Enabled = null;
     ResponseKindsToCache                = attr.ResponseKindsToCache;
     ResponseKindsToIgnore               = attr.ResponseKindsToIgnore;
     RefreshMode                         = attr.RefreshMode;
     RefreshTime                         = attr.RefreshTime;
     ExpirationTime                      = attr.ExpirationTime;
     FailedRefreshDelay                  = attr.FailedRefreshDelay;
     RequestGroupingBehavior             = attr.RequestGroupingBehavior;
     RefreshBehavior                     = attr.RefreshBehavior;
     RevokedResponseBehavior             = attr.RevokedResponseBehavior;
     ExpirationBehavior                  = attr.ExpirationBehavior;
     CacheResponsesWhenSupressedBehavior = attr.CacheResponsesWhenSupressedBehavior;
     NotIgnoredResponseBehavior          = attr.NotIgnoredResponseBehavior;
 }
예제 #6
0
        public async Task When_FilterCalledWithoutCached_Then_SetObjectCached_Test(string method, string path, string queryString)
        {
            var expectedValue = new { prop1 = "teste" };

            ActionExecutionDelegateMock
            .Setup(d => d.Invoke())
            .ReturnsAsync(
                SetActionExecutedContext(expectedValue)
                );

            HttpContext.Request.Method      = method;
            HttpContext.Request.Path        = path;
            HttpContext.Request.QueryString = new QueryString(queryString);

            //Act
            await CachedAttribute.OnActionExecutionAsync(Context, ActionExecutionDelegateMock.Object);

            ResponseCacheServiceMock.Verify_GetCachedResponseAsStringAsync(Times.Once());
            ResponseCacheServiceMock.Verify_SetCacheResponseAsync(Times.Once());
            var storedValue = await ResponseCacheServiceMock.Object.GetCachedResponseAsStringAsync($"{path}|{BuildValuesFromQueryString(HttpContext)}");

            Assert.Equal(JsonSerializer.Serialize(expectedValue), storedValue);
        }
예제 #7
0
        public void DefaultConstructor()
        {
            var attribute = new CachedAttribute();

            Assert.False(attribute.Duration.HasValue);
        }
예제 #8
0
        public void NegativeDays()
        {
            var attribute = new CachedAttribute(days: -1);

            Assert.False(attribute.Duration.HasValue);
        }
예제 #9
0
        protected override Object InterceptLoad(IInvocation invocation, Attribute annotation, Boolean?isAsyncBegin)
        {
            ServiceDescription serviceDescription;
            IServiceResult     serviceResult;
            MethodInfo         method = invocation.Method;

            Object[] args = invocation.Arguments;

            CachedAttribute cached = annotation is CachedAttribute ? (CachedAttribute)annotation : null;

            if (cached == null && pauseCache.Value)
            {
                return(base.InterceptLoad(invocation, annotation, isAsyncBegin));
            }
            Type returnType = method.ReturnType;

            if (ImmutableTypeSet.IsImmutableType(returnType))
            {
                // No possible result which might been read by cache
                return(base.InterceptLoad(invocation, annotation, isAsyncBegin));
            }
            if (cached == null)
            {
                ISecurityScope[] securityScopes = SecurityScopeProvider.SecurityScopes;
                serviceDescription = SyncToAsyncUtil.CreateServiceDescription(ServiceName, method, args, securityScopes);
                serviceResult      = CacheService.GetORIsForServiceRequest(serviceDescription);
                return(CreateResultObject(serviceResult, returnType, args, annotation));
            }

            if (args.Length != 1)
            {
                throw new Exception("This annotation is only allowed on methods with exactly 1 argument. Please check your "
                                    + typeof(CachedAttribute).FullName + " annotation on method " + method.ToString());
            }
            Type entityType = cached.Type;

            if (entityType == null || typeof(void).Equals(entityType))
            {
                entityType = TypeInfoItemUtil.GetElementTypeUsingReflection(returnType, null);
            }
            if (entityType == null || typeof(void).Equals(entityType))
            {
                throw new Exception("Please specify a valid returnType for the " + typeof(CachedAttribute).FullName + " annotation on method "
                                    + method.ToString());
            }
            IEntityMetaData metaData = GetSpecifiedMetaData(method, typeof(CachedAttribute), entityType);
            Member          member   = GetSpecifiedMember(method, typeof(CachedAttribute), metaData, cached.AlternateIdName);

            sbyte idIndex;

            try
            {
                idIndex = metaData.GetIdIndexByMemberName(member.Name);
            }
            catch (Exception e)
            {
                throw new Exception(
                          "Member "
                          + entityType.FullName
                          + "."
                          + cached.AlternateIdName
                          + " is not configured as an alternate ID member. There must be a single-column unique contraint on the respective table column. Please check your "
                          + typeof(CachedAttribute).FullName + " annotation on method " + method.ToString(), e);
            }
            bool           returnMisses = cached.ReturnMisses;
            List <IObjRef> orisToGet    = new List <IObjRef>();

            FillOrisToGet(orisToGet, args, entityType, idIndex, returnMisses);
            return(CreateResultObject(orisToGet, returnType, returnMisses, annotation));
        }
예제 #10
0
        private MethodBuilder GenerateMethod(GeneratorContext ctx, MethodInfo method, PropertyInfo property = null)
        {
            MethodInfo   methodImpl   = GetImplementation(ctx.ServiceType, ctx.ImplementationType, method);
            PropertyInfo propertyImpl = property != null?GetImplementation(ctx.ServiceType, ctx.ImplementationType, property) : null;

            MethodBuilder methodBuilder = ctx.TypeBuilder.DefineMethod(method.Name,
                                                                       method.Attributes & ~MethodAttributes.Abstract,
                                                                       method.ReturnType,
                                                                       method.GetParameters().Select(o => o.ParameterType).ToArray());

            ctx.GenericArguments      = method.GetGenericArguments();
            ctx.GenericParameterNames = new string[0];
            if (method.IsGenericMethod)
            {
                ctx.GenericParameterNames = ctx.GenericArguments.Select(o => o.Name).ToArray();
                GenericTypeParameterBuilder[] genPar0 = methodBuilder.DefineGenericParameters(ctx.GenericParameterNames);
                for (int i = 0; i < ctx.GenericParameterNames.Length; i++)
                {
                    genPar0[i].SetGenericParameterAttributes(ctx.GenericArguments[i]
#if NETSTANDARD1_6
                                                             .GetTypeInfo()
#endif
                                                             .GenericParameterAttributes);
                    genPar0[i].SetInterfaceConstraints(ctx.GenericArguments[i]
#if NETSTANDARD1_6
                                                       .GetTypeInfo()
#endif
                                                       .GetGenericParameterConstraints());
                }
            }

            // cache handler discovery
            CachedAttribute cacheParam = property != null
                                ? FindAttribute <CachedAttribute>(ctx.ServiceType, ctx.ImplementationType, property)
                                : FindAttribute <CachedAttribute>(ctx.ServiceType, ctx.ImplementationType, method);

            ctx.Handler = cacheParam?.Manager;

            bool isGetter = true;
            //bool isAsync = false;
            if (property != null)
            {
                ctx.Handler = null;
            }
            // methods with no result
            if (method.ReturnType == typeof(void))
            {
                //isGetter = false;
                //if (ctx.Property == null)
                ctx.Handler = null;
            }
            // cannot cache the enumerables
            if (method.ReturnType
#if NETSTANDARD1_6
                .GetTypeInfo()
#endif
                .IsGenericType&&
                (method.ReturnType.GetGenericTypeDefinition()
#if NETSTANDARD1_6
                 .GetTypeInfo()
#endif
                 .IsEquivalentTo(typeof(IEnumerable <>))
#if NETSTANDARD2_1
                 || method.ReturnType.GetGenericTypeDefinition()
                 .IsEquivalentTo(typeof(IAsyncEnumerable <>))
#endif
                ))
            {
                ctx.Handler = null;
            }
            // TODO generic methods are not supported yet
            if (method.IsGenericMethod)
            {
                ctx.Handler = null;
            }
            // TODO async methods are not supported yet
            if (method.ReturnType
#if NETSTANDARD1_6
                .GetTypeInfo()
#endif
                .IsEquivalentTo(typeof(Task)) ||
                (method.ReturnType
#if NETSTANDARD1_6
                 .GetTypeInfo()
#endif
                 .BaseType
#if NETSTANDARD1_6
                 ?.GetTypeInfo()
#endif
                 ?.IsEquivalentTo(typeof(Task)) ?? false))
            {
                //isAsync = true;
                ctx.Handler = null;
            }
            // reference and output parameters are not supported (yet?)
            if (method.GetParameters().Any(o => o.ParameterType.IsByRef))
            {
                //isGetter = true;
                ctx.Handler = null;
            }

            // direct proxy test
            //ctx.Handler = null;

            #region Without cache - direct call of non-cached variant

            ILGenerator il = methodBuilder.GetILGenerator();

            if (ctx.Handler == null)
            {
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Call, NonCachedMethod(ctx.ServiceBase));
                int parCount = method.GetParameters().Length;
                if (property != null && !isGetter)
                {
                    parCount++;
                }
                for (int i = 1; i <= parCount; i++)
                {
                    il.Emit_Ldarg(i);
                }
                il.Emit(OpCodes.Callvirt, methodImpl);
                if (property != null && !isGetter)
                {
                    il.Emit(OpCodes.Nop);
                }
                il.Emit(OpCodes.Ret);

                return(methodBuilder);
            }

            if (property != null)
            {
                return(isGetter
                                        ? GeneratePropertyGetMethod(ctx, method, property)
                                        : GeneratePropertySetMethod(ctx, method, property));
            }

            #endregion
            ctx.NestedFields.Clear();
            ctx.NestedKeyFields.Clear();
            GenerateNestedType(ref ctx, method, methodImpl);

            FieldBuilder handler  = GetCacheField(ctx, ctx.Handler);
            LocalBuilder local    = il.DeclareLocal(ctx.NestedType);
            LocalBuilder param    = il.DeclareLocal(typeof(CacheParams));
            FieldInfo    keyField = typeof(CacheParams)
#if NETSTANDARD1_6
                                    .GetTypeInfo()
#endif
                                    .GetField(nameof(CacheParams.Key));
            FieldInfo tagsField = typeof(CacheParams)
#if NETSTANDARD1_6
                                  .GetTypeInfo()
#endif
                                  .GetField(nameof(CacheParams.Tags));
            FieldInfo durField = typeof(CacheParams)
#if NETSTANDARD1_6
                                 .GetTypeInfo()
#endif
                                 .GetField(nameof(CacheParams.Duration));

            il.Emit(OpCodes.Newobj, ctx.NestedType
#if NETSTANDARD1_6
                    .GetTypeInfo()
#endif
                    .GetConstructor(Type.EmptyTypes));
            il.Emit(OpCodes.Stloc_0);
            {
                int idx0 = 1;
                foreach (FieldBuilder field in ctx.NestedFields)
                {
                    il.Emit(OpCodes.Ldloc_0);
                    il.Emit_Ldarg(idx0);
                    il.Emit(OpCodes.Stfld, field);
                    idx0++;
                }
            }

            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldfld, handler);
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Newobj, typeof(CacheParams)
#if NETSTANDARD1_6
                    .GetTypeInfo()
#endif
                    .GetConstructor(Type.EmptyTypes));
            il.Emit(OpCodes.Stloc_1);
            il.Emit(OpCodes.Ldloc_1);

            #region Generate cache key
            {
                int addBytes = 2;
                if (method.IsGenericMethod)
                {
                    addBytes += ctx.GenericArguments.Length;
                }

                il.Emit_Ldc_I4(ctx.NestedKeyFields.Count + addBytes);
                il.Emit(OpCodes.Newarr, typeof(object));

                string svcKey = ctx.ServiceType
#if NETSTANDARD1_6
                                .GetTypeInfo()
#endif
                                .GetCustomAttribute <CacheKeyAttribute>()?.CacheKey
                                ?? ctx.ImplementationType
#if NETSTANDARD1_6
                                .GetTypeInfo()
#endif
                                .GetCustomAttribute <CacheKeyAttribute>()?.CacheKey
                                ?? ctx.ImplementationType.ToString();

                il.Emit(OpCodes.Dup);
                il.Emit(OpCodes.Ldc_I4_0);
                il.Emit(OpCodes.Ldstr, svcKey);
                il.Emit(OpCodes.Stelem_Ref);

                // add generic type arguments from method
                int idx0 = 1;
                if (method.IsGenericMethod)
                {
                    foreach (Type gen in ctx.GenericArguments)
                    {
                        il.Emit(OpCodes.Dup);
                        il.Emit_Ldc_I4(idx0);
                        il.Emit(OpCodes.Ldtoken, gen);
                        il.Emit(OpCodes.Call, typeof(Type)
#if NETSTANDARD1_6
                                .GetTypeInfo()
#endif
                                .GetMethod(nameof(Type.GetTypeFromHandle), new[] { typeof(RuntimeTypeHandle) }));
                        il.Emit(OpCodes.Stelem_Ref);
                        idx0++;
                    }
                }

                string methodKey =
                    (methodImpl.GetCustomAttribute <CacheKeyAttribute>()
                     ?? method.GetCustomAttribute <CacheKeyAttribute>())?.CacheKey
                    ?? method.ToString();

                il.Emit(OpCodes.Dup);
                il.Emit_Ldc_I4(idx0);
                il.Emit(OpCodes.Ldstr, methodKey);
                il.Emit(OpCodes.Stelem_Ref);
                idx0++;

                foreach (FieldBuilder field in ctx.NestedKeyFields)
                {
                    il.Emit(OpCodes.Dup);
                    il.Emit_Ldc_I4(idx0);
                    il.Emit(OpCodes.Ldloc_0);
                    il.Emit(OpCodes.Ldfld, field);
                    if (field.FieldType
#if NETSTANDARD1_6
                        .GetTypeInfo()
#endif
                        .IsValueType)
                    {
                        il.Emit(OpCodes.Box, field.FieldType);
                    }
                    il.Emit(OpCodes.Stelem_Ref);
                    idx0++;
                }
                il.Emit(OpCodes.Call, GenerateKeyMethod);
                il.Emit(OpCodes.Stfld, keyField);
                il.Emit(OpCodes.Ldloc_1);
            }
            #endregion

            #region Generate tags array
            {
                object[] tags =
                    (property != null
                                                ? propertyImpl.GetCustomAttributes <CacheTagsAttribute>(true).Union(
                         property.GetCustomAttributes <CacheTagsAttribute>(true))
                                                : methodImpl.GetCustomAttributes <CacheTagsAttribute>(true).Union(
                         method.GetCustomAttributes <CacheTagsAttribute>(true))
                    ).Union(
                        ctx.ImplementationType
#if NETSTANDARD1_6
                        .GetTypeInfo()
#endif
                        .GetCustomAttributes <CacheTagsAttribute>(true)).Union(
                        ctx.ServiceType
#if NETSTANDARD1_6
                        .GetTypeInfo()
#endif
                        .GetCustomAttributes <CacheTagsAttribute>(true)).Union(
                        ctx.ImplementationType
#if NETSTANDARD1_6
                        .GetTypeInfo()
#endif
                        .Module.GetCustomAttributes <CacheTagsAttribute>()).Union(
                        ctx.ServiceType
#if NETSTANDARD1_6
                        .GetTypeInfo()
#endif
                        .Module.GetCustomAttributes <CacheTagsAttribute>()).Union(
                        ctx.ImplementationType
#if NETSTANDARD1_6
                        .GetTypeInfo()
#endif
                        .Assembly.GetCustomAttributes <CacheTagsAttribute>()).Union(
                        ctx.ServiceType
#if NETSTANDARD1_6
                        .GetTypeInfo()
#endif
                        .Assembly.GetCustomAttributes <CacheTagsAttribute>())
                    .SelectMany(o => o.Tags)
                    .Distinct()
                    .ToArray();

                // pass tags to delegate
                il.Emit_Ldc_I4(tags.Length);
                il.Emit(OpCodes.Newarr, typeof(string));
                int idx0 = 0;
                foreach (object tag in tags)
                {
                    il.Emit(OpCodes.Dup);
                    il.Emit_Ldc_I4(idx0);
                    il.Emit(OpCodes.Ldstr, CachedServiceBase.GetComponentKey(tag, -1));
                    il.Emit(OpCodes.Stelem_Ref);
                    idx0++;
                }
                il.Emit(OpCodes.Stfld, tagsField);
                il.Emit(OpCodes.Ldloc_1);
            }
            #endregion

            il.Emit_Ldc_I4(cacheParam?.Durability ?? DefaultDurability);
            il.Emit(OpCodes.Stfld, durField);
            il.Emit(OpCodes.Ldloc_1);

            #region Call delegate
            {
                il.Emit(OpCodes.Ldloc_0);
                il.Emit(OpCodes.Ldftn, ctx.NestedMethod);
                ConstructorInfo lambdaCtor;
                //if (isGetter)
                //{
                lambdaCtor = typeof(Func <,>)
                             .MakeGenericType(ctx.ServiceBase, method.ReturnType)
#if NETSTANDARD1_6
                             .GetTypeInfo()
#endif
                             .GetConstructor(new[] { typeof(object), typeof(IntPtr) });
                //}
                //else
                //{
                //	Type[] genParamTypes = new[] { ctx.ServiceBase }.Union(ctx.NestedFields.Select(o => o.FieldType)).ToArray();
                //	Type lambda = typeof(Action).Assembly.GetTypes()
                //		.Where(o => o.Name == nameof(Action) &&
                //			o.GetGenericArguments().Length == genParamTypes.Length)
                //		.Single();
                //	lambda = lambda.MakeGenericType(genParamTypes);
                //	lambdaCtor = lambda
                //		.GetConstructor(new[] { typeof(object), typeof(IntPtr) });
                //}
                il.Emit(OpCodes.Newobj, lambdaCtor);

                string methodName =
                    //isAsync
                    //? isGetter ? nameof(ICacheManager.GetAsync) : nameof(ICacheManager.SetAsync)
                    //: isGetter ? nameof(ICacheManager.Get) : nameof(ICacheManager.Set);
                    isGetter ? nameof(ICacheManager.Get) : nameof(ICacheManager.Set);

                int parNum = isGetter ? 3 : 4;

                MethodInfo getMethod = typeof(ICacheManager)
#if NETSTANDARD1_6
                                       .GetTypeInfo()
#endif
                                       .GetMethods()
                                       .Where(o => o.IsGenericMethod &&
                                              o.GetGenericArguments().Length == 2 &&
                                              o.GetParameters().Length == parNum &&
                                              o.GetParameters()[1].ParameterType == typeof(CacheParams))
                                       .Single(o => o.Name == methodName);

                il.Emit(OpCodes.Callvirt, getMethod.MakeGenericMethod(new[] { ctx.ServiceBase, method.ReturnType }));
            }
            #endregion

            il.Emit(OpCodes.Ret);
            return(methodBuilder);
        }
예제 #11
0
 public void SetUp()
 {
     cachedAttribute = new CachedAttribute(1);
 }